Merge "Import translations. DO NOT MERGE" into rvc-dev
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 3f254c0..0647d8a 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -307,8 +307,8 @@
      *                                {@link #getRemainingLeaseQuotaBytes()} before trying to
      *                                acquire a lease.
      *
-     * @see {@link #acquireLease(BlobHandle, int)}
-     * @see {@link #acquireLease(BlobHandle, CharSequence)}
+     * @see #acquireLease(BlobHandle, int)
+     * @see #acquireLease(BlobHandle, CharSequence)
      */
     public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId,
             @CurrentTimeMillisLong long leaseExpiryTimeMillis) throws IOException {
@@ -367,8 +367,8 @@
      *                                {@link #getRemainingLeaseQuotaBytes()} before trying to
      *                                acquire a lease.
      *
-     * @see {@link #acquireLease(BlobHandle, int, long)}
-     * @see {@link #acquireLease(BlobHandle, CharSequence)}
+     * @see #acquireLease(BlobHandle, int, long)
+     * @see #acquireLease(BlobHandle, CharSequence)
      */
     public void acquireLease(@NonNull BlobHandle blobHandle, @NonNull CharSequence description,
             @CurrentTimeMillisLong long leaseExpiryTimeMillis) throws IOException {
@@ -420,8 +420,8 @@
      *                                {@link #getRemainingLeaseQuotaBytes()} before trying to
      *                                acquire a lease.
      *
-     * @see {@link #acquireLease(BlobHandle, int, long)}
-     * @see {@link #acquireLease(BlobHandle, CharSequence, long)}
+     * @see #acquireLease(BlobHandle, int, long)
+     * @see #acquireLease(BlobHandle, CharSequence, long)
      */
     public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId)
             throws IOException {
@@ -467,8 +467,8 @@
      *                                {@link #getRemainingLeaseQuotaBytes()} before trying to
      *                                acquire a lease.
      *
-     * @see {@link #acquireLease(BlobHandle, int)}
-     * @see {@link #acquireLease(BlobHandle, CharSequence, long)}
+     * @see #acquireLease(BlobHandle, int)
+     * @see #acquireLease(BlobHandle, CharSequence, long)
      */
     public void acquireLease(@NonNull BlobHandle blobHandle, @NonNull CharSequence description)
             throws IOException {
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index abf78c6..2312635 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -41,7 +41,7 @@
  * system will execute this job on your application's {@link android.app.job.JobService}.
  * You identify the service component that implements the logic for your job when you
  * construct the JobInfo using
- * {@link android.app.job.JobInfo.Builder#JobInfo.Builder(int,android.content.ComponentName)}.
+ * {@link android.app.job.JobInfo.Builder#Builder(int,android.content.ComponentName)}.
  * </p>
  * <p>
  * The framework will be intelligent about when it executes jobs, and attempt to batch
@@ -153,7 +153,7 @@
      * method is ignored.
      *
      * @param jobId unique identifier for the job to be canceled, as supplied to
-     *     {@link JobInfo.Builder#JobInfo.Builder(int, android.content.ComponentName)
+     *     {@link JobInfo.Builder#Builder(int, android.content.ComponentName)
      *     JobInfo.Builder(int, android.content.ComponentName)}.
      */
     public abstract void cancel(int jobId);
diff --git a/apex/sdkextensions/Android.bp b/apex/sdkextensions/Android.bp
index fc26e08..dbb5bd3d 100644
--- a/apex/sdkextensions/Android.bp
+++ b/apex/sdkextensions/Android.bp
@@ -22,6 +22,7 @@
     binaries: [ "derive_sdk" ],
     prebuilts: [ "cur_sdkinfo" ],
     manifest: "manifest.json",
+    min_sdk_version: "current",
 }
 
 apex_defaults {
diff --git a/apex/sdkextensions/derive_sdk/Android.bp b/apex/sdkextensions/derive_sdk/Android.bp
index cf49902..c419b51 100644
--- a/apex/sdkextensions/derive_sdk/Android.bp
+++ b/apex/sdkextensions/derive_sdk/Android.bp
@@ -20,13 +20,13 @@
     ],
     proto: {
         type: "lite",
+        static: true,
     },
     sdk_version: "current",
     stl: "c++_static",
     shared_libs: [ "liblog" ],
     static_libs: [
         "libbase_ndk",
-        "libprotobuf-cpp-lite-ndk",
     ],
 }
 
@@ -45,7 +45,8 @@
     compile_multilib: "prefer32",
     stem: "derive_sdk",
     apex_available: [ "test_com.android.sdkext" ],
-    visibility: [ "//frameworks/base/apex/sdkextensions/testing" ]
+    visibility: [ "//frameworks/base/apex/sdkextensions/testing" ],
+    installable: false,
 }
 
 prebuilt_etc {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 9a73fdd..3017ec0 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -9344,6 +9344,8 @@
     // Return_code 1 indicates success.
     // For full list, see frameworks/base/core/java/android/content/pm/PackageManager.java
     optional int32 return_code  = 4;
+    // Total size of the APKs installed for this package
+    optional int64 apks_size_bytes = 5;
 }
 
 /**
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 17720a3..bfae632 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3743,7 +3743,6 @@
      * To receive this callback, you must return true from onKeyDown for the current
      * event stream.
      *
-     * @see KeyEvent.Callback#onKeyLongPress()
      * @see KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
      */
     public boolean onKeyLongPress(int keyCode, KeyEvent event) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 49cc621..9067069 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2450,8 +2450,7 @@
      * has access to it.
      *
      * @see ActivityOptions#setLaunchDisplayId(int)
-     * @see android.view.Display.FLAG_PRIVATE
-     * @see android.view.Display.TYPE_VIRTUAL
+     * @see android.view.Display#FLAG_PRIVATE
      *
      * @param context Source context, from which an activity will be started.
      * @param displayId Target display id.
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 33bacf0..4d21c8d 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -6858,10 +6858,7 @@
      * @param ops The operations to watch.
      * @param callback Where to report changes.
      *
-     * @see #isOperationActive
      * @see #stopWatchingActive
-     * @see #startOp(int, int, String, boolean, String, String)
-     * @see #finishOp(int, int, String, String)
      */
     // TODO: Uncomment below annotation once b/73559440 is fixed
     // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
@@ -6909,10 +6906,7 @@
      * long running and it has a clear start and stop delimiters. Unregistering a
      * non-registered callback has no effect.
      *
-     * @see #isOperationActive
      * @see #startWatchingActive
-     * @see #startOp(int, int, String, boolean, String, String)
-     * @see #finishOp(int, int, String, String)
      */
     public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
         synchronized (mActiveWatchers) {
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 1a619bd..2d06ee8 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -205,4 +205,15 @@
      * @param {@code true} if the task got focus, {@code false} if it lost it.
      */
     void onTaskFocusChanged(int taskId, boolean focused);
+
+    /**
+     * Called when a task changes its requested orientation. It is different from {@link
+     * #onActivityRequestedOrientationChanged(int, int)} in the sense that this method is called
+     * when a task changes requested orientation due to activity launch, dimiss or reparenting.
+     *
+     * @param taskId id of the task.
+     * @param requestedOrientation the new requested orientation of this task as screen orientations
+     *                             in {@link android.content.pm.ActivityInfo}.
+     */
+     void onTaskRequestedOrientationChanged(int taskId, int requestedOrientation);
 }
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 818a121..e233ade 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -732,7 +732,7 @@
          * a non-null value if the intent needs to be intercepted.
          *
          * <p> Whenever a new activity is started, this method will be called on instances created
-         * using {@link #Instrumentation.ActivityMonitor()} to check if there is a match. In case
+         * using {@link #ActivityMonitor()} to check if there is a match. In case
          * of a match, the activity start will be blocked and the returned result will be used.
          *
          * @param intent The intent used for starting the activity.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f26b136..a033b82 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3513,7 +3513,7 @@
         }
 
         /**
-         * @deprecated use {@link Notification.Builder#Notification.Builder(Context, String)}
+         * @deprecated use {@link #Builder(Context, String)}
          * instead. All posted Notifications must specify a NotificationChannel Id.
          */
         @Deprecated
@@ -3631,7 +3631,7 @@
          * @param shortcutId the {@link ShortcutInfo#getId() id} of the shortcut this notification
          *                   is linked to
          *
-         * @see Notification.BubbleMetadata.Builder#Builder(String)
+         * @see BubbleMetadata.Builder#Builder(String)
          */
         @NonNull
         public Builder setShortcutId(String shortcutId) {
@@ -5993,7 +5993,7 @@
          * metadata matches the shortcutId set on the  notification builder, if one was set.
          * If the shortcutId's were specified but do not match, an exception is thrown here.
          *
-         * @see Notification.BubbleMetadata.Builder#Builder(String)
+         * @see BubbleMetadata.Builder#Builder(String)
          * @see #setShortcutId(String)
          */
         @NonNull
@@ -7297,7 +7297,7 @@
          * Should be unique amongst all individuals in the conversation, and should be
          * consistent during re-posts of the notification.
          *
-         * @see Message#Notification.MessagingStyle.Message(CharSequence, long, CharSequence)
+         * @see Message#Message(CharSequence, long, CharSequence)
          *
          * @return this object for method chaining
          *
@@ -7317,7 +7317,7 @@
          * Should be <code>null</code> for messages by the current user, in which case
          * the platform will insert the user set in {@code MessagingStyle(Person)}.
          *
-         * @see Message#Notification.MessagingStyle.Message(CharSequence, long, CharSequence)
+         * @see Message#Message(CharSequence, long, CharSequence)
          *
          * @return this object for method chaining
          */
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index d6df400..eef9c02 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -368,7 +368,7 @@
      * </p>
      * </p>
      *
-     * @see {@link #addAutomaticZenRule(AutomaticZenRule)}
+     * @see #addAutomaticZenRule(AutomaticZenRule)
      */
     @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_AUTOMATIC_ZEN_RULE =
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 47ccc2f..106f8ac 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -1109,10 +1109,16 @@
             Slog.v(TAG, "Changing resources "
                     + resourcesImpl + " config to: " + config);
         }
-        int displayId = key.mDisplayId;
-        final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
+
         tmpConfig.setTo(config);
 
+        // Apply the override configuration before setting the display adjustments to ensure that
+        // the process config does not override activity display adjustments.
+        final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
+        if (hasOverrideConfiguration) {
+            tmpConfig.updateFrom(key.mOverrideConfiguration);
+        }
+
         // Get new DisplayMetrics based on the DisplayAdjustments given to the ResourcesImpl. Update
         // a copy if the CompatibilityInfo changed, because the ResourcesImpl object will handle the
         // update internally.
@@ -1121,17 +1127,23 @@
             daj = new DisplayAdjustments(daj);
             daj.setCompatibilityInfo(compat);
         }
+
+        final int displayId = key.mDisplayId;
         if (displayId == Display.DEFAULT_DISPLAY) {
-            daj.setConfiguration(config);
+            daj.setConfiguration(tmpConfig);
         }
         DisplayMetrics dm = getDisplayMetrics(displayId, daj);
         if (displayId != Display.DEFAULT_DISPLAY) {
             applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
+
+            // Re-apply the override configuration to ensure that configuration contexts based on
+            // a display context (ex: createDisplayContext().createConfigurationContext()) have the
+            // correct override.
+            if (hasOverrideConfiguration) {
+                tmpConfig.updateFrom(key.mOverrideConfiguration);
+            }
         }
 
-        if (hasOverrideConfiguration) {
-            tmpConfig.updateFrom(key.mOverrideConfiguration);
-        }
         resourcesImpl.updateConfiguration(tmpConfig, dm, compat);
     }
 
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index bfa91aa..5d8daf8 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -195,4 +195,8 @@
     @Override
     public void onTaskFocusChanged(int taskId, boolean focused) {
     }
+
+    @Override
+    public void onTaskRequestedOrientationChanged(int taskId, int requestedOrientation) {
+    }
 }
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index beb4449..3bc043e 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -805,7 +805,7 @@
      * has a work profile that was restored from another work profile with serial number
      * {@code ancestralSerialNumber}.
      *
-     * @see UserManager#getSerialNumberForUser(UserHandle)
+     * @see android.os.UserManager#getSerialNumberForUser(UserHandle)
      */
     @Nullable
     public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 917eeb8..253c73796 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -103,8 +103,6 @@
 
     /**
      * The name of the emergency role
-     *
-     * @see android.telephony.TelephonyManager#ACTION_EMERGENCY_ASSISTANCE
      */
     public static final String ROLE_EMERGENCY = "android.app.role.EMERGENCY";
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 09c6849..8472144 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3639,7 +3639,6 @@
      * @see android.telephony.CarrierConfigManager
      * @see #EUICC_SERVICE
      * @see android.telephony.euicc.EuiccManager
-     * @see #MMS_SERVICE
      * @see android.telephony.MmsManager
      * @see #INPUT_METHOD_SERVICE
      * @see android.view.inputmethod.InputMethodManager
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7d8a4a4..baaf8f7 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6015,6 +6015,8 @@
             FLAG_ACTIVITY_TASK_ON_HOME,
             FLAG_ACTIVITY_RETAIN_IN_RECENTS,
             FLAG_ACTIVITY_LAUNCH_ADJACENT,
+            FLAG_ACTIVITY_REQUIRE_NON_BROWSER,
+            FLAG_ACTIVITY_REQUIRE_DEFAULT,
             FLAG_RECEIVER_REGISTERED_ONLY,
             FLAG_RECEIVER_REPLACE_PENDING,
             FLAG_RECEIVER_FOREGROUND,
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 5795cd2..d06a69c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1561,6 +1561,14 @@
      */
     public static final int INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED = -124;
 
+    /**
+     * Installation failed return code: the package was skipped and should be ignored.
+     *
+     * The reason for the skip is undefined.
+     * @hide
+     */
+    public static final int INSTALL_PARSE_FAILED_SKIPPED = -125;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "DELETE_" }, value = {
             DELETE_KEEP_DATA,
@@ -7940,7 +7948,6 @@
      *
      * @return true if the drawable represents the default activity icon, false otherwise
      * @see #getDefaultActivityIcon()
-     * @see PackageItemInfo#loadDefaultIcon(PackageManager)
      * @see #getActivityIcon
      * @see LauncherActivityInfo#getIcon(int)
      */
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index addac98..3b3521f 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2001,6 +2001,7 @@
                     Slog.i(TAG, "Skipping target and overlay pair " + pkg.mOverlayTarget + " and "
                         + pkg.baseCodePath+ ": overlay ignored due to required system property: "
                         + propName + " with value: " + propValue);
+                    mParseError = PackageManager.INSTALL_PARSE_FAILED_SKIPPED;
                     return null;
                 }
 
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 197ad74..d3d15c8 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -2347,14 +2347,12 @@
             String propValue = sa.getString(
                     R.styleable.AndroidManifestResourceOverlay_requiredSystemPropertyValue);
             if (!PackageParser.checkRequiredSystemProperties(propName, propValue)) {
-                Slog.i(TAG, "Skipping target and overlay pair " + target + " and "
+                String message = "Skipping target and overlay pair " + target + " and "
                         + pkg.getBaseCodePath()
                         + ": overlay ignored due to required system property: "
-                        + propName + " with value: " + propValue);
-                return input.error("Skipping target and overlay pair " + target + " and "
-                        + pkg.getBaseCodePath()
-                        + ": overlay ignored due to required system property: "
-                        + propName + " with value: " + propValue);
+                        + propName + " with value: " + propValue;
+                Slog.i(TAG, message);
+                return input.skip(message);
             }
 
             return input.success(pkg.setOverlay(true)
diff --git a/core/java/android/content/pm/parsing/result/ParseInput.java b/core/java/android/content/pm/parsing/result/ParseInput.java
index 6b659be..d5898b7 100644
--- a/core/java/android/content/pm/parsing/result/ParseInput.java
+++ b/core/java/android/content/pm/parsing/result/ParseInput.java
@@ -88,6 +88,14 @@
      */
     ParseResult<?> enableDeferredError(String packageName, int targetSdkVersion);
 
+    /**
+     * This will assign errorCode to {@link PackageManager#INSTALL_PARSE_FAILED_SKIPPED, used for
+     * packages which should be ignored by the caller.
+     *
+     * @see #error(int, String, Exception)
+     */
+    <ResultType> ParseResult<ResultType> skip(@NonNull String parseError);
+
     /** @see #error(int, String, Exception) */
     <ResultType> ParseResult<ResultType> error(int parseError);
 
diff --git a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
index b26bf71..6115206 100644
--- a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
+++ b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
@@ -147,6 +147,11 @@
     }
 
     @Override
+    public <ResultType> ParseResult<ResultType> skip(@NonNull String parseError) {
+        return error(PackageManager.INSTALL_PARSE_FAILED_SKIPPED, parseError);
+    }
+
+    @Override
     public <ResultType> ParseResult<ResultType> error(int parseError) {
         return error(parseError, null);
     }
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index afa6303..836624b 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -803,7 +803,7 @@
          * @param isMetered {@code true} if the VPN network should be treated as metered regardless
          *     of underlying network meteredness. Defaults to {@code true}.
          * @return this {@link Builder} object to facilitate chaining of method calls
-         * @see NetworkCapabilities.NET_CAPABILITY_NOT_METERED
+         * @see NetworkCapabilities#NET_CAPABILITY_NOT_METERED
          */
         @NonNull
         public Builder setMetered(boolean isMetered) {
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 63e5107..9c2c5b8 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -836,7 +836,7 @@
          * @param isMetered {@code true} if VPN network should be treated as metered regardless of
          *     underlying network meteredness
          * @return this {@link Builder} object to facilitate chaining method calls
-         * @see #setUnderlyingNetworks(Networks[])
+         * @see #setUnderlyingNetworks(Network[])
          * @see ConnectivityManager#isActiveNetworkMetered()
          */
         @NonNull
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index e8af564..02b822a 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -1022,7 +1022,6 @@
              * behaviors or empty states. Instead, apps should store data needed
              * while a user is locked under device protected storage areas.
              *
-             * @see Context#createCredentialProtectedStorageContext()
              * @see Context#createDeviceProtectedStorageContext()
              */
             public @NonNull Builder detectCredentialProtectedWhileLocked() {
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 1fef071..fd7cdda 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -64,21 +64,16 @@
 
     /**
      * A click effect. Use this effect as a baseline, as it's the most common type of click effect.
-     *
-     * @see #get(int)
      */
     public static final int EFFECT_CLICK = Effect.CLICK;
 
     /**
      * A double click effect.
-     *
-     * @see #get(int)
      */
     public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK;
 
     /**
      * A tick effect. This effect is less strong compared to {@link #EFFECT_CLICK}.
-     * @see #get(int)
      */
     public static final int EFFECT_TICK = Effect.TICK;
 
@@ -102,7 +97,6 @@
 
     /**
      * A heavy click effect. This effect is stronger than {@link #EFFECT_CLICK}.
-     * @see #get(int)
      */
     public static final int EFFECT_HEAVY_CLICK = Effect.HEAVY_CLICK;
 
diff --git a/core/java/android/os/strictmode/CredentialProtectedWhileLockedViolation.java b/core/java/android/os/strictmode/CredentialProtectedWhileLockedViolation.java
index 12503f6..89cd430 100644
--- a/core/java/android/os/strictmode/CredentialProtectedWhileLockedViolation.java
+++ b/core/java/android/os/strictmode/CredentialProtectedWhileLockedViolation.java
@@ -28,7 +28,6 @@
  * store data needed while a user is locked under device protected storage
  * areas.
  *
- * @see Context#createCredentialProtectedStorageContext()
  * @see Context#createDeviceProtectedStorageContext()
  */
 public final class CredentialProtectedWhileLockedViolation extends Violation {
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index 9c6c92a..17fae1c 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -797,7 +797,6 @@
          * to changes.
          *
          * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
-         * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
          */
         @NonNull
         public static final Uri ENTERPRISE_CONTENT_URI =
@@ -1796,7 +1795,6 @@
          * to changes.
          *
          * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
-         * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
          */
         @NonNull
         public static final Uri ENTERPRISE_CONTENT_URI =
@@ -2010,7 +2008,6 @@
          * {@link DevicePolicyManager#setCrossProfileCalendarPackages(ComponentName, Set)}.
          *
          * @see DevicePolicyManager#getCrossProfileCalendarPackages(ComponentName)
-         * @see Settings.Secure#CROSS_PROFILE_CALENDAR_ENABLED
          */
         @NonNull
         public static final Uri ENTERPRISE_CONTENT_URI =
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index a10a456..75840a5 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -24,7 +24,6 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ContentInterface;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -251,14 +250,14 @@
      * Get string array identifies the type or types of metadata returned
      * using DocumentsContract#getDocumentMetadata.
      *
-     * @see #getDocumentMetadata(ContentInterface, Uri)
+     * @see #getDocumentMetadata(ContentResolver, Uri)
      */
     public static final String METADATA_TYPES = "android:documentMetadataTypes";
 
     /**
      * Get Exif information using DocumentsContract#getDocumentMetadata.
      *
-     * @see #getDocumentMetadata(ContentInterface, Uri)
+     * @see #getDocumentMetadata(ContentResolver, Uri)
      */
     public static final String METADATA_EXIF = "android:documentExif";
 
@@ -266,7 +265,7 @@
      * Get total count of all documents currently stored under the given
      * directory tree. Only valid for {@link Document#MIME_TYPE_DIR} documents.
      *
-     * @see #getDocumentMetadata(ContentInterface, Uri)
+     * @see #getDocumentMetadata(ContentResolver, Uri)
      */
     public static final String METADATA_TREE_COUNT = "android:metadataTreeCount";
 
@@ -274,7 +273,7 @@
      * Get total size of all documents currently stored under the given
      * directory tree. Only valid for {@link Document#MIME_TYPE_DIR} documents.
      *
-     * @see #getDocumentMetadata(ContentInterface, Uri)
+     * @see #getDocumentMetadata(ContentResolver, Uri)
      */
     public static final String METADATA_TREE_SIZE = "android:metadataTreeSize";
 
@@ -405,7 +404,7 @@
          * Flag indicating that a document can be represented as a thumbnail.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#getDocumentThumbnail(ContentInterface, Uri,
+         * @see DocumentsContract#getDocumentThumbnail(ContentResolver, Uri,
          *      Point, CancellationSignal)
          * @see DocumentsProvider#openDocumentThumbnail(String, Point,
          *      android.os.CancellationSignal)
@@ -431,7 +430,7 @@
          * Flag indicating that a document is deletable.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#deleteDocument(ContentInterface, Uri)
+         * @see DocumentsContract#deleteDocument(ContentResolver, Uri)
          * @see DocumentsProvider#deleteDocument(String)
          */
         public static final int FLAG_SUPPORTS_DELETE = 1 << 2;
@@ -469,7 +468,7 @@
          * Flag indicating that a document can be renamed.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#renameDocument(ContentInterface, Uri, String)
+         * @see DocumentsContract#renameDocument(ContentResolver, Uri, String)
          * @see DocumentsProvider#renameDocument(String, String)
          */
         public static final int FLAG_SUPPORTS_RENAME = 1 << 6;
@@ -479,7 +478,7 @@
          * within the same document provider.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#copyDocument(ContentInterface, Uri, Uri)
+         * @see DocumentsContract#copyDocument(ContentResolver, Uri, Uri)
          * @see DocumentsProvider#copyDocument(String, String)
          */
         public static final int FLAG_SUPPORTS_COPY = 1 << 7;
@@ -489,7 +488,7 @@
          * within the same document provider.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#moveDocument(ContentInterface, Uri, Uri, Uri)
+         * @see DocumentsContract#moveDocument(ContentResolver, Uri, Uri, Uri)
          * @see DocumentsProvider#moveDocument(String, String, String)
          */
         public static final int FLAG_SUPPORTS_MOVE = 1 << 8;
@@ -513,7 +512,7 @@
          * Flag indicating that a document can be removed from a parent.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#removeDocument(ContentInterface, Uri, Uri)
+         * @see DocumentsContract#removeDocument(ContentResolver, Uri, Uri)
          * @see DocumentsProvider#removeDocument(String, String)
          */
         public static final int FLAG_SUPPORTS_REMOVE = 1 << 10;
@@ -549,7 +548,7 @@
          * using DocumentsContract#getDocumentMetadata
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#getDocumentMetadata(ContentInterface, Uri)
+         * @see DocumentsContract#getDocumentMetadata(ContentResolver, Uri)
          */
         public static final int FLAG_SUPPORTS_METADATA = 1 << 14;
 
@@ -750,7 +749,7 @@
          * Flag indicating that this root can be ejected.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#ejectRoot(ContentInterface, Uri)
+         * @see DocumentsContract#ejectRoot(ContentResolver, Uri)
          * @see DocumentsProvider#ejectRoot(String)
          */
         public static final int FLAG_SUPPORTS_EJECT = 1 << 5;
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index c28d2bb..e274460 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -262,7 +262,7 @@
          *
          * @param condition condition used to trigger the updates.
          * @param updates actions to be applied to the
-         * {@link #CustomDescription.Builder(RemoteViews) template presentation} when the condition
+         * {@link #Builder(RemoteViews) template presentation} when the condition
          * is satisfied.
          *
          * @return this builder
diff --git a/core/java/android/service/autofill/ImageTransformation.java b/core/java/android/service/autofill/ImageTransformation.java
index 12376e8..974f0ea 100644
--- a/core/java/android/service/autofill/ImageTransformation.java
+++ b/core/java/android/service/autofill/ImageTransformation.java
@@ -123,7 +123,7 @@
          * {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
          *
          * @deprecated use
-         * {@link #ImageTransformation.Builder(AutofillId, Pattern, int, CharSequence)} instead.
+         * {@link #Builder(AutofillId, Pattern, int, CharSequence)} instead.
          */
         @Deprecated
         public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId) {
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index 19961e5..6c22b19 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -144,22 +144,24 @@
             final SurfaceControlViewHost host = new SurfaceControlViewHost(this, getDisplay(),
                     hostInputToken);
             host.setView(suggestionRoot, lp);
-            suggestionRoot.setOnClickListener((v) -> {
+
+            // Set the suggestion view to be non-focusable so that if its background is set to a
+            // ripple drawable, the ripple won't be shown initially.
+            suggestionView.setFocusable(false);
+            suggestionView.setOnClickListener((v) -> {
                 try {
-                    if (suggestionView.hasOnClickListeners()) {
-                        suggestionView.callOnClick();
-                    }
                     callback.onClick();
                 } catch (RemoteException e) {
                     Log.w(TAG, "RemoteException calling onClick()");
                 }
             });
-
-            suggestionRoot.setOnLongClickListener((v) -> {
+            final View.OnLongClickListener onLongClickListener =
+                    suggestionView.getOnLongClickListener();
+            suggestionView.setOnLongClickListener((v) -> {
+                if (onLongClickListener != null) {
+                    onLongClickListener.onLongClick(v);
+                }
                 try {
-                    if (suggestionView.hasOnLongClickListeners()) {
-                        suggestionView.performLongClick();
-                    }
                     callback.onLongClick();
                 } catch (RemoteException e) {
                     Log.w(TAG, "RemoteException calling onLongClick()");
diff --git a/core/java/android/service/autofill/InlineSuggestionRoot.java b/core/java/android/service/autofill/InlineSuggestionRoot.java
index 653e513..c879653 100644
--- a/core/java/android/service/autofill/InlineSuggestionRoot.java
+++ b/core/java/android/service/autofill/InlineSuggestionRoot.java
@@ -52,13 +52,8 @@
     }
 
     @Override
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        return true;
-    }
-
-    @Override
     @SuppressLint("ClickableViewAccessibility")
-    public boolean onTouchEvent(@NonNull MotionEvent event) {
+    public boolean dispatchTouchEvent(@NonNull MotionEvent event) {
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN: {
                 mDownX = event.getX();
@@ -80,6 +75,6 @@
                 }
             } break;
         }
-        return super.onTouchEvent(event);
+        return super.dispatchTouchEvent(event);
     }
 }
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 4df4362..e640eec 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -719,7 +719,7 @@
          *
          * <p>The sanitizer can also be used as an alternative for a
          * {@link #setValidator(Validator) validator}. If any of the {@code ids} is a
-         * {@link #SaveInfo.Builder(int, AutofillId[]) required id} and the {@code sanitizer} fails
+         * {@link #Builder(int, AutofillId[]) required id} and the {@code sanitizer} fails
          * because of it, then the save UI is not shown.
          *
          * @param sanitizer an implementation provided by the Android System.
@@ -777,7 +777,7 @@
          * Builds a new {@link SaveInfo} instance.
          *
          * @throws IllegalStateException if no
-         * {@link #SaveInfo.Builder(int, AutofillId[]) required ids},
+         * {@link #Builder(int, AutofillId[]) required ids},
          * or {@link #setOptionalIds(AutofillId[]) optional ids}, or {@link #FLAG_DELAY_SAVE}
          * were set
          */
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index c36a33f..e4fbf9f 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -379,8 +379,6 @@
      *
      * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
      * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
-     * @see #onEmergencyNumberListChanged
      */
     public static final int LISTEN_EMERGENCY_NUMBER_LIST                   = 0x01000000;
 
@@ -459,7 +457,7 @@
      * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
      * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
-     * @see #onRegistrationFailed()
+     * @see #onRegistrationFailed
      */
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public static final int LISTEN_REGISTRATION_FAILURE = 0x40000000;
@@ -470,7 +468,7 @@
      * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
      * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
-     * @see #onBarringInfoChanged()
+     * @see #onBarringInfoChanged
      */
     @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public static final int LISTEN_BARRING_INFO = 0x80000000;
diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java
index 2c260f6..5fcd38e 100644
--- a/core/java/android/util/proto/ProtoOutputStream.java
+++ b/core/java/android/util/proto/ProtoOutputStream.java
@@ -2275,7 +2275,7 @@
     /**
      * Write an individual field tag by hand.
      *
-     * @see See <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+     * See <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
      * Encoding</a> for details on the structure of how tags and data are written.
      */
     public void writeTag(int id, @WireType int wireType) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0625806..c0c29eb 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -1464,8 +1464,23 @@
      */
     public static final class DesiredDisplayConfigSpecs {
         public int defaultConfig;
-        public float minRefreshRate;
-        public float maxRefreshRate;
+        /**
+         * The primary refresh rate range represents display manager's general guidance on the
+         * display configs surface flinger will consider when switching refresh rates. Unless
+         * surface flinger has a specific reason to do otherwise, it will stay within this range.
+         */
+        public float primaryRefreshRateMin;
+        public float primaryRefreshRateMax;
+        /**
+         * The app request refresh rate range allows surface flinger to consider more display
+         * configs when switching refresh rates. Although surface flinger will generally stay within
+         * the primary range, specific considerations, such as layer frame rate settings specified
+         * via the setFrameRate() api, may cause surface flinger to go outside the primary
+         * range. Surface flinger never goes outside the app request range. The app request range
+         * will be greater than or equal to the primary refresh rate range, never smaller.
+         */
+        public float appRequestRefreshRateMin;
+        public float appRequestRefreshRateMax;
 
         public DesiredDisplayConfigSpecs() {}
 
@@ -1473,11 +1488,14 @@
             copyFrom(other);
         }
 
-        public DesiredDisplayConfigSpecs(
-                int defaultConfig, float minRefreshRate, float maxRefreshRate) {
+        public DesiredDisplayConfigSpecs(int defaultConfig, float primaryRefreshRateMin,
+                float primaryRefreshRateMax, float appRequestRefreshRateMin,
+                float appRequestRefreshRateMax) {
             this.defaultConfig = defaultConfig;
-            this.minRefreshRate = minRefreshRate;
-            this.maxRefreshRate = maxRefreshRate;
+            this.primaryRefreshRateMin = primaryRefreshRateMin;
+            this.primaryRefreshRateMax = primaryRefreshRateMax;
+            this.appRequestRefreshRateMin = appRequestRefreshRateMin;
+            this.appRequestRefreshRateMax = appRequestRefreshRateMax;
         }
 
         @Override
@@ -1490,8 +1508,10 @@
          */
         public boolean equals(DesiredDisplayConfigSpecs other) {
             return other != null && defaultConfig == other.defaultConfig
-                    && minRefreshRate == other.minRefreshRate
-                    && maxRefreshRate == other.maxRefreshRate;
+                    && primaryRefreshRateMin == other.primaryRefreshRateMin
+                    && primaryRefreshRateMax == other.primaryRefreshRateMax
+                    && appRequestRefreshRateMin == other.appRequestRefreshRateMin
+                    && appRequestRefreshRateMax == other.appRequestRefreshRateMax;
         }
 
         @Override
@@ -1504,14 +1524,18 @@
          */
         public void copyFrom(DesiredDisplayConfigSpecs other) {
             defaultConfig = other.defaultConfig;
-            minRefreshRate = other.minRefreshRate;
-            maxRefreshRate = other.maxRefreshRate;
+            primaryRefreshRateMin = other.primaryRefreshRateMin;
+            primaryRefreshRateMax = other.primaryRefreshRateMax;
+            appRequestRefreshRateMin = other.appRequestRefreshRateMin;
+            appRequestRefreshRateMax = other.appRequestRefreshRateMax;
         }
 
         @Override
         public String toString() {
-            return String.format("defaultConfig=%d min=%.0f max=%.0f", defaultConfig,
-                    minRefreshRate, maxRefreshRate);
+            return String.format("defaultConfig=%d primaryRefreshRateRange=[%.0f %.0f]"
+                            + " appRequestRefreshRateRange=[%.0f %.0f]",
+                    defaultConfig, primaryRefreshRateMin, primaryRefreshRateMax,
+                    appRequestRefreshRateMin, appRequestRefreshRateMax);
         }
     }
 
diff --git a/core/java/android/view/VerifiedInputEvent.java b/core/java/android/view/VerifiedInputEvent.java
index 531b3ed..e2db501 100644
--- a/core/java/android/view/VerifiedInputEvent.java
+++ b/core/java/android/view/VerifiedInputEvent.java
@@ -92,8 +92,6 @@
      * time base.
      *
      * @see InputEvent#getEventTime()
-     * @see KeyEvent#getEventTimeNano()
-     * @see MotionEvent#getEventTimeNano()
      */
     @SuppressLint("MethodNameUnits")
     public long getEventTimeNanos() {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3102999..facf861 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7263,6 +7263,16 @@
     }
 
     /**
+     * @return the registered {@link OnLongClickListener} if there is one, {@code null} otherwise.
+     * @hide
+     */
+    @Nullable
+    public OnLongClickListener getOnLongClickListener() {
+        ListenerInfo li = mListenerInfo;
+        return (li != null) ? li.mOnLongClickListener : null;
+    }
+
+    /**
      * Register a callback to be invoked when this view is context clicked. If the view is not
      * context clickable, it becomes context clickable.
      *
@@ -26175,9 +26185,9 @@
 
         /**
          * Returns the View object that had been passed to the
-         * {@link #View.DragShadowBuilder(View)}
+         * {@link #DragShadowBuilder(View)}
          * constructor.  If that View parameter was {@code null} or if the
-         * {@link #View.DragShadowBuilder()}
+         * {@link #DragShadowBuilder()}
          * constructor was used to instantiate the builder object, this method will return
          * null.
          *
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e4dbd637..9a3c706 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -700,7 +700,6 @@
          * @see #TYPE_TOAST
          * @see #TYPE_SYSTEM_OVERLAY
          * @see #TYPE_PRIORITY_PHONE
-         * @see #TYPE_STATUS_BAR_PANEL
          * @see #TYPE_SYSTEM_DIALOG
          * @see #TYPE_KEYGUARD_DIALOG
          * @see #TYPE_SYSTEM_ERROR
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 6646c31..eaaaa80 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -5120,7 +5120,7 @@
          * Obtains a pooled instance that is a clone of another one.
          *
          * <p>In most situations object pooling is not beneficial. Create a new instance using the
-         * constructor {@link AccessibilityNodeInfo.RangeInfo#AccessibilityNodeInfo.RangeInfo(int,
+         * constructor {@link AccessibilityNodeInfo.RangeInfo#RangeInfo(int,
          * float, float, float)} instead.
          *
          * @param other The instance to clone.
@@ -5135,7 +5135,7 @@
          * Obtains a pooled instance.
          *
          * <p>In most situations object pooling is not beneficial. Create a new instance using the
-         * constructor {@link AccessibilityNodeInfo.RangeInfo#AccessibilityNodeInfo.RangeInfo(int,
+         * constructor {@link AccessibilityNodeInfo.RangeInfo#RangeInfo(int,
          * float, float, float)} instead.
          *
          * @param type The type of the range.
@@ -5271,7 +5271,7 @@
          *
          * <p>In most situations object pooling is not beneficial. Create a new instance using the
          * constructor {@link
-         * AccessibilityNodeInfo.CollectionInfo#AccessibilityNodeInfo.CollectionInfo} instead.
+         * AccessibilityNodeInfo.CollectionInfo#CollectionInfo} instead.
          *
          * @param other The instance to clone.
          * @hide
@@ -5286,7 +5286,7 @@
          *
          * <p>In most situations object pooling is not beneficial. Create a new instance using the
          * constructor {@link
-         * AccessibilityNodeInfo.CollectionInfo#AccessibilityNodeInfo.CollectionInfo(int, int,
+         * AccessibilityNodeInfo.CollectionInfo#CollectionInfo(int, int,
          * boolean)} instead.
          *
          * @param rowCount The number of rows, or -1 if count is unknown.
@@ -5303,7 +5303,7 @@
          *
          * <p>In most situations object pooling is not beneficial. Create a new instance using the
          * constructor {@link
-         * AccessibilityNodeInfo.CollectionInfo#AccessibilityNodeInfo.CollectionInfo(int, int,
+         * AccessibilityNodeInfo.CollectionInfo#CollectionInfo(int, int,
          * boolean, int)} instead.
          *
          * @param rowCount The number of rows.
@@ -5440,7 +5440,7 @@
          *
          * <p>In most situations object pooling is not beneficial. Create a new instance using the
          * constructor {@link
-         * AccessibilityNodeInfo.CollectionItemInfo#AccessibilityNodeInfo.CollectionItemInfo}
+         * AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo}
          * instead.
          *
          * @param other The instance to clone.
@@ -5456,7 +5456,7 @@
          *
          * <p>In most situations object pooling is not beneficial. Create a new instance using the
          * constructor {@link
-         * AccessibilityNodeInfo.CollectionItemInfo#AccessibilityNodeInfo.CollectionItemInfo(int,
+         * AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo(int,
          * int, int, int, boolean)} instead.
          *
          * @param rowIndex The row index at which the item is located.
@@ -5476,7 +5476,7 @@
          *
          * <p>In most situations object pooling is not beneficial. Creates a new instance using the
          * constructor {@link
-         * AccessibilityNodeInfo.CollectionItemInfo#AccessibilityNodeInfo.CollectionItemInfo(int,
+         * AccessibilityNodeInfo.CollectionItemInfo#CollectionItemInfo(int,
          * int, int, int, boolean, boolean)} instead.
          *
          * @param rowIndex The row index at which the item is located.
diff --git a/core/java/android/view/inspector/StaticInspectionCompanionProvider.java b/core/java/android/view/inspector/StaticInspectionCompanionProvider.java
index 42a892d..903fc13 100644
--- a/core/java/android/view/inspector/StaticInspectionCompanionProvider.java
+++ b/core/java/android/view/inspector/StaticInspectionCompanionProvider.java
@@ -21,8 +21,6 @@
 
 /**
  * An inspection companion provider that finds companions as inner classes or generated code.
- *
- * @see android.processor.view.inspector.PlatformInspectableProcessor
  */
 public class StaticInspectionCompanionProvider implements InspectionCompanionProvider {
     /**
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index fb96210..b35eb06 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -714,9 +714,7 @@
     /**
      * Callback object to be called when the toast is shown or hidden.
      *
-     * <p>Callback methods will be called on the looper thread used for the {@link Toast} object.
-     *
-     * @see #makeText(Context, Looper, CharSequence, int)
+     * @see #makeText(Context, CharSequence, int)
      * @see #addCallback(Callback)
      */
     public abstract static class Callback {
diff --git a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
index 4786d41..0e703fa 100644
--- a/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
+++ b/core/java/com/android/internal/policy/GestureNavigationSettingsObserver.java
@@ -26,6 +26,7 @@
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
+import android.util.DisplayMetrics;
 import android.util.TypedValue;
 
 /**
@@ -99,12 +100,13 @@
     }
 
     private int getSensitivity(Resources userRes, String side) {
+        final DisplayMetrics dm = userRes.getDisplayMetrics();
         final float defaultInset = userRes.getDimension(
-                com.android.internal.R.dimen.config_backGestureInset);
+                com.android.internal.R.dimen.config_backGestureInset) / dm.density;
         final float backGestureInset = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
                 BACK_GESTURE_EDGE_WIDTH, defaultInset);
         final float inset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, backGestureInset,
-                userRes.getDisplayMetrics());
+                dm);
         final float scale = Settings.Secure.getFloatForUser(
                 mContext.getContentResolver(), side, 1.0f, UserHandle.USER_CURRENT);
         return (int) (inset * scale);
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 5869787..36b4b6a 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -174,8 +174,10 @@
     jclass clazz;
     jmethodID ctor;
     jfieldID defaultConfig;
-    jfieldID minRefreshRate;
-    jfieldID maxRefreshRate;
+    jfieldID primaryRefreshRateMin;
+    jfieldID primaryRefreshRateMax;
+    jfieldID appRequestRefreshRateMin;
+    jfieldID appRequestRefreshRateMax;
 } gDesiredDisplayConfigSpecsClassInfo;
 
 class JNamedColorSpace {
@@ -919,13 +921,24 @@
 
     jint defaultConfig = env->GetIntField(desiredDisplayConfigSpecs,
                                           gDesiredDisplayConfigSpecsClassInfo.defaultConfig);
-    jfloat minRefreshRate = env->GetFloatField(desiredDisplayConfigSpecs,
-                                               gDesiredDisplayConfigSpecsClassInfo.minRefreshRate);
-    jfloat maxRefreshRate = env->GetFloatField(desiredDisplayConfigSpecs,
-                                               gDesiredDisplayConfigSpecsClassInfo.maxRefreshRate);
+    jfloat primaryRefreshRateMin =
+            env->GetFloatField(desiredDisplayConfigSpecs,
+                               gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMin);
+    jfloat primaryRefreshRateMax =
+            env->GetFloatField(desiredDisplayConfigSpecs,
+                               gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMax);
+    jfloat appRequestRefreshRateMin =
+            env->GetFloatField(desiredDisplayConfigSpecs,
+                               gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMin);
+    jfloat appRequestRefreshRateMax =
+            env->GetFloatField(desiredDisplayConfigSpecs,
+                               gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMax);
 
-    size_t result = SurfaceComposerClient::setDesiredDisplayConfigSpecs(
-            token, defaultConfig, minRefreshRate, maxRefreshRate);
+    size_t result = SurfaceComposerClient::setDesiredDisplayConfigSpecs(token, defaultConfig,
+                                                                        primaryRefreshRateMin,
+                                                                        primaryRefreshRateMax,
+                                                                        appRequestRefreshRateMin,
+                                                                        appRequestRefreshRateMax);
     return result == NO_ERROR ? JNI_TRUE : JNI_FALSE;
 }
 
@@ -934,16 +947,23 @@
     if (token == nullptr) return nullptr;
 
     int32_t defaultConfig;
-    float minRefreshRate;
-    float maxRefreshRate;
-    if (SurfaceComposerClient::getDesiredDisplayConfigSpecs(token, &defaultConfig, &minRefreshRate,
-                                                            &maxRefreshRate) != NO_ERROR) {
+    float primaryRefreshRateMin;
+    float primaryRefreshRateMax;
+    float appRequestRefreshRateMin;
+    float appRequestRefreshRateMax;
+    if (SurfaceComposerClient::getDesiredDisplayConfigSpecs(token, &defaultConfig,
+                                                            &primaryRefreshRateMin,
+                                                            &primaryRefreshRateMax,
+                                                            &appRequestRefreshRateMin,
+                                                            &appRequestRefreshRateMax) !=
+        NO_ERROR) {
         return nullptr;
     }
 
     return env->NewObject(gDesiredDisplayConfigSpecsClassInfo.clazz,
-                          gDesiredDisplayConfigSpecsClassInfo.ctor, defaultConfig, minRefreshRate,
-                          maxRefreshRate);
+                          gDesiredDisplayConfigSpecsClassInfo.ctor, defaultConfig,
+                          primaryRefreshRateMin, primaryRefreshRateMax, appRequestRefreshRateMin,
+                          appRequestRefreshRateMax);
 }
 
 static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) {
@@ -1757,13 +1777,17 @@
     gDesiredDisplayConfigSpecsClassInfo.clazz =
             MakeGlobalRefOrDie(env, desiredDisplayConfigSpecsClazz);
     gDesiredDisplayConfigSpecsClassInfo.ctor =
-            GetMethodIDOrDie(env, gDesiredDisplayConfigSpecsClassInfo.clazz, "<init>", "(IFF)V");
+            GetMethodIDOrDie(env, gDesiredDisplayConfigSpecsClassInfo.clazz, "<init>", "(IFFFF)V");
     gDesiredDisplayConfigSpecsClassInfo.defaultConfig =
             GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "defaultConfig", "I");
-    gDesiredDisplayConfigSpecsClassInfo.minRefreshRate =
-            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "minRefreshRate", "F");
-    gDesiredDisplayConfigSpecsClassInfo.maxRefreshRate =
-            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "maxRefreshRate", "F");
+    gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMin =
+            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "primaryRefreshRateMin", "F");
+    gDesiredDisplayConfigSpecsClassInfo.primaryRefreshRateMax =
+            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "primaryRefreshRateMax", "F");
+    gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMin =
+            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "appRequestRefreshRateMin", "F");
+    gDesiredDisplayConfigSpecsClassInfo.appRequestRefreshRateMax =
+            GetFieldIDOrDie(env, desiredDisplayConfigSpecsClazz, "appRequestRefreshRateMax", "F");
 
     return err;
 }
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a8d1605..b92bbd6 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4820,7 +4820,7 @@
              May be a string value, which is a comma-separated language tag list, such as "ja-JP,zh-CN".
              When not specified or an empty string is given, it will fallback to the default one.
              {@see android.os.LocaleList#forLanguageTags(String)}
-             {@see android.text.TextView#setTextLocales(android.os.LocaleList)} -->
+             {@see android.widget.TextView#setTextLocales(android.os.LocaleList)} -->
         <attr name="textLocale" format="string" />
         <!-- Text color for links. -->
         <attr name="textColorLink" />
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 34417e6..a93dacf 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -44,8 +44,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.IBinder;
+import android.util.DisplayMetrics;
 import android.util.MergedConfiguration;
 import android.view.Display;
 import android.view.View;
@@ -367,6 +370,58 @@
     }
 
     @Test
+    public void testHandleConfigurationChanged_DoesntOverrideActivityConfig() {
+        final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            final Configuration oldActivityConfig =
+                    new Configuration(activity.getResources().getConfiguration());
+            final DisplayMetrics oldActivityMetrics = new DisplayMetrics();
+            activity.getDisplay().getMetrics(oldActivityMetrics);
+            final Resources oldAppResources = activity.getApplication().getResources();
+            final Configuration oldAppConfig =
+                    new Configuration(oldAppResources.getConfiguration());
+            final DisplayMetrics oldApplicationMetrics = new DisplayMetrics();
+            oldApplicationMetrics.setTo(oldAppResources.getDisplayMetrics());
+            assertEquals("Process config must match the top activity config by default",
+                    0, oldActivityConfig.diffPublicOnly(oldAppConfig));
+            assertEquals("Process config must match the top activity config by default",
+                    oldActivityMetrics, oldApplicationMetrics);
+
+            // Update the application configuration separately from activity config
+            final Configuration newAppConfig = new Configuration(oldAppConfig);
+            newAppConfig.densityDpi += 100;
+            newAppConfig.screenHeightDp += 100;
+            final Rect newBounds = new Rect(newAppConfig.windowConfiguration.getAppBounds());
+            newBounds.bottom += 100;
+            newAppConfig.windowConfiguration.setAppBounds(newBounds);
+            newAppConfig.windowConfiguration.setBounds(newBounds);
+            newAppConfig.seq++;
+
+            final ActivityThread activityThread = activity.getActivityThread();
+            activityThread.handleConfigurationChanged(newAppConfig);
+
+            // Verify that application config update was applied, but didn't change activity config.
+            assertEquals("Activity config must not change if the process config changes",
+                    oldActivityConfig, activity.getResources().getConfiguration());
+
+            final DisplayMetrics newActivityMetrics = new DisplayMetrics();
+            activity.getDisplay().getMetrics(newActivityMetrics);
+            assertEquals("Activity display size must not change if the process config changes",
+                    oldActivityMetrics, newActivityMetrics);
+            final Resources newAppResources = activity.getApplication().getResources();
+            assertEquals("Application config must be updated",
+                    newAppConfig, newAppResources.getConfiguration());
+            final DisplayMetrics newApplicationMetrics = new DisplayMetrics();
+            newApplicationMetrics.setTo(newAppResources.getDisplayMetrics());
+            assertNotEquals("Application display size must be updated after config update",
+                    oldApplicationMetrics, newApplicationMetrics);
+            assertNotEquals("Application display size must be updated after config update",
+                    newActivityMetrics, newApplicationMetrics);
+        });
+    }
+
+    @Test
     public void testResumeAfterNewIntent() {
         final Activity activity = mActivityTestRule.launchActivity(new Intent());
         final ActivityThread activityThread = activity.getActivityThread();
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 752695f..22f5489 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -998,7 +998,7 @@
      * Sets the rotation value for the display list around the Z axis.
      *
      * @param rotation The rotation value of the display list, in degrees
-     * @see View#setRotationZ(float)
+     * @see View#setRotation(float)
      * @see #getRotationZ()
      * @return True if the value changed, false if the new value was the same as the previous value.
      */
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index e70529b..9cf12f1 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -617,7 +617,7 @@
      * {@link #setTintList(ColorStateList) tint}.
      * </p>
      *
-     * @see {@link #setColorFilter(ColorFilter)} }
+     * @see #setColorFilter(ColorFilter)
      * @deprecated use {@link #setColorFilter(ColorFilter)} with an instance
      * of {@link android.graphics.BlendModeColorFilter}
      */
diff --git a/graphics/java/android/graphics/fonts/FontStyle.java b/graphics/java/android/graphics/fonts/FontStyle.java
index af517d6..09799fd 100644
--- a/graphics/java/android/graphics/fonts/FontStyle.java
+++ b/graphics/java/android/graphics/fonts/FontStyle.java
@@ -217,7 +217,7 @@
      /**
       * Gets the weight value
       *
-      * @see FontStyle#setWeight(int)
+      * @see #FontStyle(int, int)
       * @return a weight value
       */
     public @IntRange(from = 0, to = 1000) int getWeight() {
diff --git a/graphics/java/android/graphics/text/LineBreaker.java b/graphics/java/android/graphics/text/LineBreaker.java
index 54622c5..babcfc3 100644
--- a/graphics/java/android/graphics/text/LineBreaker.java
+++ b/graphics/java/android/graphics/text/LineBreaker.java
@@ -320,7 +320,7 @@
         /**
          * Returns the array of tab stops in pixels.
          *
-         * @see #setTabStops(float[], int)
+         * @see #setTabStops
          */
         public @Nullable float[] getTabStops() {
             return mVariableTabStops;
@@ -329,7 +329,7 @@
         /**
          * Returns the default tab stops in pixels.
          *
-         * @see #setTabStop(float[], int)
+         * @see #setTabStops
          */
         public @Px @FloatRange(from = 0) float getDefaultTabStop() {
             return mDefaultTabStop;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 6761435..31e4555 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -27,7 +27,6 @@
 #include "DamageAccumulator.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #endif
-#include "utils/FatVector.h"
 #include "utils/MathUtils.h"
 #include "utils/StringUtils.h"
 #include "utils/TraceUtils.h"
@@ -37,6 +36,7 @@
 #include <atomic>
 #include <sstream>
 #include <string>
+#include <ui/FatVector.h>
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index d55e5b0..c0ec217 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -27,6 +27,8 @@
 
 #include <androidfw/ResourceTypes.h>
 
+#include <ui/FatVector.h>
+
 #include "AnimatorManager.h"
 #include "CanvasTransform.h"
 #include "Debug.h"
@@ -35,7 +37,6 @@
 #include "RenderProperties.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #include "pipeline/skia/SkiaLayer.h"
-#include "utils/FatVector.h"
 
 #include <vector>
 
diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp
index 0ce04a2..a2fef1e 100644
--- a/libs/hwui/jni/FontFamily.cpp
+++ b/libs/hwui/jni/FontFamily.cpp
@@ -29,9 +29,9 @@
 
 #include <hwui/MinikinSkia.h>
 #include <hwui/Typeface.h>
-#include <utils/FatVector.h>
 #include <minikin/FontFamily.h>
 #include <minikin/LocaleList.h>
+#include <ui/FatVector.h>
 
 #include <memory>
 
@@ -104,7 +104,7 @@
 
 static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex,
         jint weight, jint italic) {
-    uirenderer::FatVector<SkFontArguments::Axis, 2> skiaAxes;
+    FatVector<SkFontArguments::Axis, 2> skiaAxes;
     for (const auto& axis : builder->axes) {
         skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
     }
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 7e8f8d8..5714cd1 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -28,8 +28,8 @@
 
 #include <hwui/MinikinSkia.h>
 #include <hwui/Typeface.h>
-#include <utils/FatVector.h>
 #include <minikin/FontFamily.h>
+#include <ui/FatVector.h>
 
 #include <memory>
 
@@ -93,7 +93,7 @@
     sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
             release_global_ref, reinterpret_cast<void*>(fontRef)));
 
-    uirenderer::FatVector<SkFontArguments::Axis, 2> skiaAxes;
+    FatVector<SkFontArguments::Axis, 2> skiaAxes;
     for (const auto& axis : builder->axes) {
         skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
     }
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
index cfc0f9b..d669f84 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
@@ -21,7 +21,7 @@
 
 #include <SkCanvas.h>
 #include <SkDrawable.h>
-#include <utils/FatVector.h>
+#include <ui/FatVector.h>
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index c19b187..335bcdc 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -440,6 +440,12 @@
 
     if (dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw()) {
         mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
+        // Notify the callbacks, even if there's nothing to draw so they aren't waiting
+        // indefinitely
+        for (auto& func : mFrameCompleteCallbacks) {
+            std::invoke(func, mFrameNumber);
+        }
+        mFrameCompleteCallbacks.clear();
         return;
     }
 
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index cae3e3b..206b58f 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -27,7 +27,6 @@
 #include "pipeline/skia/SkiaOpenGLPipeline.h"
 #include "pipeline/skia/SkiaVulkanPipeline.h"
 #include "renderstate/RenderState.h"
-#include "utils/FatVector.h"
 #include "utils/TimeUtils.h"
 #include "utils/TraceUtils.h"
 
@@ -40,6 +39,8 @@
 #include <utils/Mutex.h>
 #include <thread>
 
+#include <ui/FatVector.h>
+
 namespace android {
 namespace uirenderer {
 namespace renderthread {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index a5355fc..ba70afc 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -23,13 +23,13 @@
 #include <GrContext.h>
 #include <GrTypes.h>
 #include <android/sync.h>
+#include <ui/FatVector.h>
 #include <vk/GrVkExtensions.h>
 #include <vk/GrVkTypes.h>
 
 #include "Properties.h"
 #include "RenderThread.h"
 #include "renderstate/RenderState.h"
-#include "utils/FatVector.h"
 #include "utils/TraceUtils.h"
 
 namespace android {
diff --git a/libs/hwui/tests/unit/FatVectorTests.cpp b/libs/hwui/tests/unit/FatVectorTests.cpp
index 8523e6c..6585a62 100644
--- a/libs/hwui/tests/unit/FatVectorTests.cpp
+++ b/libs/hwui/tests/unit/FatVectorTests.cpp
@@ -15,7 +15,7 @@
  */
 
 #include <gtest/gtest.h>
-#include <utils/FatVector.h>
+#include <ui/FatVector.h>
 
 #include <tests/common/TestUtils.h>
 
diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h
deleted file mode 100644
index 49f1984..0000000
--- a/libs/hwui/utils/FatVector.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2015, 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.
- */
-
-#ifndef ANDROID_FAT_VECTOR_H
-#define ANDROID_FAT_VECTOR_H
-
-#include "utils/Macros.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <utils/Log.h>
-#include <type_traits>
-
-#include <vector>
-
-namespace android {
-namespace uirenderer {
-
-template <typename T, size_t SIZE>
-class InlineStdAllocator {
-public:
-    struct Allocation {
-        PREVENT_COPY_AND_ASSIGN(Allocation);
-
-    public:
-        Allocation(){};
-        // char array instead of T array, so memory is uninitialized, with no destructors run
-        char array[sizeof(T) * SIZE];
-        bool inUse = false;
-    };
-
-    typedef T value_type;  // needed to implement std::allocator
-    typedef T* pointer;    // needed to implement std::allocator
-
-    explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {}
-    InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {}
-    ~InlineStdAllocator() {}
-
-    T* allocate(size_t num, const void* = 0) {
-        if (!mAllocation.inUse && num <= SIZE) {
-            mAllocation.inUse = true;
-            return (T*)mAllocation.array;
-        } else {
-            return (T*)malloc(num * sizeof(T));
-        }
-    }
-
-    void deallocate(pointer p, size_t num) {
-        if (p == (T*)mAllocation.array) {
-            mAllocation.inUse = false;
-        } else {
-            // 'free' instead of delete here - destruction handled separately
-            free(p);
-        }
-    }
-    Allocation& mAllocation;
-};
-
-/**
- * std::vector with SIZE elements preallocated into an internal buffer.
- *
- * Useful for avoiding the cost of malloc in cases where only SIZE or
- * fewer elements are needed in the common case.
- */
-template <typename T, size_t SIZE>
-class FatVector : public std::vector<T, InlineStdAllocator<T, SIZE>> {
-public:
-    FatVector()
-            : std::vector<T, InlineStdAllocator<T, SIZE>>(
-                      InlineStdAllocator<T, SIZE>(mAllocation)) {
-        this->reserve(SIZE);
-    }
-
-    explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); }
-
-private:
-    typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
-};
-
-}  // namespace uirenderer
-}  // namespace android
-
-#endif  // ANDROID_FAT_VECTOR_H
diff --git a/media/java/android/media/AudioFocusRequest.java b/media/java/android/media/AudioFocusRequest.java
index 4e70501..4c0850b 100644
--- a/media/java/android/media/AudioFocusRequest.java
+++ b/media/java/android/media/AudioFocusRequest.java
@@ -80,9 +80,9 @@
  * <p>An {@code AudioFocusRequest} instance always contains one of the four types of requests
  * explained above. It is passed when building an {@code AudioFocusRequest} instance with its
  * builder in the {@link Builder} constructor
- * {@link AudioFocusRequest.Builder#AudioFocusRequest.Builder(int)}, or
+ * {@link AudioFocusRequest.Builder#Builder(int)}, or
  * with {@link AudioFocusRequest.Builder#setFocusGain(int)} after copying an existing instance with
- * {@link AudioFocusRequest.Builder#AudioFocusRequest.Builder(AudioFocusRequest)}.
+ * {@link AudioFocusRequest.Builder#Builder(AudioFocusRequest)}.
  *
  * <h3>Qualifying your focus request</h3>
  * <h4>Use case requiring a focus request</h4>
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 1d229b80..9f3fc5d 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1357,7 +1357,7 @@
      * Can only be called only if the AudioTrack is opened in offload mode
      * {@see Builder#setOffloadedPlayback(boolean)}.
      * Can only be called only if the AudioTrack is in state {@link #PLAYSTATE_PLAYING}
-     * {@see #getPlaystate()}.
+     * {@see #getPlayState()}.
      * Use this method in the same thread as any write() operation.
      */
     public void setOffloadEndOfStream() {
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 1617b87..559a61d 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -535,7 +535,7 @@
      *         if such a frame cannot be retrieved. {@link Bitmap#getConfig()} can
      *         be used to query the actual {@link Bitmap.Config}.
      *
-     * @see {@link #getFrameAtTime(long, int, BitmapParams)}
+     * @see #getFrameAtTime(long, int, BitmapParams)
      */
     public @Nullable Bitmap getFrameAtTime(long timeUs, @Option int option) {
         if (option < OPTION_PREVIOUS_SYNC ||
@@ -581,7 +581,7 @@
      * @return A Bitmap containing a representative video frame, which
      *         can be null, if such a frame cannot be retrieved.
      *
-     * @see {@link #getFrameAtTime(long, int)}
+     * @see #getFrameAtTime(long, int)
      */
     public @Nullable Bitmap getFrameAtTime(
             long timeUs, @Option int option, @NonNull BitmapParams params) {
@@ -623,7 +623,7 @@
      *         be used to query the actual {@link Bitmap.Config}.
      * @throws IllegalArgumentException if passed in invalid option or width by height
      *         is less than or equal to 0.
-     * @see {@link #getScaledFrameAtTime(long, int, int, int, BitmapParams)}
+     * @see #getScaledFrameAtTime(long, int, int, int, BitmapParams)
      */
     public @Nullable Bitmap getScaledFrameAtTime(long timeUs, @Option int option,
             @IntRange(from=1) int dstWidth, @IntRange(from=1) int dstHeight) {
@@ -668,7 +668,7 @@
      *         scaled video frame, which can be null, if such a frame cannot be retrieved.
      * @throws IllegalArgumentException if passed in invalid option or width by height
      *         is less than or equal to 0.
-     * @see {@link #getScaledFrameAtTime(long, int, int, int)}
+     * @see #getScaledFrameAtTime(long, int, int, int)
      */
     public @Nullable Bitmap getScaledFrameAtTime(long timeUs, @Option int option,
             @IntRange(from=1) int dstWidth, @IntRange(from=1) int dstHeight,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 1306657..c483d0e 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -692,6 +692,7 @@
         </activity>
 
         <activity android:name=".controls.management.ControlsFavoritingActivity"
+                  android:label="@string/controls_favorite_default_title"
                   android:theme="@style/Theme.ControlsManagement"
                   android:excludeFromRecents="true"
                   android:showForAllUsers="true"
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index bd75116..eb15262 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -124,7 +124,7 @@
         val collator = Collator.getInstance(resources.configuration.locales[0])
         comparator = compareBy(collator) { it.structureName }
         appName = intent.getCharSequenceExtra(EXTRA_APP)
-        structureExtra = intent.getCharSequenceExtra(EXTRA_STRUCTURE) ?: ""
+        structureExtra = intent.getCharSequenceExtra(EXTRA_STRUCTURE)
         component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
         fromProviderSelector = intent.getBooleanExtra(EXTRA_FROM_PROVIDER_SELECTOR, false)
 
@@ -206,7 +206,9 @@
                 override fun onPageSelected(position: Int) {
                     super.onPageSelected(position)
                     val name = listOfStructures[position].structureName
-                    titleView.text = if (!TextUtils.isEmpty(name)) name else appName
+                    val title = if (!TextUtils.isEmpty(name)) name else appName
+                    titleView.text = title
+                    setTitle(title)
                 }
 
                 override fun onPageScrolled(
@@ -261,7 +263,6 @@
 
         val title = structureExtra
             ?: (appName ?: resources.getText(R.string.controls_favorite_default_title))
-        setTitle(title)
         titleView = requireViewById<TextView>(R.id.title).apply {
             text = title
         }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 7091d98..9055479 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -19,6 +19,8 @@
 import android.app.Dialog
 import android.content.Context
 import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
 import android.os.Vibrator
 import android.os.VibrationEffect
 import android.service.controls.Control
@@ -26,10 +28,12 @@
 import android.service.controls.actions.CommandAction
 import android.util.Log
 import android.view.HapticFeedbackConstants
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.globalactions.GlobalActionsComponent
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.R
 
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -38,6 +42,7 @@
 class ControlActionCoordinatorImpl @Inject constructor(
     private val context: Context,
     private val bgExecutor: DelayableExecutor,
+    @Main private val uiExecutor: DelayableExecutor,
     private val activityStarter: ActivityStarter,
     private val keyguardStateController: KeyguardStateController,
     private val globalActionsComponent: GlobalActionsComponent
@@ -110,9 +115,24 @@
     }
 
     private fun showDialog(cvh: ControlViewHolder, intent: Intent) {
-        dialog = DetailDialog(cvh, intent).also {
-            it.setOnDismissListener { _ -> dialog = null }
-            it.show()
+        bgExecutor.execute {
+            val activities: List<ResolveInfo> = cvh.context.packageManager.queryIntentActivities(
+                intent,
+                PackageManager.MATCH_DEFAULT_ONLY
+            )
+
+            uiExecutor.execute {
+                // make sure the intent is valid before attempting to open the dialog
+                if (activities.isNotEmpty()) {
+                    dialog = DetailDialog(cvh, intent).also {
+                        it.setOnDismissListener { _ -> dialog = null }
+                        it.show()
+                    }
+                } else {
+                    cvh.setTransientStatus(
+                        cvh.context.resources.getString(R.string.controls_error_failed))
+                }
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index fb6b093..4e4c82c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -31,6 +31,13 @@
 
     fun show(parent: ViewGroup, dismissGlobalActions: Runnable)
     fun hide()
+
+    /**
+     * Request all open dialogs be closed. Set [immediately] to true to dismiss without
+     * animations.
+     */
+    fun closeDialogs(immediately: Boolean)
+
     fun onRefreshState(componentName: ComponentName, controls: List<Control>)
     fun onActionResponse(
         componentName: ComponentName,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 3c54ad9..52d564d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -503,15 +503,24 @@
         }
     }
 
-    override fun hide() {
-        Log.d(ControlsUiController.TAG, "hide()")
-        hidden = true
-        popup?.dismissImmediate()
+    override fun closeDialogs(immediately: Boolean) {
+        if (immediately) {
+            popup?.dismissImmediate()
+        } else {
+            popup?.dismiss()
+        }
+        popup = null
 
         controlViewsById.forEach {
             it.value.dismiss()
         }
         controlActionCoordinator.closeDialogs()
+    }
+
+    override fun hide() {
+        hidden = true
+
+        closeDialogs(true)
         controlsController.get().unsubscribe()
 
         parent.removeAllViews()
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
index 9823a91..3dc0ff3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -145,6 +145,10 @@
                             template.isChecked())
                         true
                     }
+                    AccessibilityNodeInfo.ACTION_LONG_CLICK -> {
+                        cvh.controlActionCoordinator.longPress(cvh)
+                        true
+                    }
                     AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS.getId() -> {
                         if (arguments == null || !arguments.containsKey(
                                 AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE)) {
@@ -152,8 +156,8 @@
                         } else {
                             val value = arguments.getFloat(
                                 AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE)
-                            val level = rangeToLevelValue(value - rangeTemplate.getCurrentValue())
-                            updateRange(level, template.isChecked(), /* isDragging */ false)
+                            val level = rangeToLevelValue(value)
+                            updateRange(level, template.isChecked(), /* isDragging */ true)
                             endUpdateRange()
                             true
                         }
@@ -168,7 +172,7 @@
                 host: ViewGroup,
                 child: View,
                 event: AccessibilityEvent
-            ): Boolean = false
+            ): Boolean = true
         })
     }
 
@@ -180,14 +184,12 @@
     fun updateRange(level: Int, checked: Boolean, isDragging: Boolean) {
         val newLevel = if (checked) Math.max(MIN_LEVEL, Math.min(MAX_LEVEL, level)) else MIN_LEVEL
 
-        if (newLevel == clipLayer.level) return
-
         rangeAnimator?.cancel()
         if (isDragging) {
             clipLayer.level = newLevel
             val isEdge = newLevel == MIN_LEVEL || newLevel == MAX_LEVEL
             cvh.controlActionCoordinator.drag(isEdge)
-        } else {
+        } else if (newLevel != clipLayer.level) {
             rangeAnimator = ValueAnimator.ofInt(cvh.clipLayer.level, newLevel).apply {
                 addUpdateListener {
                     cvh.clipLayer.level = it.animatedValue as Int
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index cf9e141..cf3538c 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -23,6 +23,10 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.Dialog;
@@ -2174,24 +2178,7 @@
             mShowing = true;
             mHadTopUi = mNotificationShadeWindowController.getForceHasTopUi();
             mNotificationShadeWindowController.setForceHasTopUi(true);
-            mBackgroundDrawable.setAlpha(0);
-            mContainer.setTranslationX(mGlobalActionsLayout.getAnimationOffsetX());
-            mContainer.setTranslationY(mGlobalActionsLayout.getAnimationOffsetY());
-            mContainer.setAlpha(0);
-            mContainer.animate()
-                    .alpha(1)
-                    .translationX(0)
-                    .translationY(0)
-                    .setDuration(450)
-                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                    .setUpdateListener(animation -> {
-                        float animatedValue = animation.getAnimatedFraction();
-                        int alpha = (int) (animatedValue * mScrimAlpha * 255);
-                        mBackgroundDrawable.setAlpha(alpha);
-                        mDepthController.updateGlobalDialogVisibility(animatedValue,
-                                mGlobalActionsLayout);
-                    })
-                    .start();
+
             ViewGroup root = (ViewGroup) mGlobalActionsLayout.getRootView();
             root.setOnApplyWindowInsetsListener((v, windowInsets) -> {
                 if (mUseControlsLayout) {
@@ -2205,29 +2192,66 @@
             if (mControlsUiController != null) {
                 mControlsUiController.show(mControlsView, this::dismissForControlsActivity);
             }
+
+            mBackgroundDrawable.setAlpha(0);
+            float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
+            ObjectAnimator alphaAnimator =
+                    ObjectAnimator.ofFloat(mContainer, "transitionAlpha", 0f, 1f);
+            alphaAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+            alphaAnimator.setDuration(183);
+            alphaAnimator.addUpdateListener((animation) -> {
+                float animatedValue = animation.getAnimatedFraction();
+                int alpha = (int) (animatedValue * mScrimAlpha * 255);
+                mBackgroundDrawable.setAlpha(alpha);
+                mDepthController.updateGlobalDialogVisibility(animatedValue,
+                        mGlobalActionsLayout);
+            });
+
+            ObjectAnimator xAnimator =
+                    ObjectAnimator.ofFloat(mContainer, "translationX", xOffset, 0f);
+            alphaAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+            alphaAnimator.setDuration(350);
+
+            AnimatorSet animatorSet = new AnimatorSet();
+            animatorSet.playTogether(alphaAnimator, xAnimator);
+            animatorSet.start();
         }
 
         @Override
         public void dismiss() {
             dismissWithAnimation(() -> {
                 mContainer.setTranslationX(0);
-                mContainer.setTranslationY(0);
-                mContainer.setAlpha(1);
-                mContainer.animate()
-                        .alpha(0)
-                        .translationX(mGlobalActionsLayout.getAnimationOffsetX())
-                        .translationY(mGlobalActionsLayout.getAnimationOffsetY())
-                        .setDuration(450)
-                        .withEndAction(this::completeDismiss)
-                        .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                        .setUpdateListener(animation -> {
-                            float animatedValue = 1f - animation.getAnimatedFraction();
-                            int alpha = (int) (animatedValue * mScrimAlpha * 255);
-                            mBackgroundDrawable.setAlpha(alpha);
-                            mDepthController.updateGlobalDialogVisibility(animatedValue,
-                                    mGlobalActionsLayout);
-                        })
-                        .start();
+                ObjectAnimator alphaAnimator =
+                        ObjectAnimator.ofFloat(mContainer, "transitionAlpha", 1f, 0f);
+                alphaAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+                alphaAnimator.setDuration(233);
+                alphaAnimator.addUpdateListener((animation) -> {
+                    float animatedValue = 1f - animation.getAnimatedFraction();
+                    int alpha = (int) (animatedValue * mScrimAlpha * 255);
+                    mBackgroundDrawable.setAlpha(alpha);
+                    mDepthController.updateGlobalDialogVisibility(animatedValue,
+                            mGlobalActionsLayout);
+                });
+
+                float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
+                ObjectAnimator xAnimator =
+                        ObjectAnimator.ofFloat(mContainer, "translationX", 0f, xOffset);
+                alphaAnimator.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+                alphaAnimator.setDuration(350);
+
+                AnimatorSet animatorSet = new AnimatorSet();
+                animatorSet.playTogether(alphaAnimator, xAnimator);
+                animatorSet.addListener(new AnimatorListenerAdapter() {
+                    public void onAnimationEnd(Animator animation) {
+                        completeDismiss();
+                    }
+                });
+
+                animatorSet.start();
+
+                // close first, as popup windows will not fade during the animation
+                dismissOverflow(false);
+                if (mControlsUiController != null) mControlsUiController.closeDialogs(false);
             });
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java
index 2f32d97..c7612d4 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java
@@ -90,11 +90,11 @@
 
     @Override
     public float getAnimationOffsetX() {
-        return 0;
+        return getAnimationDistance();
     }
 
     @Override
     public float getAnimationOffsetY() {
-        return -getAnimationDistance();
+        return 0f;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
index 1425100..f72a74b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt
@@ -19,6 +19,7 @@
 import android.media.MediaMetadata
 import android.media.session.MediaController
 import android.media.session.PlaybackState
+import android.os.SystemClock
 import android.view.MotionEvent
 import android.view.View
 import android.widget.SeekBar
@@ -31,6 +32,38 @@
 
 private const val POSITION_UPDATE_INTERVAL_MILLIS = 100L
 
+private fun PlaybackState.isInMotion(): Boolean {
+    return this.state == PlaybackState.STATE_PLAYING ||
+            this.state == PlaybackState.STATE_FAST_FORWARDING ||
+            this.state == PlaybackState.STATE_REWINDING
+}
+
+/**
+ * Gets the playback position while accounting for the time since the [PlaybackState] was last
+ * retrieved.
+ *
+ * This method closely follows the implementation of
+ * [MediaSessionRecord#getStateWithUpdatedPosition].
+ */
+private fun PlaybackState.computePosition(duration: Long): Long {
+    var currentPosition = this.position
+    if (this.isInMotion()) {
+        val updateTime = this.getLastPositionUpdateTime()
+        val currentTime = SystemClock.elapsedRealtime()
+        if (updateTime > 0) {
+            var position = (this.playbackSpeed * (currentTime - updateTime)).toLong() +
+                    this.getPosition()
+            if (duration >= 0 && position > duration) {
+                position = duration.toLong()
+            } else if (position < 0) {
+                position = 0
+            }
+            currentPosition = position
+        }
+    }
+    return currentPosition
+}
+
 /** ViewModel for seek bar in QS media player. */
 class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
 
@@ -98,7 +131,8 @@
 
     @AnyThread
     private fun checkPlaybackPosition(): Runnable = bgExecutor.executeDelayed({
-        val currentPosition = controller?.playbackState?.position?.toInt()
+        val duration = _data?.duration ?: -1
+        val currentPosition = playbackState?.computePosition(duration.toLong())?.toInt()
         if (currentPosition != null && _data.elapsedTime != currentPosition) {
             _data = _data.copy(elapsedTime = currentPosition)
         }
@@ -109,13 +143,7 @@
 
     @WorkerThread
     private fun shouldPollPlaybackPosition(): Boolean {
-        val state = playbackState?.state
-        val moving = if (state == null) false else
-                state == PlaybackState.STATE_PLAYING ||
-                state == PlaybackState.STATE_BUFFERING ||
-                state == PlaybackState.STATE_FAST_FORWARDING ||
-                state == PlaybackState.STATE_REWINDING
-        return moving && listening
+        return listening && playbackState?.isInMotion() ?: false
     }
 
     /** Gets a listener to attach to the seek bar to handle seeking. */
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 7295f327..8d797f1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -190,6 +190,7 @@
     private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
     private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
             mSurfaceControlTransactionFactory;
+    private PictureInPictureParams mPictureInPictureParams;
 
     public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler,
             @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
@@ -213,6 +214,10 @@
         return new Rect(mLastReportedBounds);
     }
 
+    public boolean isInPip() {
+        return mInPip;
+    }
+
     /**
      * Registers {@link PipTransitionCallback} to receive transition callbacks.
      */
@@ -257,8 +262,9 @@
     @Override
     public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) {
         Objects.requireNonNull(info, "Requires RunningTaskInfo");
+        mPictureInPictureParams = info.pictureInPictureParams;
         final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
-                info.topActivity, getAspectRatioOrDefault(info.pictureInPictureParams),
+                info.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
                 null /* bounds */, getMinimalSize(info.topActivityInfo));
         Objects.requireNonNull(destinationBounds, "Missing destination bounds");
         mTaskInfo = info;
@@ -304,6 +310,7 @@
             Log.wtf(TAG, "Unrecognized token: " + token);
             return;
         }
+        mPictureInPictureParams = null;
         mInPip = false;
     }
 
@@ -311,7 +318,7 @@
     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
         Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
         final PictureInPictureParams newParams = info.pictureInPictureParams;
-        if (!shouldUpdateDestinationBounds(newParams)) {
+        if (!applyPictureInPictureParams(newParams)) {
             Log.d(TAG, "Ignored onTaskInfoChanged with PiP param: " + newParams);
             return;
         }
@@ -359,7 +366,7 @@
         }
 
         final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds(
-                mTaskInfo.topActivity, getAspectRatioOrDefault(mTaskInfo.pictureInPictureParams),
+                mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
                 null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
         if (newDestinationBounds.equals(currentDestinationBounds)) return;
         if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
@@ -373,12 +380,14 @@
      * @return {@code true} if the aspect ratio is changed since no other parameters within
      * {@link PictureInPictureParams} would affect the bounds.
      */
-    private boolean shouldUpdateDestinationBounds(PictureInPictureParams params) {
-        if (params == null || mTaskInfo.pictureInPictureParams == null) {
-            return params != mTaskInfo.pictureInPictureParams;
+    private boolean applyPictureInPictureParams(@NonNull PictureInPictureParams params) {
+        final boolean changed = (mPictureInPictureParams == null) ? true : !Objects.equals(
+                mPictureInPictureParams.getAspectRatioRational(), params.getAspectRatioRational());
+        if (changed) {
+            mPictureInPictureParams = params;
+            mPipBoundsHandler.onAspectRatioChanged(params.getAspectRatio());
         }
-        return !Objects.equals(mTaskInfo.pictureInPictureParams.getAspectRatioRational(),
-                params.getAspectRatioRational());
+        return changed;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 00f693d..b1e4d67 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -168,7 +168,10 @@
     void synchronizePinnedStackBounds() {
         cancelAnimations();
         mBounds.set(mPipTaskOrganizer.getLastReportedBounds());
-        mFloatingContentCoordinator.onContentMoved(this);
+
+        if (mPipTaskOrganizer.isInPip()) {
+            mFloatingContentCoordinator.onContentMoved(this);
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 6748e3f..0e8c1b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -36,6 +36,7 @@
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.ISystemGestureExclusionListener;
@@ -232,13 +233,14 @@
         mIsBackGestureAllowed =
                 !mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible();
 
+        final DisplayMetrics dm = res.getDisplayMetrics();
         final float defaultGestureHeight = res.getDimension(
-                com.android.internal.R.dimen.navigation_bar_gesture_height);
+                com.android.internal.R.dimen.navigation_bar_gesture_height) / dm.density;
         final float gestureHeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.BACK_GESTURE_BOTTOM_HEIGHT,
                 defaultGestureHeight);
-        mBottomGestureHeight = TypedValue.applyDimension(
-                TypedValue.COMPLEX_UNIT_DIP, gestureHeight, res.getDisplayMetrics());
+        mBottomGestureHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, gestureHeight,
+                dm);
 
         // Reduce the default touch slop to ensure that we can intercept the gesture
         // before the app starts to react to it.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
index 28a3d6a..cde575d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
@@ -362,28 +362,21 @@
 
     @Test
     fun taskUpdatesProgress() {
-        // GIVEN that the PlaybackState contins the current position
-        val position = 200L
+        // GIVEN that the PlaybackState contins the initial position
+        val initialPosition = 0L
         val state = PlaybackState.Builder().run {
-            setState(PlaybackState.STATE_PLAYING, position, 1f)
+            setState(PlaybackState.STATE_PLAYING, initialPosition, 1f)
             build()
         }
         whenever(mockController.getPlaybackState()).thenReturn(state)
         viewModel.updateController(mockController, Color.RED)
-        // AND the playback state advances
-        val nextPosition = 300L
-        val nextState = PlaybackState.Builder().run {
-            setState(PlaybackState.STATE_PLAYING, nextPosition, 1f)
-            build()
-        }
-        whenever(mockController.getPlaybackState()).thenReturn(nextState)
         // WHEN the task runs
         with(fakeExecutor) {
             advanceClockToNext()
             runAllReady()
         }
-        // THEN elapsed time is captured
-        assertThat(viewModel.progress.value!!.elapsedTime).isEqualTo(nextPosition.toInt())
+        // THEN elapsed time has increased
+        assertThat(viewModel.progress.value!!.elapsedTime).isGreaterThan(initialPosition.toInt())
     }
 
     @Test
diff --git a/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml
index 12d6c2c..d074f15 100644
--- a/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml
+++ b/packages/Tethering/res/values-mcc310-mnc004-ne/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="no_upstream_notification_title" msgid="5030042590486713460">"Tethering has no internet"</string>
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"टेदरिङमार्फत इन्टरनेट कनेक्सन प्राप्त हुन सकेन"</string>
     <string name="no_upstream_notification_message" msgid="3843613362272973447">"यन्त्रहरू कनेक्ट गर्न सकिएन"</string>
     <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"टेदरिङ निष्क्रिय पार्नुहोस्"</string>
     <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"हटस्पट वा टेदरिङ सक्रिय छ"</string>
diff --git a/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml b/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml
index 05b90692..528a1e5 100644
--- a/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml
+++ b/packages/Tethering/res/values-mcc310-mnc004-zh-rTW/strings.xml
@@ -16,9 +16,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="no_upstream_notification_title" msgid="5030042590486713460">"無法透過數據連線連上網際網路"</string>
+    <string name="no_upstream_notification_title" msgid="5030042590486713460">"無法透過網路共用連上網際網路"</string>
     <string name="no_upstream_notification_message" msgid="3843613362272973447">"裝置無法連線"</string>
-    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"關閉數據連線"</string>
-    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"無線基地台或數據連線已開啟"</string>
+    <string name="no_upstream_notification_disable_button" msgid="6385491461813507624">"關閉網路共用"</string>
+    <string name="upstream_roaming_notification_title" msgid="3015912166812283303">"無線基地台或網路共用已開啟"</string>
     <string name="upstream_roaming_notification_message" msgid="6724434706748439902">"使用漫遊服務可能須支付額外費用"</string>
 </resources>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml
index 0a0aa21..1503244 100644
--- a/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml
+++ b/packages/Tethering/res/values-mcc311-mnc480-ne/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="no_upstream_notification_title" msgid="611650570559011140">"Tethering has no internet"</string>
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"टेदरिङमार्फत इन्टरनेट कनेक्सन प्राप्त हुन सकेन"</string>
     <string name="no_upstream_notification_message" msgid="6508394877641864863">"यन्त्रहरू कनेक्ट गर्न सकिएन"</string>
     <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"टेदरिङ निष्क्रिय पार्नुहोस्"</string>
     <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"हटस्पट वा टेदरिङ सक्रिय छ"</string>
diff --git a/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml b/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml
index ea01b94..cd653df 100644
--- a/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml
+++ b/packages/Tethering/res/values-mcc311-mnc480-zh-rTW/strings.xml
@@ -16,9 +16,9 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="no_upstream_notification_title" msgid="611650570559011140">"無法透過數據連線連上網際網路"</string>
+    <string name="no_upstream_notification_title" msgid="611650570559011140">"無法透過網路共用連上網際網路"</string>
     <string name="no_upstream_notification_message" msgid="6508394877641864863">"裝置無法連線"</string>
-    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"關閉數據連線"</string>
-    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"無線基地台或數據連線已開啟"</string>
+    <string name="no_upstream_notification_disable_button" msgid="7609346639290990508">"關閉網路共用"</string>
+    <string name="upstream_roaming_notification_title" msgid="6032901176124830787">"無線基地台或網路共用已開啟"</string>
     <string name="upstream_roaming_notification_message" msgid="7599056263326217523">"使用漫遊服務可能須支付額外費用"</string>
 </resources>
diff --git a/packages/Tethering/res/values-zh-rTW/strings.xml b/packages/Tethering/res/values-zh-rTW/strings.xml
index 9d738a7..50a50bf 100644
--- a/packages/Tethering/res/values-zh-rTW/strings.xml
+++ b/packages/Tethering/res/values-zh-rTW/strings.xml
@@ -16,11 +16,11 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="tethered_notification_title" msgid="6426563586025792944">"數據連線或無線基地台已啟用"</string>
+    <string name="tethered_notification_title" msgid="6426563586025792944">"網路共用或無線基地台已啟用"</string>
     <string name="tethered_notification_message" msgid="64800879503420696">"輕觸即可進行設定。"</string>
-    <string name="disable_tether_notification_title" msgid="3004509127903564191">"數據連線已停用"</string>
+    <string name="disable_tether_notification_title" msgid="3004509127903564191">"網路共用已停用"</string>
     <string name="disable_tether_notification_message" msgid="6717523799293901476">"詳情請洽你的管理員"</string>
-    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"無線基地台與數據連線狀態"</string>
+    <string name="notification_channel_tethering_status" msgid="2663463891530932727">"無線基地台與網路共用狀態"</string>
     <string name="no_upstream_notification_title" msgid="1204601824631788482"></string>
     <string name="no_upstream_notification_message" msgid="8586582938243032621"></string>
     <string name="no_upstream_notification_disable_button" msgid="8800919436924640822"></string>
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 35089d6..20d1b98 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -325,7 +325,7 @@
         @GuardedBy("mLock")
         private CountDownLatch mCountDownLatch = new CountDownLatch(0);
 
-        @Nullable Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(
+        @Nullable Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(ViewState viewState,
                 boolean isInlineRequest) {
             mCountDownLatch = new CountDownLatch(isInlineRequest ? 2 : 1);
             mPendingFillRequest = null;
@@ -338,6 +338,7 @@
                     mPendingInlineSuggestionsRequest = inlineSuggestionsRequest;
                     mCountDownLatch.countDown();
                     maybeRequestFillLocked();
+                    viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
                 }
             } : null;
         }
@@ -716,18 +717,23 @@
                 mService.getRemoteInlineSuggestionRenderServiceLocked();
         if (isInlineSuggestionsEnabledByAutofillProviderLocked() && remoteRenderService != null) {
             Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer =
-                    mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ true);
+                    mAssistReceiver.newAutofillRequestLocked(viewState,
+                            /*isInlineRequest=*/ true);
             if (inlineSuggestionsRequestConsumer != null) {
                 final AutofillId focusedId = mCurrentViewId;
                 remoteRenderService.getInlineSuggestionsRendererInfo(
                         new RemoteCallback((extras) -> {
-                            mInlineSessionController.onCreateInlineSuggestionsRequestLocked(
-                                    focusedId, inlineSuggestionsRequestConsumer, extras);
-                        }
-                ));
+                            synchronized (mLock) {
+                                mInlineSessionController.onCreateInlineSuggestionsRequestLocked(
+                                        focusedId, inlineSuggestionsRequestConsumer, extras);
+                            }
+                        }, mHandler)
+                );
+                viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST);
             }
         } else {
-            mAssistReceiver.newAutofillRequestLocked(/*isInlineRequest=*/ false);
+            mAssistReceiver.newAutofillRequestLocked(viewState,
+                    /*isInlineRequest=*/ false);
         }
 
         // Now request the assist structure data.
@@ -2368,7 +2374,9 @@
      */
     @GuardedBy("mLock")
     private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id) {
-        if (mResponses == null) {
+        final ViewState currentView = mViewStates.get(id);
+        if (mResponses == null && currentView != null
+                && (currentView.getState() & ViewState.STATE_PENDING_CREATE_INLINE_REQUEST) == 0) {
             return true;
         }
 
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 9114576..adb1e3e 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -80,6 +80,8 @@
     public static final int STATE_CHAR_REMOVED = 0x4000;
     /** Showing inline suggestions is not allowed for this View. */
     public static final int STATE_INLINE_DISABLED = 0x8000;
+    /** The View is waiting for an inline suggestions request from IME.*/
+    public static final int STATE_PENDING_CREATE_INLINE_REQUEST = 0x10000;
 
     public final AutofillId id;
 
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index 3dbf2c6..16d83ec 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -116,6 +116,7 @@
                 viewMessage = res.getString(R.string.user_switching_message, mNewUser.name);
             }
         }
+        view.setAccessibilityPaneTitle(viewMessage);
         ((TextView) view.findViewById(R.id.message)).setText(viewMessage);
         setView(view);
     }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 4a1afb2..041bedc 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1819,7 +1819,8 @@
         intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
                 com.android.internal.R.string.sync_binding_label);
         intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivityAsUser(context, 0,
-                new Intent(Settings.ACTION_SYNC_SETTINGS), 0, null, UserHandle.of(userId)));
+                new Intent(Settings.ACTION_SYNC_SETTINGS), PendingIntent.FLAG_IMMUTABLE, null,
+                UserHandle.of(userId)));
 
         return intent;
     }
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 612fd39..c54ebf8 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -152,6 +152,47 @@
         return votes;
     }
 
+    private static final class VoteSummary {
+        public float minRefreshRate;
+        public float maxRefreshRate;
+        public int width;
+        public int height;
+
+        VoteSummary() {
+            reset();
+        }
+
+        public void reset() {
+            minRefreshRate = 0f;
+            maxRefreshRate = Float.POSITIVE_INFINITY;
+            width = Vote.INVALID_SIZE;
+            height = Vote.INVALID_SIZE;
+        }
+    }
+
+    // VoteSummary is returned as an output param to cut down a bit on the number of temporary
+    // objects.
+    private void summarizeVotes(
+            SparseArray<Vote> votes, int lowestConsideredPriority, /*out*/ VoteSummary summary) {
+        summary.reset();
+        for (int priority = Vote.MAX_PRIORITY; priority >= lowestConsideredPriority; priority--) {
+            Vote vote = votes.get(priority);
+            if (vote == null) {
+                continue;
+            }
+            // For refresh rates, just use the tightest bounds of all the votes
+            summary.minRefreshRate = Math.max(summary.minRefreshRate, vote.refreshRateRange.min);
+            summary.maxRefreshRate = Math.min(summary.maxRefreshRate, vote.refreshRateRange.max);
+            // For display size, use only the first vote we come across (i.e. the highest
+            // priority vote that includes the width / height).
+            if (summary.height == Vote.INVALID_SIZE && summary.width == Vote.INVALID_SIZE
+                    && vote.height > 0 && vote.width > 0) {
+                summary.width = vote.width;
+                summary.height = vote.height;
+            }
+        }
+    }
+
     /**
      * Calculates the refresh rate ranges and display modes that the system is allowed to freely
      * switch between based on global and display-specific constraints.
@@ -174,52 +215,31 @@
             }
 
             int[] availableModes = new int[]{defaultMode.getModeId()};
-            float minRefreshRate = 0f;
-            float maxRefreshRate = Float.POSITIVE_INFINITY;
+            VoteSummary primarySummary = new VoteSummary();
             int lowestConsideredPriority = Vote.MIN_PRIORITY;
             while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
-                minRefreshRate = 0f;
-                maxRefreshRate = Float.POSITIVE_INFINITY;
-                int height = Vote.INVALID_SIZE;
-                int width = Vote.INVALID_SIZE;
-
-                for (int priority = Vote.MAX_PRIORITY;
-                        priority >= lowestConsideredPriority; priority--) {
-                    Vote vote = votes.get(priority);
-                    if (vote == null) {
-                        continue;
-                    }
-                    // For refresh rates, just use the tightest bounds of all the votes
-                    minRefreshRate = Math.max(minRefreshRate, vote.refreshRateRange.min);
-                    maxRefreshRate = Math.min(maxRefreshRate, vote.refreshRateRange.max);
-                    // For display size, use only the first vote we come across (i.e. the highest
-                    // priority vote that includes the width / height).
-                    if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE
-                            && vote.height > 0 && vote.width > 0) {
-                        width = vote.width;
-                        height = vote.height;
-                    }
-                }
+                summarizeVotes(votes, lowestConsideredPriority, primarySummary);
 
                 // If we don't have anything specifying the width / height of the display, just use
                 // the default width and height. We don't want these switching out from underneath
                 // us since it's a pretty disruptive behavior.
-                if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) {
-                    width = defaultMode.getPhysicalWidth();
-                    height = defaultMode.getPhysicalHeight();
+                if (primarySummary.height == Vote.INVALID_SIZE
+                        || primarySummary.width == Vote.INVALID_SIZE) {
+                    primarySummary.width = defaultMode.getPhysicalWidth();
+                    primarySummary.height = defaultMode.getPhysicalHeight();
                 }
 
-                availableModes = filterModes(modes, width, height, minRefreshRate, maxRefreshRate);
+                availableModes = filterModes(modes, primarySummary);
                 if (availableModes.length > 0) {
                     if (DEBUG) {
                         Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
                                 + " with lowest priority considered "
                                 + Vote.priorityToString(lowestConsideredPriority)
                                 + " and constraints: "
-                                + "width=" + width
-                                + ", height=" + height
-                                + ", minRefreshRate=" + minRefreshRate
-                                + ", maxRefreshRate=" + maxRefreshRate);
+                                + "width=" + primarySummary.width
+                                + ", height=" + primarySummary.height
+                                + ", minRefreshRate=" + primarySummary.minRefreshRate
+                                + ", maxRefreshRate=" + primarySummary.maxRefreshRate);
                     }
                     break;
                 }
@@ -228,10 +248,10 @@
                     Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
                             + Vote.priorityToString(lowestConsideredPriority)
                             + " and with the following constraints: "
-                            + "width=" + width
-                            + ", height=" + height
-                            + ", minRefreshRate=" + minRefreshRate
-                            + ", maxRefreshRate=" + maxRefreshRate);
+                            + "width=" + primarySummary.width
+                            + ", height=" + primarySummary.height
+                            + ", minRefreshRate=" + primarySummary.minRefreshRate
+                            + ", maxRefreshRate=" + primarySummary.maxRefreshRate);
                 }
 
                 // If we haven't found anything with the current set of votes, drop the
@@ -239,6 +259,20 @@
                 lowestConsideredPriority++;
             }
 
+            VoteSummary appRequestSummary = new VoteSummary();
+            summarizeVotes(
+                    votes, Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, appRequestSummary);
+            appRequestSummary.minRefreshRate =
+                    Math.min(appRequestSummary.minRefreshRate, primarySummary.minRefreshRate);
+            appRequestSummary.maxRefreshRate =
+                    Math.max(appRequestSummary.maxRefreshRate, primarySummary.maxRefreshRate);
+            if (DEBUG) {
+                Slog.i(TAG,
+                        String.format("App request range: [%.0f %.0f]",
+                                appRequestSummary.minRefreshRate,
+                                appRequestSummary.maxRefreshRate));
+            }
+
             int baseModeId = defaultMode.getModeId();
             if (availableModes.length > 0) {
                 baseModeId = availableModes[0];
@@ -246,20 +280,23 @@
             // filterModes function is going to filter the modes based on the voting system. If
             // the application requests a given mode with preferredModeId function, it will be
             // stored as baseModeId.
-            return new DesiredDisplayModeSpecs(
-                    baseModeId, new RefreshRateRange(minRefreshRate, maxRefreshRate));
+            return new DesiredDisplayModeSpecs(baseModeId,
+                    new RefreshRateRange(
+                            primarySummary.minRefreshRate, primarySummary.maxRefreshRate),
+                    new RefreshRateRange(
+                            appRequestSummary.minRefreshRate, appRequestSummary.maxRefreshRate));
         }
     }
 
-    private int[] filterModes(Display.Mode[] supportedModes,
-            int width, int height, float minRefreshRate, float maxRefreshRate) {
+    private int[] filterModes(Display.Mode[] supportedModes, VoteSummary summary) {
         ArrayList<Display.Mode> availableModes = new ArrayList<>();
         for (Display.Mode mode : supportedModes) {
-            if (mode.getPhysicalWidth() != width || mode.getPhysicalHeight() != height) {
+            if (mode.getPhysicalWidth() != summary.width
+                    || mode.getPhysicalHeight() != summary.height) {
                 if (DEBUG) {
                     Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size"
-                            + ": desiredWidth=" + width
-                            + ": desiredHeight=" + height
+                            + ": desiredWidth=" + summary.width
+                            + ": desiredHeight=" + summary.height
                             + ": actualWidth=" + mode.getPhysicalWidth()
                             + ": actualHeight=" + mode.getPhysicalHeight());
                 }
@@ -269,13 +306,13 @@
             // Some refresh rates are calculated based on frame timings, so they aren't *exactly*
             // equal to expected refresh rate. Given that, we apply a bit of tolerance to this
             // comparison.
-            if (refreshRate < (minRefreshRate - FLOAT_TOLERANCE)
-                    || refreshRate > (maxRefreshRate + FLOAT_TOLERANCE)) {
+            if (refreshRate < (summary.minRefreshRate - FLOAT_TOLERANCE)
+                    || refreshRate > (summary.maxRefreshRate + FLOAT_TOLERANCE)) {
                 if (DEBUG) {
                     Slog.w(TAG, "Discarding mode " + mode.getModeId()
                             + ", outside refresh rate bounds"
-                            + ": minRefreshRate=" + minRefreshRate
-                            + ", maxRefreshRate=" + maxRefreshRate
+                            + ": minRefreshRate=" + summary.minRefreshRate
+                            + ", maxRefreshRate=" + summary.maxRefreshRate
                             + ", modeRefreshRate=" + refreshRate);
                 }
                 continue;
@@ -535,7 +572,7 @@
 
     /**
      * Information about the desired display mode to be set by the system. Includes the base
-     * mode ID and refresh rate range.
+     * mode ID and the primary and app request refresh rate ranges.
      *
      * We have this class in addition to SurfaceControl.DesiredDisplayConfigSpecs to make clear the
      * distinction between the config ID / physical index that
@@ -548,17 +585,28 @@
          */
         public int baseModeId;
         /**
-         * The refresh rate range.
+         * The primary refresh rate range.
          */
-        public final RefreshRateRange refreshRateRange;
+        public final RefreshRateRange primaryRefreshRateRange;
+        /**
+         * The app request refresh rate range. Lower priority considerations won't be included in
+         * this range, allowing surface flinger to consider additional refresh rates for apps that
+         * call setFrameRate(). This range will be greater than or equal to the primary refresh rate
+         * range, never smaller.
+         */
+        public final RefreshRateRange appRequestRefreshRateRange;
 
         public DesiredDisplayModeSpecs() {
-            refreshRateRange = new RefreshRateRange();
+            primaryRefreshRateRange = new RefreshRateRange();
+            appRequestRefreshRateRange = new RefreshRateRange();
         }
 
-        public DesiredDisplayModeSpecs(int baseModeId, @NonNull RefreshRateRange refreshRateRange) {
+        public DesiredDisplayModeSpecs(int baseModeId,
+                @NonNull RefreshRateRange primaryRefreshRateRange,
+                @NonNull RefreshRateRange appRequestRefreshRateRange) {
             this.baseModeId = baseModeId;
-            this.refreshRateRange = refreshRateRange;
+            this.primaryRefreshRateRange = primaryRefreshRateRange;
+            this.appRequestRefreshRateRange = appRequestRefreshRateRange;
         }
 
         /**
@@ -566,8 +614,10 @@
          */
         @Override
         public String toString() {
-            return String.format("baseModeId=%d min=%.0f max=%.0f", baseModeId,
-                    refreshRateRange.min, refreshRateRange.max);
+            return String.format("baseModeId=%d primaryRefreshRateRange=[%.0f %.0f]"
+                            + " appRequestRefreshRateRange=[%.0f %.0f]",
+                    baseModeId, primaryRefreshRateRange.min, primaryRefreshRateRange.max,
+                    appRequestRefreshRateRange.min, appRequestRefreshRateRange.max);
         }
         /**
          * Checks whether the two objects have the same values.
@@ -587,7 +637,11 @@
             if (baseModeId != desiredDisplayModeSpecs.baseModeId) {
                 return false;
             }
-            if (!refreshRateRange.equals(desiredDisplayModeSpecs.refreshRateRange)) {
+            if (!primaryRefreshRateRange.equals(desiredDisplayModeSpecs.primaryRefreshRateRange)) {
+                return false;
+            }
+            if (!appRequestRefreshRateRange.equals(
+                        desiredDisplayModeSpecs.appRequestRefreshRateRange)) {
                 return false;
             }
             return true;
@@ -595,7 +649,7 @@
 
         @Override
         public int hashCode() {
-            return Objects.hash(baseModeId, refreshRateRange);
+            return Objects.hash(baseModeId, primaryRefreshRateRange, appRequestRefreshRateRange);
         }
 
         /**
@@ -603,8 +657,10 @@
          */
         public void copyFrom(DesiredDisplayModeSpecs other) {
             baseModeId = other.baseModeId;
-            refreshRateRange.min = other.refreshRateRange.min;
-            refreshRateRange.max = other.refreshRateRange.max;
+            primaryRefreshRateRange.min = other.primaryRefreshRateRange.min;
+            primaryRefreshRateRange.max = other.primaryRefreshRateRange.max;
+            appRequestRefreshRateRange.min = other.appRequestRefreshRateRange.min;
+            appRequestRefreshRateRange.max = other.appRequestRefreshRateRange.max;
         }
     }
 
@@ -637,12 +693,17 @@
         // LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
         public static final int PRIORITY_LOW_POWER_MODE = 5;
 
-        // Whenever a new priority is added, remember to update MIN_PRIORITY and/or MAX_PRIORITY as
-        // appropriate, as well as priorityToString.
+        // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
+        // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
 
         public static final int MIN_PRIORITY = PRIORITY_LOW_BRIGHTNESS;
         public static final int MAX_PRIORITY = PRIORITY_LOW_POWER_MODE;
 
+        // The cutoff for the app request refresh rate range. Votes with priorities lower than this
+        // value will not be considered when constructing the app request refresh rate range.
+        public static final int APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF =
+                PRIORITY_APP_REQUEST_REFRESH_RATE;
+
         /**
          * A value signifying an invalid width or height in a vote.
          */
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 6132467..4f5a02a 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -310,9 +310,14 @@
                 // list of available modes will take care of updating display config specs.
                 if (activeBaseMode != NO_DISPLAY_MODE_ID) {
                     if (mDisplayModeSpecs.baseModeId != activeBaseMode
-                            || mDisplayModeSpecs.refreshRateRange.min != configSpecs.minRefreshRate
-                            || mDisplayModeSpecs.refreshRateRange.max
-                                    != configSpecs.maxRefreshRate) {
+                            || mDisplayModeSpecs.primaryRefreshRateRange.min
+                                    != configSpecs.primaryRefreshRateMin
+                            || mDisplayModeSpecs.primaryRefreshRateRange.max
+                                    != configSpecs.primaryRefreshRateMax
+                            || mDisplayModeSpecs.appRequestRefreshRateRange.min
+                                    != configSpecs.appRequestRefreshRateMin
+                            || mDisplayModeSpecs.appRequestRefreshRateRange.max
+                                    != configSpecs.appRequestRefreshRateMax) {
                         mDisplayModeSpecsInvalid = true;
                         sendTraversalRequestLocked();
                     }
@@ -799,8 +804,10 @@
                         LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this,
                         getDisplayTokenLocked(),
                         new SurfaceControl.DesiredDisplayConfigSpecs(baseConfigId,
-                                mDisplayModeSpecs.refreshRateRange.min,
-                                mDisplayModeSpecs.refreshRateRange.max)));
+                                mDisplayModeSpecs.primaryRefreshRateRange.min,
+                                mDisplayModeSpecs.primaryRefreshRateRange.max,
+                                mDisplayModeSpecs.appRequestRefreshRateRange.min,
+                                mDisplayModeSpecs.appRequestRefreshRateRange.max)));
             }
         }
 
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index fad0a7d..8bb7c0f 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -1312,7 +1312,7 @@
         intent.putExtra(Intent.EXTRA_CLIENT_LABEL, mConfig.clientLabel);
 
         final PendingIntent pendingIntent = PendingIntent.getActivity(
-            mContext, 0, new Intent(mConfig.settingsAction), 0);
+            mContext, 0, new Intent(mConfig.settingsAction), PendingIntent.FLAG_IMMUTABLE);
         intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);
 
         ApplicationInfo appInfo = null;
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 4872b66..acce699 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -32,6 +32,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import android.content.pm.parsing.ParsingPackageUtils;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.RemoteException;
@@ -49,6 +50,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
 import com.android.server.utils.TimingsTraceAndSlog;
 
 import com.google.android.collect.Lists;
@@ -480,7 +482,7 @@
             if (allPkgs.length == 0) {
                 return;
             }
-            int flags = PackageManager.GET_META_DATA
+            final int flags = PackageManager.GET_META_DATA
                     | PackageManager.GET_SIGNING_CERTIFICATES
                     | PackageManager.GET_SIGNATURES;
             ArrayMap<File, ApexInfo> parsingApexInfo = new ArrayMap<>();
@@ -489,7 +491,7 @@
 
             for (ApexInfo ai : allPkgs) {
                 File apexFile = new File(ai.modulePath);
-                parallelPackageParser.submit(apexFile, flags);
+                parallelPackageParser.submit(apexFile, 0);
                 parsingApexInfo.put(apexFile, ai);
             }
 
@@ -502,8 +504,18 @@
                 ApexInfo ai = parsingApexInfo.get(parseResult.scanFile);
 
                 if (throwable == null) {
+                    // Unfortunately, ParallelPackageParser won't collect certificates for us. We
+                    // need to manually collect them here.
+                    ParsedPackage pp = parseResult.parsedPackage;
+                    try {
+                        pp.setSigningDetails(
+                                ParsingPackageUtils.collectCertificates(pp, false));
+                    } catch (PackageParserException e) {
+                        throw new IllegalStateException(
+                                "Unable to collect certificates for " + ai.modulePath, e);
+                    }
                     final PackageInfo packageInfo = PackageInfoWithoutStateUtils.generate(
-                            parseResult.parsedPackage, ai, flags);
+                            pp, ai, flags);
                     if (packageInfo == null) {
                         throw new IllegalStateException("Unable to generate package info: "
                                 + ai.modulePath);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 33a1116..3df044f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1847,13 +1847,44 @@
         }
     }
 
-    private void logDataLoaderInstallationSession(int returnCode, String extraMessage) {
+    private void logDataLoaderInstallationSession(int returnCode) {
+        // Skip logging the side-loaded app installations, as those are private and aren't reported
+        // anywhere; app stores already have a record of the installation and that's why reporting
+        // it here is fine
+        final String packageNameToLog =
+                (params.installFlags & PackageManager.INSTALL_FROM_ADB) == 0 ? mPackageName : "";
         final long currentTimestamp = System.currentTimeMillis();
         FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLER_V2_REPORTED,
                 isIncrementalInstallation(),
-                mPackageName,
+                packageNameToLog,
                 currentTimestamp - createdMillis,
-                returnCode);
+                returnCode,
+                getApksSize());
+    }
+
+    private long getApksSize() {
+        final PackageSetting ps = mPm.getPackageSetting(mPackageName);
+        if (ps == null) {
+            return 0;
+        }
+        final File apkDirOrPath = ps.codePath;
+        if (apkDirOrPath == null) {
+            return 0;
+        }
+        if (apkDirOrPath.isFile() && apkDirOrPath.getName().toLowerCase().endsWith(".apk")) {
+            return apkDirOrPath.length();
+        }
+        if (!apkDirOrPath.isDirectory()) {
+            return 0;
+        }
+        final File[] files = apkDirOrPath.listFiles();
+        long apksSize = 0;
+        for (int i = 0; i < files.length; i++) {
+            if (files[i].getName().toLowerCase().endsWith(".apk")) {
+                apksSize += files[i].length();
+            }
+        }
+        return apksSize;
     }
 
     /**
@@ -2879,7 +2910,7 @@
 
         mCallback.onSessionFinished(this, success);
         if (isDataLoaderInstallation()) {
-            logDataLoaderInstallationSession(returnCode, msg);
+            logDataLoaderInstallationSession(returnCode);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a4d74f0..f288f1f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2072,8 +2072,18 @@
             int autoRevokePermissionsMode,
             boolean launchedForRestore, String installerPackage,
             IPackageInstallObserver2 installObserver, int dataLoaderType) {
-        final boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED;
+        boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED;
         final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null;
+        final String packageName = res.name;
+        final PackageSetting pkgSetting = succeeded ? getPackageSetting(packageName) : null;
+        if (succeeded && pkgSetting == null) {
+            Slog.e(TAG, packageName + " was removed before handlePackagePostInstall "
+                    + "could be executed");
+            res.returnCode = INSTALL_FAILED_PACKAGE_CHANGED;
+            res.returnMsg = "Package was removed before install could complete.";
+            notifyInstallObserver(res, installObserver);
+            return;
+        }
 
         if (succeeded) {
             // Send the removed broadcasts
@@ -2117,8 +2127,6 @@
                 mInstantAppRegistry.onPackageInstalledLPw(res.pkg, res.newUsers);
             }
 
-            final String packageName = res.pkg.getPackageName();
-
             // Determine the set of users who are adding this package for
             // the first time vs. those who are seeing an update.
             int[] firstUserIds = EMPTY_INT_ARRAY;
@@ -2126,7 +2134,7 @@
             int[] updateUserIds = EMPTY_INT_ARRAY;
             int[] instantUserIds = EMPTY_INT_ARRAY;
             final boolean allNewUsers = res.origUsers == null || res.origUsers.length == 0;
-            final PackageSetting ps = getPackageSetting(res.pkg.getPackageName());
+            final PackageSetting ps = pkgSetting;
             for (int newUser : res.newUsers) {
                 final boolean isInstantApp = ps.getInstantApp(newUser);
                 if (allNewUsers) {
@@ -2263,7 +2271,7 @@
                         if (packageExternalStorageType != StorageEnums.UNKNOWN) {
                             FrameworkStatsLog.write(
                                     FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
-                                    packageExternalStorageType, res.pkg.getPackageName());
+                                    packageExternalStorageType, packageName);
                         }
                     }
                     if (DEBUG_INSTALL) {
@@ -2295,12 +2303,9 @@
                     if (packageIsBrowser(packageName, userId)) {
                         // If this browser is restored from user's backup, do not clear
                         // default-browser state for this user
-                        synchronized (mLock) {
-                            final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
-                            if (pkgSetting.getInstallReason(userId)
-                                    != PackageManager.INSTALL_REASON_DEVICE_RESTORE) {
-                                mPermissionManager.setDefaultBrowser(null, true, true, userId);
-                            }
+                        if (pkgSetting.getInstallReason(userId)
+                                != PackageManager.INSTALL_REASON_DEVICE_RESTORE) {
+                            mPermissionManager.setDefaultBrowser(null, true, true, userId);
                         }
                     }
 
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index a635f98..9051d85 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -22,7 +22,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.AppOpsManager;
 import android.app.DownloadManager;
 import android.app.SearchManager;
 import android.app.admin.DevicePolicyManager;
@@ -60,6 +59,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.Xml;
 
 import com.android.internal.util.ArrayUtils;
@@ -222,10 +222,75 @@
     private final Context mContext;
     private final Object mLock = new Object();
     private final PackageManagerInternal mServiceInternal;
-    private final PermissionManagerService mPermissionManager;
 
-    DefaultPermissionGrantPolicy(Context context, Looper looper,
-            @NonNull PermissionManagerService permissionManager) {
+    /** Directly interact with the PackageManger */
+    private final PackageManagerWrapper NO_PM_CACHE = new PackageManagerWrapper() {
+        @Override
+        public int getPermissionFlags(@NonNull String permission, @NonNull PackageInfo pkg,
+                @NonNull UserHandle user) {
+            return mContext.getPackageManager().getPermissionFlags(permission, pkg.packageName,
+                    user);
+        }
+
+        @Override
+        public void updatePermissionFlags(@NonNull String permission, @NonNull PackageInfo pkg,
+                int flagMask, int flagValues, @NonNull UserHandle user) {
+            mContext.getPackageManager().updatePermissionFlags(permission, pkg.packageName,
+                    flagMask, flagValues, user);
+        }
+
+        @Override
+        public void grantPermission(@NonNull String permission, @NonNull PackageInfo pkg,
+                @NonNull UserHandle user) {
+            mContext.getPackageManager().grantRuntimePermission(pkg.packageName, permission,
+                    user);
+        }
+
+        @Override
+        public void revokePermission(@NonNull String permission, @NonNull PackageInfo pkg,
+                @NonNull UserHandle user) {
+            mContext.getPackageManager().revokeRuntimePermission(pkg.packageName, permission,
+                    user);
+        }
+
+        @Override
+        public boolean isGranted(@NonNull String permission, @NonNull PackageInfo pkg,
+                @NonNull UserHandle user) {
+            return mContext.createContextAsUser(user, 0).getPackageManager().checkPermission(
+                    permission, pkg.packageName) == PackageManager.PERMISSION_GRANTED;
+        }
+
+        @Override
+        public @Nullable PermissionInfo getPermissionInfo(@NonNull String permissionName) {
+            if (permissionName == null) {
+                return null;
+            }
+
+            try {
+                return mContext.getPackageManager().getPermissionInfo(permissionName, 0);
+            } catch (NameNotFoundException e) {
+                Slog.e(TAG, "Permission not found: " + permissionName);
+                return null;
+            }
+        }
+
+        @Override
+        public @Nullable PackageInfo getPackageInfo(@NonNull String pkg) {
+            if (pkg == null) {
+                return null;
+            }
+
+            try {
+                return mContext.getPackageManager().getPackageInfo(pkg,
+                        DEFAULT_PACKAGE_INFO_QUERY_FLAGS);
+            } catch (NameNotFoundException e) {
+                Slog.e(TAG, "Package not found: " + pkg);
+                return null;
+            }
+        }
+    };
+
+    DefaultPermissionGrantPolicy(Context context, Looper looper) {
         mContext = context;
         mHandler = new Handler(looper) {
             @Override
@@ -233,13 +298,12 @@
                 if (msg.what == MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS) {
                     synchronized (mLock) {
                         if (mGrantExceptions == null) {
-                            mGrantExceptions = readDefaultPermissionExceptionsLocked();
+                            mGrantExceptions = readDefaultPermissionExceptionsLocked(NO_PM_CACHE);
                         }
                     }
                 }
             }
         };
-        mPermissionManager = permissionManager;
         mServiceInternal = LocalServices.getService(PackageManagerInternal.class);
     }
 
@@ -293,24 +357,30 @@
     }
 
     public void grantDefaultPermissions(int userId) {
-        grantPermissionsToSysComponentsAndPrivApps(userId);
-        grantDefaultSystemHandlerPermissions(userId);
-        grantDefaultPermissionExceptions(userId);
+        DelayingPackageManagerCache pm = new DelayingPackageManagerCache();
+
+        grantPermissionsToSysComponentsAndPrivApps(pm, userId);
+        grantDefaultSystemHandlerPermissions(pm, userId);
+        grantDefaultPermissionExceptions(pm, userId);
+
+        // Apply delayed state
+        pm.apply();
     }
 
-    private void grantRuntimePermissionsForSystemPackage(int userId, PackageInfo pkg) {
+    private void grantRuntimePermissionsForSystemPackage(PackageManagerWrapper pm,
+            int userId, PackageInfo pkg) {
         Set<String> permissions = new ArraySet<>();
         for (String permission : pkg.requestedPermissions) {
-            final BasePermission bp = mPermissionManager.getPermission(permission);
-            if (bp == null) {
+            final PermissionInfo perm = pm.getPermissionInfo(permission);
+            if (perm == null) {
                 continue;
             }
-            if (bp.isRuntime()) {
+            if (perm.isRuntime()) {
                 permissions.add(permission);
             }
         }
         if (!permissions.isEmpty()) {
-            grantRuntimePermissions(pkg, permissions, true /*systemFixed*/, userId);
+            grantRuntimePermissions(pm, pkg, permissions, true /*systemFixed*/, userId);
         }
     }
 
@@ -318,7 +388,8 @@
         mHandler.sendEmptyMessage(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
     }
 
-    private void grantPermissionsToSysComponentsAndPrivApps(int userId) {
+    private void grantPermissionsToSysComponentsAndPrivApps(DelayingPackageManagerCache pm,
+            int userId) {
         Log.i(TAG, "Granting permissions to platform components for user " + userId);
         List<PackageInfo> packages = mContext.getPackageManager().getInstalledPackagesAsUser(
                 DEFAULT_PACKAGE_INFO_QUERY_FLAGS, UserHandle.USER_SYSTEM);
@@ -326,72 +397,76 @@
             if (pkg == null) {
                 continue;
             }
-            if (!isSysComponentOrPersistentPlatformSignedPrivApp(pkg)
+
+            // Package info is already loaded, cache it
+            pm.addPackageInfo(pkg.packageName, pkg);
+
+            if (!pm.isSysComponentOrPersistentPlatformSignedPrivApp(pkg)
                     || !doesPackageSupportRuntimePermissions(pkg)
                     || ArrayUtils.isEmpty(pkg.requestedPermissions)) {
                 continue;
             }
-            grantRuntimePermissionsForSystemPackage(userId, pkg);
+            grantRuntimePermissionsForSystemPackage(pm, userId, pkg);
         }
     }
 
     @SafeVarargs
-    private final void grantIgnoringSystemPackage(String packageName, int userId,
-            Set<String>... permissionGroups) {
-        grantPermissionsToPackage(packageName, userId, true /* ignoreSystemPackage */,
+    private final void grantIgnoringSystemPackage(PackageManagerWrapper pm, String packageName,
+            int userId, Set<String>... permissionGroups) {
+        grantPermissionsToPackage(pm, packageName, userId, true /* ignoreSystemPackage */,
                 true /*whitelistRestrictedPermissions*/, permissionGroups);
     }
 
     @SafeVarargs
-    private final void grantSystemFixedPermissionsToSystemPackage(String packageName, int userId,
-            Set<String>... permissionGroups) {
-        grantPermissionsToSystemPackage(
-                packageName, userId, true /* systemFixed */, permissionGroups);
-    }
-
-    @SafeVarargs
-    private final void grantPermissionsToSystemPackage(
+    private final void grantSystemFixedPermissionsToSystemPackage(PackageManagerWrapper pm,
             String packageName, int userId, Set<String>... permissionGroups) {
-        grantPermissionsToSystemPackage(
-                packageName, userId, false /* systemFixed */, permissionGroups);
+        grantPermissionsToSystemPackage(pm, packageName, userId, true /* systemFixed */,
+                permissionGroups);
     }
 
     @SafeVarargs
-    private final void grantPermissionsToSystemPackage(String packageName, int userId,
-            boolean systemFixed, Set<String>... permissionGroups) {
-        if (!isSystemPackage(packageName)) {
+    private final void grantPermissionsToSystemPackage(PackageManagerWrapper pm,
+            String packageName, int userId, Set<String>... permissionGroups) {
+        grantPermissionsToSystemPackage(pm, packageName, userId, false /* systemFixed */,
+                permissionGroups);
+    }
+
+    @SafeVarargs
+    private final void grantPermissionsToSystemPackage(PackageManagerWrapper pm, String packageName,
+            int userId, boolean systemFixed, Set<String>... permissionGroups) {
+        if (!pm.isSystemPackage(packageName)) {
             return;
         }
-        grantPermissionsToPackage(getSystemPackageInfo(packageName),
+        grantPermissionsToPackage(pm, pm.getSystemPackageInfo(packageName),
                 userId, systemFixed, false /* ignoreSystemPackage */,
                 true /*whitelistRestrictedPermissions*/, permissionGroups);
     }
 
     @SafeVarargs
-    private final void grantPermissionsToPackage(String packageName, int userId,
-            boolean ignoreSystemPackage, boolean whitelistRestrictedPermissions,
+    private final void grantPermissionsToPackage(PackageManagerWrapper pm, String packageName,
+            int userId, boolean ignoreSystemPackage, boolean whitelistRestrictedPermissions,
             Set<String>... permissionGroups) {
-        grantPermissionsToPackage(getPackageInfo(packageName),
+        grantPermissionsToPackage(pm, pm.getPackageInfo(packageName),
                 userId, false /* systemFixed */, ignoreSystemPackage,
                 whitelistRestrictedPermissions, permissionGroups);
     }
 
     @SafeVarargs
-    private final void grantPermissionsToPackage(PackageInfo packageInfo, int userId,
-            boolean systemFixed, boolean ignoreSystemPackage,
+    private final void grantPermissionsToPackage(PackageManagerWrapper pm, PackageInfo packageInfo,
+            int userId, boolean systemFixed, boolean ignoreSystemPackage,
             boolean whitelistRestrictedPermissions, Set<String>... permissionGroups) {
         if (packageInfo == null) {
             return;
         }
         if (doesPackageSupportRuntimePermissions(packageInfo)) {
             for (Set<String> permissionGroup : permissionGroups) {
-                grantRuntimePermissions(packageInfo, permissionGroup, systemFixed,
+                grantRuntimePermissions(pm, packageInfo, permissionGroup, systemFixed,
                         ignoreSystemPackage, whitelistRestrictedPermissions, userId);
             }
         }
     }
 
-    private void grantDefaultSystemHandlerPermissions(int userId) {
+    private void grantDefaultSystemHandlerPermissions(PackageManagerWrapper pm, int userId) {
         Log.i(TAG, "Granting permissions to default platform handlers for user " + userId);
 
         final PackagesProvider locationPackagesProvider;
@@ -434,7 +509,7 @@
                 syncAdapterPackagesProvider.getPackages(CalendarContract.AUTHORITY, userId) : null;
 
         // Installer
-        grantSystemFixedPermissionsToSystemPackage(
+        grantSystemFixedPermissionsToSystemPackage(pm,
                 ArrayUtils.firstOrNull(getKnownPackages(
                         PackageManagerInternal.PACKAGE_INSTALLER, userId)),
                 userId, STORAGE_PERMISSIONS);
@@ -442,68 +517,68 @@
         // Verifier
         final String verifier = ArrayUtils.firstOrNull(getKnownPackages(
                 PackageManagerInternal.PACKAGE_VERIFIER, userId));
-        grantSystemFixedPermissionsToSystemPackage(verifier, userId, STORAGE_PERMISSIONS);
-        grantPermissionsToSystemPackage(verifier, userId, PHONE_PERMISSIONS, SMS_PERMISSIONS);
+        grantSystemFixedPermissionsToSystemPackage(pm, verifier, userId, STORAGE_PERMISSIONS);
+        grantPermissionsToSystemPackage(pm, verifier, userId, PHONE_PERMISSIONS, SMS_PERMISSIONS);
 
         // SetupWizard
-        grantPermissionsToSystemPackage(
+        grantPermissionsToSystemPackage(pm,
                 ArrayUtils.firstOrNull(getKnownPackages(
                         PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId)), userId,
                 PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
                 CAMERA_PERMISSIONS);
 
         // Camera
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(MediaStore.ACTION_IMAGE_CAPTURE, userId),
+        grantPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerActivityPackage(pm, MediaStore.ACTION_IMAGE_CAPTURE, userId),
                 userId, CAMERA_PERMISSIONS, MICROPHONE_PERMISSIONS, STORAGE_PERMISSIONS);
 
         // Sound recorder
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(
+        grantPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerActivityPackage(pm,
                         MediaStore.Audio.Media.RECORD_SOUND_ACTION, userId),
                 userId, MICROPHONE_PERMISSIONS);
 
         // Media provider
-        grantSystemFixedPermissionsToSystemPackage(
+        grantSystemFixedPermissionsToSystemPackage(pm,
                 getDefaultProviderAuthorityPackage(MediaStore.AUTHORITY, userId), userId,
                 STORAGE_PERMISSIONS);
 
         // Downloads provider
-        grantSystemFixedPermissionsToSystemPackage(
+        grantSystemFixedPermissionsToSystemPackage(pm,
                 getDefaultProviderAuthorityPackage("downloads", userId), userId,
                 STORAGE_PERMISSIONS);
 
         // Downloads UI
-        grantSystemFixedPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(
+        grantSystemFixedPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerActivityPackage(pm,
                         DownloadManager.ACTION_VIEW_DOWNLOADS, userId),
                 userId, STORAGE_PERMISSIONS);
 
         // Storage provider
-        grantSystemFixedPermissionsToSystemPackage(
+        grantSystemFixedPermissionsToSystemPackage(pm,
                 getDefaultProviderAuthorityPackage("com.android.externalstorage.documents", userId),
                 userId, STORAGE_PERMISSIONS);
 
         // CertInstaller
-        grantSystemFixedPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(Credentials.INSTALL_ACTION, userId), userId,
-                STORAGE_PERMISSIONS);
+        grantSystemFixedPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerActivityPackage(pm, Credentials.INSTALL_ACTION, userId),
+                userId, STORAGE_PERMISSIONS);
 
         // Dialer
         if (dialerAppPackageNames == null) {
             String dialerPackage =
-                    getDefaultSystemHandlerActivityPackage(Intent.ACTION_DIAL, userId);
-            grantDefaultPermissionsToDefaultSystemDialerApp(dialerPackage, userId);
+                    getDefaultSystemHandlerActivityPackage(pm, Intent.ACTION_DIAL, userId);
+            grantDefaultPermissionsToDefaultSystemDialerApp(pm, dialerPackage, userId);
         } else {
             for (String dialerAppPackageName : dialerAppPackageNames) {
-                grantDefaultPermissionsToDefaultSystemDialerApp(dialerAppPackageName, userId);
+                grantDefaultPermissionsToDefaultSystemDialerApp(pm, dialerAppPackageName, userId);
             }
         }
 
         // Sim call manager
         if (simCallManagerPackageNames != null) {
             for (String simCallManagerPackageName : simCallManagerPackageNames) {
-                grantDefaultPermissionsToDefaultSystemSimCallManager(
+                grantDefaultPermissionsToDefaultSystemSimCallManager(pm,
                         simCallManagerPackageName, userId);
             }
         }
@@ -511,77 +586,79 @@
         // Use Open Wifi
         if (useOpenWifiAppPackageNames != null) {
             for (String useOpenWifiPackageName : useOpenWifiAppPackageNames) {
-                grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(
+                grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(pm,
                         useOpenWifiPackageName, userId);
             }
         }
 
         // SMS
         if (smsAppPackageNames == null) {
-            String smsPackage = getDefaultSystemHandlerActivityPackageForCategory(
+            String smsPackage = getDefaultSystemHandlerActivityPackageForCategory(pm,
                     Intent.CATEGORY_APP_MESSAGING, userId);
-            grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId);
+            grantDefaultPermissionsToDefaultSystemSmsApp(pm, smsPackage, userId);
         } else {
             for (String smsPackage : smsAppPackageNames) {
-                grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId);
+                grantDefaultPermissionsToDefaultSystemSmsApp(pm, smsPackage, userId);
             }
         }
 
         // Cell Broadcast Receiver
-        grantSystemFixedPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(Intents.SMS_CB_RECEIVED_ACTION, userId),
+        grantSystemFixedPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerActivityPackage(pm, Intents.SMS_CB_RECEIVED_ACTION, userId),
                 userId, SMS_PERMISSIONS);
 
         // Carrier Provisioning Service
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerServicePackage(Intents.SMS_CARRIER_PROVISION_ACTION, userId),
+        grantPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerServicePackage(pm, Intents.SMS_CARRIER_PROVISION_ACTION,
+                        userId),
                 userId, SMS_PERMISSIONS);
 
         // Calendar
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackageForCategory(
+        grantPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerActivityPackageForCategory(pm,
                         Intent.CATEGORY_APP_CALENDAR, userId),
                 userId, CALENDAR_PERMISSIONS, CONTACTS_PERMISSIONS);
 
         // Calendar provider
         String calendarProvider =
                 getDefaultProviderAuthorityPackage(CalendarContract.AUTHORITY, userId);
-        grantPermissionsToSystemPackage(calendarProvider, userId,
+        grantPermissionsToSystemPackage(pm, calendarProvider, userId,
                 CONTACTS_PERMISSIONS, STORAGE_PERMISSIONS);
-        grantSystemFixedPermissionsToSystemPackage(calendarProvider, userId, CALENDAR_PERMISSIONS);
+        grantSystemFixedPermissionsToSystemPackage(pm, calendarProvider, userId,
+                CALENDAR_PERMISSIONS);
 
         // Calendar provider sync adapters
-        grantPermissionToEachSystemPackage(
-                getHeadlessSyncAdapterPackages(calendarSyncAdapterPackages, userId),
+        grantPermissionToEachSystemPackage(pm,
+                getHeadlessSyncAdapterPackages(pm, calendarSyncAdapterPackages, userId),
                 userId, CALENDAR_PERMISSIONS);
 
         // Contacts
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackageForCategory(
+        grantPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerActivityPackageForCategory(pm,
                         Intent.CATEGORY_APP_CONTACTS, userId),
                 userId, CONTACTS_PERMISSIONS, PHONE_PERMISSIONS);
 
         // Contacts provider sync adapters
-        grantPermissionToEachSystemPackage(
-                getHeadlessSyncAdapterPackages(contactsSyncAdapterPackages, userId),
+        grantPermissionToEachSystemPackage(pm,
+                getHeadlessSyncAdapterPackages(pm, contactsSyncAdapterPackages, userId),
                 userId, CONTACTS_PERMISSIONS);
 
         // Contacts provider
         String contactsProviderPackage =
                 getDefaultProviderAuthorityPackage(ContactsContract.AUTHORITY, userId);
-        grantSystemFixedPermissionsToSystemPackage(contactsProviderPackage, userId,
+        grantSystemFixedPermissionsToSystemPackage(pm, contactsProviderPackage, userId,
                 CONTACTS_PERMISSIONS, PHONE_PERMISSIONS);
-        grantPermissionsToSystemPackage(contactsProviderPackage, userId, STORAGE_PERMISSIONS);
+        grantPermissionsToSystemPackage(pm, contactsProviderPackage, userId, STORAGE_PERMISSIONS);
 
         // Device provisioning
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(
+        grantPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerActivityPackage(pm,
                         DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, userId),
                 userId, CONTACTS_PERMISSIONS);
 
         // Email
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackageForCategory(
+        grantPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerActivityPackageForCategory(pm,
                         Intent.CATEGORY_APP_EMAIL, userId),
                 userId, CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS);
 
@@ -589,19 +666,19 @@
         String browserPackage = ArrayUtils.firstOrNull(getKnownPackages(
                 PackageManagerInternal.PACKAGE_BROWSER, userId));
         if (browserPackage == null) {
-            browserPackage = getDefaultSystemHandlerActivityPackageForCategory(
+            browserPackage = getDefaultSystemHandlerActivityPackageForCategory(pm,
                     Intent.CATEGORY_APP_BROWSER, userId);
-            if (!isSystemPackage(browserPackage)) {
+            if (!pm.isSystemPackage(browserPackage)) {
                 browserPackage = null;
             }
         }
-        grantPermissionsToPackage(browserPackage, userId, false /* ignoreSystemPackage */,
+        grantPermissionsToPackage(pm, browserPackage, userId, false /* ignoreSystemPackage */,
                 true /*whitelistRestrictedPermissions*/, FOREGROUND_LOCATION_PERMISSIONS);
 
         // Voice interaction
         if (voiceInteractPackageNames != null) {
             for (String voiceInteractPackageName : voiceInteractPackageNames) {
-                grantPermissionsToSystemPackage(voiceInteractPackageName, userId,
+                grantPermissionsToSystemPackage(pm, voiceInteractPackageName, userId,
                         CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS,
                         PHONE_PERMISSIONS, SMS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS);
             }
@@ -609,8 +686,8 @@
 
         if (ActivityManager.isLowRamDeviceStatic()) {
             // Allow voice search on low-ram devices
-            grantPermissionsToSystemPackage(
-                    getDefaultSystemHandlerActivityPackage(
+            grantPermissionsToSystemPackage(pm,
+                    getDefaultSystemHandlerActivityPackage(pm,
                             SearchManager.INTENT_ACTION_GLOBAL_SEARCH, userId),
                     userId, MICROPHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS);
         }
@@ -618,25 +695,26 @@
         // Voice recognition
         Intent voiceRecoIntent = new Intent(RecognitionService.SERVICE_INTERFACE)
                 .addCategory(Intent.CATEGORY_DEFAULT);
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerServicePackage(voiceRecoIntent, userId), userId,
+        grantPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerServicePackage(pm, voiceRecoIntent, userId), userId,
                 MICROPHONE_PERMISSIONS);
 
         // Location
         if (locationPackageNames != null) {
             for (String packageName : locationPackageNames) {
-                grantPermissionsToSystemPackage(packageName, userId,
+                grantPermissionsToSystemPackage(pm, packageName, userId,
                         CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS,
                         PHONE_PERMISSIONS, SMS_PERMISSIONS, CAMERA_PERMISSIONS,
                         SENSORS_PERMISSIONS, STORAGE_PERMISSIONS);
-                grantSystemFixedPermissionsToSystemPackage(packageName, userId,
+                grantSystemFixedPermissionsToSystemPackage(pm, packageName, userId,
                         ALWAYS_LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS);
             }
         }
         if (locationExtraPackageNames != null) {
             // Also grant location permission to location extra packages.
             for (String packageName : locationExtraPackageNames) {
-                grantPermissionsToSystemPackage(packageName, userId, ALWAYS_LOCATION_PERMISSIONS);
+                grantPermissionsToSystemPackage(pm, packageName, userId,
+                        ALWAYS_LOCATION_PERMISSIONS);
             }
         }
 
@@ -644,72 +722,72 @@
         Intent musicIntent = new Intent(Intent.ACTION_VIEW)
                 .addCategory(Intent.CATEGORY_DEFAULT)
                 .setDataAndType(Uri.fromFile(new File("foo.mp3")), AUDIO_MIME_TYPE);
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(musicIntent, userId), userId,
+        grantPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerActivityPackage(pm, musicIntent, userId), userId,
                 STORAGE_PERMISSIONS);
 
         // Home
         Intent homeIntent = new Intent(Intent.ACTION_MAIN)
                 .addCategory(Intent.CATEGORY_HOME)
                 .addCategory(Intent.CATEGORY_LAUNCHER_APP);
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(homeIntent, userId), userId,
+        grantPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerActivityPackage(pm, homeIntent, userId), userId,
                 ALWAYS_LOCATION_PERMISSIONS);
 
         // Watches
         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) {
             // Home application on watches
 
-            String wearPackage = getDefaultSystemHandlerActivityPackageForCategory(
+            String wearPackage = getDefaultSystemHandlerActivityPackageForCategory(pm,
                     Intent.CATEGORY_HOME_MAIN, userId);
-            grantPermissionsToSystemPackage(wearPackage, userId,
+            grantPermissionsToSystemPackage(pm, wearPackage, userId,
                     CONTACTS_PERMISSIONS, MICROPHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS);
-            grantSystemFixedPermissionsToSystemPackage(wearPackage, userId, PHONE_PERMISSIONS);
+            grantSystemFixedPermissionsToSystemPackage(pm, wearPackage, userId, PHONE_PERMISSIONS);
 
             // Fitness tracking on watches
-            grantPermissionsToSystemPackage(
-                    getDefaultSystemHandlerActivityPackage(ACTION_TRACK, userId), userId,
+            grantPermissionsToSystemPackage(pm,
+                    getDefaultSystemHandlerActivityPackage(pm, ACTION_TRACK, userId), userId,
                     SENSORS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS);
         }
 
         // Print Spooler
-        grantSystemFixedPermissionsToSystemPackage(PrintManager.PRINT_SPOOLER_PACKAGE_NAME, userId,
-                ALWAYS_LOCATION_PERMISSIONS);
+        grantSystemFixedPermissionsToSystemPackage(pm, PrintManager.PRINT_SPOOLER_PACKAGE_NAME,
+                userId, ALWAYS_LOCATION_PERMISSIONS);
 
         // EmergencyInfo
-        grantSystemFixedPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(
+        grantSystemFixedPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerActivityPackage(pm,
                         TelephonyManager.ACTION_EMERGENCY_ASSISTANCE, userId),
                 userId, CONTACTS_PERMISSIONS, PHONE_PERMISSIONS);
 
         // NFC Tag viewer
         Intent nfcTagIntent = new Intent(Intent.ACTION_VIEW)
                 .setType("vnd.android.cursor.item/ndef_msg");
-        grantPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(nfcTagIntent, userId), userId,
+        grantPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerActivityPackage(pm, nfcTagIntent, userId), userId,
                 CONTACTS_PERMISSIONS, PHONE_PERMISSIONS);
 
         // Storage Manager
-        grantSystemFixedPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(
+        grantSystemFixedPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerActivityPackage(pm,
                         StorageManager.ACTION_MANAGE_STORAGE, userId),
                 userId, STORAGE_PERMISSIONS);
 
         // Companion devices
-        grantSystemFixedPermissionsToSystemPackage(
+        grantSystemFixedPermissionsToSystemPackage(pm,
                 CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, userId,
                 ALWAYS_LOCATION_PERMISSIONS);
 
         // Ringtone Picker
-        grantSystemFixedPermissionsToSystemPackage(
-                getDefaultSystemHandlerActivityPackage(
+        grantSystemFixedPermissionsToSystemPackage(pm,
+                getDefaultSystemHandlerActivityPackage(pm,
                         RingtoneManager.ACTION_RINGTONE_PICKER, userId),
                 userId, STORAGE_PERMISSIONS);
 
         // TextClassifier Service
         for (String textClassifierPackage :
                 getKnownPackages(PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER, userId)) {
-            grantPermissionsToSystemPackage(textClassifierPackage, userId,
+            grantPermissionsToSystemPackage(pm, textClassifierPackage, userId,
                     COARSE_BACKGROUND_LOCATION_PERMISSIONS, CONTACTS_PERMISSIONS);
         }
 
@@ -717,7 +795,7 @@
         String contentCapturePackageName =
                 mContext.getPackageManager().getContentCaptureServicePackageName();
         if (!TextUtils.isEmpty(contentCapturePackageName)) {
-            grantPermissionsToSystemPackage(contentCapturePackageName, userId,
+            grantPermissionsToSystemPackage(pm, contentCapturePackageName, userId,
                     PHONE_PERMISSIONS, SMS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
                     CONTACTS_PERMISSIONS, STORAGE_PERMISSIONS);
         }
@@ -726,36 +804,37 @@
         String attentionServicePackageName =
                 mContext.getPackageManager().getAttentionServicePackageName();
         if (!TextUtils.isEmpty(attentionServicePackageName)) {
-            grantPermissionsToSystemPackage(attentionServicePackageName, userId,
+            grantPermissionsToSystemPackage(pm, attentionServicePackageName, userId,
                     CAMERA_PERMISSIONS);
         }
 
         // There is no real "marker" interface to identify the shared storage backup, it is
         // hardcoded in BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE.
-        grantSystemFixedPermissionsToSystemPackage("com.android.sharedstoragebackup", userId,
+        grantSystemFixedPermissionsToSystemPackage(pm, "com.android.sharedstoragebackup", userId,
                 STORAGE_PERMISSIONS);
 
         // System Captions Service
         String systemCaptionsServicePackageName =
                 mContext.getPackageManager().getSystemCaptionsServicePackageName();
         if (!TextUtils.isEmpty(systemCaptionsServicePackageName)) {
-            grantPermissionsToSystemPackage(systemCaptionsServicePackageName, userId,
+            grantPermissionsToSystemPackage(pm, systemCaptionsServicePackageName, userId,
                     MICROPHONE_PERMISSIONS);
         }
     }
 
-    private String getDefaultSystemHandlerActivityPackageForCategory(String category, int userId) {
-        return getDefaultSystemHandlerActivityPackage(
+    private String getDefaultSystemHandlerActivityPackageForCategory(PackageManagerWrapper pm,
+            String category, int userId) {
+        return getDefaultSystemHandlerActivityPackage(pm,
                 new Intent(Intent.ACTION_MAIN).addCategory(category), userId);
     }
 
     @SafeVarargs
-    private final void grantPermissionToEachSystemPackage(
+    private final void grantPermissionToEachSystemPackage(PackageManagerWrapper pm,
             ArrayList<String> packages, int userId, Set<String>... permissions) {
         if (packages == null) return;
         final int count = packages.size();
         for (int i = 0; i < count; i++) {
-            grantPermissionsToSystemPackage(packages.get(i), userId, permissions);
+            grantPermissionsToSystemPackage(pm, packages.get(i), userId, permissions);
         }
     }
 
@@ -763,7 +842,7 @@
         return mServiceInternal.getKnownPackageNames(knownPkgId, userId);
     }
 
-    private void grantDefaultPermissionsToDefaultSystemDialerApp(
+    private void grantDefaultPermissionsToDefaultSystemDialerApp(PackageManagerWrapper pm,
             String dialerPackage, int userId) {
         if (dialerPackage == null) {
             return;
@@ -771,43 +850,51 @@
         boolean isPhonePermFixed =
                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0);
         if (isPhonePermFixed) {
-            grantSystemFixedPermissionsToSystemPackage(dialerPackage, userId, PHONE_PERMISSIONS);
+            grantSystemFixedPermissionsToSystemPackage(pm, dialerPackage, userId,
+                    PHONE_PERMISSIONS);
         } else {
-            grantPermissionsToSystemPackage(dialerPackage, userId, PHONE_PERMISSIONS);
+            grantPermissionsToSystemPackage(pm, dialerPackage, userId, PHONE_PERMISSIONS);
         }
-        grantPermissionsToSystemPackage(dialerPackage, userId,
+        grantPermissionsToSystemPackage(pm, dialerPackage, userId,
                 CONTACTS_PERMISSIONS, SMS_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS);
     }
 
-    private void grantDefaultPermissionsToDefaultSystemSmsApp(String smsPackage, int userId) {
-        grantPermissionsToSystemPackage(smsPackage, userId,
+    private void grantDefaultPermissionsToDefaultSystemSmsApp(PackageManagerWrapper pm,
+            String smsPackage, int userId) {
+        grantPermissionsToSystemPackage(pm, smsPackage, userId,
                 PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, SMS_PERMISSIONS,
                 STORAGE_PERMISSIONS, MICROPHONE_PERMISSIONS, CAMERA_PERMISSIONS);
     }
 
-    private void grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(
+    private void grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(PackageManagerWrapper pm,
             String useOpenWifiPackage, int userId) {
-        grantPermissionsToSystemPackage(useOpenWifiPackage, userId, ALWAYS_LOCATION_PERMISSIONS);
+        grantPermissionsToSystemPackage(pm, useOpenWifiPackage, userId,
+                ALWAYS_LOCATION_PERMISSIONS);
     }
 
     public void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, int userId) {
         Log.i(TAG, "Granting permissions to default Use Open WiFi app for user:" + userId);
-        grantIgnoringSystemPackage(packageName, userId, ALWAYS_LOCATION_PERMISSIONS);
+        grantIgnoringSystemPackage(NO_PM_CACHE, packageName, userId, ALWAYS_LOCATION_PERMISSIONS);
     }
 
     public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
+        grantDefaultPermissionsToDefaultSimCallManager(NO_PM_CACHE, packageName, userId);
+    }
+
+    private void grantDefaultPermissionsToDefaultSimCallManager(PackageManagerWrapper pm,
+            String packageName, int userId) {
         if (packageName == null) {
             return;
         }
         Log.i(TAG, "Granting permissions to sim call manager for user:" + userId);
-        grantPermissionsToPackage(packageName, userId, false /* ignoreSystemPackage */,
+        grantPermissionsToPackage(pm, packageName, userId, false /* ignoreSystemPackage */,
                 true /*whitelistRestrictedPermissions*/, PHONE_PERMISSIONS, MICROPHONE_PERMISSIONS);
     }
 
-    private void grantDefaultPermissionsToDefaultSystemSimCallManager(
+    private void grantDefaultPermissionsToDefaultSystemSimCallManager(PackageManagerWrapper pm,
             String packageName, int userId) {
-        if (isSystemPackage(packageName)) {
-            grantDefaultPermissionsToDefaultSimCallManager(packageName, userId);
+        if (pm.isSystemPackage(packageName)) {
+            grantDefaultPermissionsToDefaultSimCallManager(pm, packageName, userId);
         }
     }
 
@@ -817,7 +904,7 @@
             return;
         }
         for (String packageName : packageNames) {
-            grantPermissionsToSystemPackage(packageName, userId,
+            grantPermissionsToSystemPackage(NO_PM_CACHE, packageName, userId,
                     PHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, SMS_PERMISSIONS);
         }
     }
@@ -828,7 +915,7 @@
             return;
         }
         for (String packageName : packageNames) {
-            grantPermissionsToSystemPackage(packageName, userId,
+            grantPermissionsToSystemPackage(NO_PM_CACHE, packageName, userId,
                     PHONE_PERMISSIONS, MICROPHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
                     CAMERA_PERMISSIONS, CONTACTS_PERMISSIONS);
         }
@@ -843,7 +930,7 @@
         for (String packageName : packageNames) {
             // Grant these permissions as system-fixed, so that nobody can accidentally
             // break cellular data.
-            grantSystemFixedPermissionsToSystemPackage(packageName, userId,
+            grantSystemFixedPermissionsToSystemPackage(NO_PM_CACHE, packageName, userId,
                     PHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS);
         }
     }
@@ -855,17 +942,20 @@
             return;
         }
         for (String packageName : packageNames) {
-            PackageInfo pkg = getSystemPackageInfo(packageName);
-            if (isSystemPackage(pkg) && doesPackageSupportRuntimePermissions(pkg)) {
-                revokeRuntimePermissions(packageName, PHONE_PERMISSIONS, true, userId);
-                revokeRuntimePermissions(packageName, ALWAYS_LOCATION_PERMISSIONS, true, userId);
+            PackageInfo pkg = NO_PM_CACHE.getSystemPackageInfo(packageName);
+            if (NO_PM_CACHE.isSystemPackage(pkg) && doesPackageSupportRuntimePermissions(pkg)) {
+                revokeRuntimePermissions(NO_PM_CACHE, packageName, PHONE_PERMISSIONS, true,
+                        userId);
+                revokeRuntimePermissions(NO_PM_CACHE, packageName, ALWAYS_LOCATION_PERMISSIONS,
+                        true, userId);
             }
         }
     }
 
     public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId) {
         Log.i(TAG, "Granting permissions to active LUI app for user:" + userId);
-        grantSystemFixedPermissionsToSystemPackage(packageName, userId, CAMERA_PERMISSIONS);
+        grantSystemFixedPermissionsToSystemPackage(NO_PM_CACHE, packageName, userId,
+                CAMERA_PERMISSIONS);
     }
 
     public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId) {
@@ -874,23 +964,27 @@
             return;
         }
         for (String packageName : packageNames) {
-            PackageInfo pkg = getSystemPackageInfo(packageName);
-            if (isSystemPackage(pkg) && doesPackageSupportRuntimePermissions(pkg)) {
-                revokeRuntimePermissions(packageName, CAMERA_PERMISSIONS, true, userId);
+            PackageInfo pkg = NO_PM_CACHE.getSystemPackageInfo(packageName);
+            if (NO_PM_CACHE.isSystemPackage(pkg) && doesPackageSupportRuntimePermissions(pkg)) {
+                revokeRuntimePermissions(NO_PM_CACHE, packageName, CAMERA_PERMISSIONS, true,
+                        userId);
             }
         }
     }
 
     public void grantDefaultPermissionsToDefaultBrowser(String packageName, int userId) {
         Log.i(TAG, "Granting permissions to default browser for user:" + userId);
-        grantPermissionsToSystemPackage(packageName, userId, FOREGROUND_LOCATION_PERMISSIONS);
+        grantPermissionsToSystemPackage(NO_PM_CACHE, packageName, userId,
+                FOREGROUND_LOCATION_PERMISSIONS);
     }
 
-    private String getDefaultSystemHandlerActivityPackage(String intentAction, int userId) {
-        return getDefaultSystemHandlerActivityPackage(new Intent(intentAction), userId);
+    private String getDefaultSystemHandlerActivityPackage(PackageManagerWrapper pm,
+            String intentAction, int userId) {
+        return getDefaultSystemHandlerActivityPackage(pm, new Intent(intentAction), userId);
     }
 
-    private String getDefaultSystemHandlerActivityPackage(Intent intent, int userId) {
+    private String getDefaultSystemHandlerActivityPackage(PackageManagerWrapper pm, Intent intent,
+            int userId) {
         ResolveInfo handler = mContext.getPackageManager().resolveActivityAsUser(
                 intent, DEFAULT_INTENT_QUERY_FLAGS, userId);
         if (handler == null || handler.activityInfo == null) {
@@ -900,14 +994,15 @@
             return null;
         }
         String packageName = handler.activityInfo.packageName;
-        return isSystemPackage(packageName) ? packageName : null;
+        return pm.isSystemPackage(packageName) ? packageName : null;
     }
 
-    private String getDefaultSystemHandlerServicePackage(String intentAction, int userId) {
-        return getDefaultSystemHandlerServicePackage(new Intent(intentAction), userId);
+    private String getDefaultSystemHandlerServicePackage(PackageManagerWrapper pm,
+            String intentAction, int userId) {
+        return getDefaultSystemHandlerServicePackage(pm, new Intent(intentAction), userId);
     }
 
-    private String getDefaultSystemHandlerServicePackage(
+    private String getDefaultSystemHandlerServicePackage(PackageManagerWrapper pm,
             Intent intent, int userId) {
         List<ResolveInfo> handlers = mContext.getPackageManager().queryIntentServicesAsUser(
                 intent, DEFAULT_INTENT_QUERY_FLAGS, userId);
@@ -918,14 +1013,14 @@
         for (int i = 0; i < handlerCount; i++) {
             ResolveInfo handler = handlers.get(i);
             String handlerPackage = handler.serviceInfo.packageName;
-            if (isSystemPackage(handlerPackage)) {
+            if (pm.isSystemPackage(handlerPackage)) {
                 return handlerPackage;
             }
         }
         return null;
     }
 
-    private ArrayList<String> getHeadlessSyncAdapterPackages(
+    private ArrayList<String> getHeadlessSyncAdapterPackages(PackageManagerWrapper pm,
             String[] syncAdapterPackageNames, int userId) {
         ArrayList<String> syncAdapterPackages = new ArrayList<>();
 
@@ -940,7 +1035,7 @@
                 continue;
             }
 
-            if (isSystemPackage(syncAdapterPackageName)) {
+            if (pm.isSystemPackage(syncAdapterPackageName)) {
                 syncAdapterPackages.add(syncAdapterPackageName);
             }
         }
@@ -957,27 +1052,15 @@
         return null;
     }
 
-    private boolean isSystemPackage(String packageName) {
-        return isSystemPackage(getPackageInfo(packageName));
-    }
-
-    private boolean isSystemPackage(PackageInfo pkg) {
-        if (pkg == null) {
-            return false;
-        }
-        return pkg.applicationInfo.isSystemApp()
-                && !isSysComponentOrPersistentPlatformSignedPrivApp(pkg);
-    }
-
-    private void grantRuntimePermissions(PackageInfo pkg, Set<String> permissions,
-            boolean systemFixed, int userId) {
-        grantRuntimePermissions(pkg, permissions, systemFixed, false,
+    private void grantRuntimePermissions(PackageManagerWrapper pm, PackageInfo pkg,
+            Set<String> permissions, boolean systemFixed, int userId) {
+        grantRuntimePermissions(pm, pkg, permissions, systemFixed, false,
                 true /*whitelistRestrictedPermissions*/, userId);
     }
 
-    private void revokeRuntimePermissions(String packageName, Set<String> permissions,
-            boolean systemFixed, int userId) {
-        PackageInfo pkg = getSystemPackageInfo(packageName);
+    private void revokeRuntimePermissions(PackageManagerWrapper pm, String packageName,
+            Set<String> permissions, boolean systemFixed, int userId) {
+        PackageInfo pkg = pm.getSystemPackageInfo(packageName);
         if (pkg == null || ArrayUtils.isEmpty(pkg.requestedPermissions)) {
             return;
         }
@@ -990,8 +1073,8 @@
             }
 
             UserHandle user = UserHandle.of(userId);
-            final int flags = mContext.getPackageManager()
-                    .getPermissionFlags(permission, packageName, user);
+            final int flags = pm.getPermissionFlags(permission, pm.getPackageInfo(packageName),
+                    user);
 
             // We didn't get this through the default grant policy. Move along.
             if ((flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) == 0) {
@@ -1007,7 +1090,7 @@
             if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0 && !systemFixed) {
                 continue;
             }
-            mContext.getPackageManager().revokeRuntimePermission(packageName, permission, user);
+            pm.revokePermission(permission, pkg, user);
 
             if (DEBUG) {
                 Log.i(TAG, "revoked " + (systemFixed ? "fixed " : "not fixed ")
@@ -1017,7 +1100,7 @@
             // Remove the GRANTED_BY_DEFAULT flag without touching the others.
             // Note that we do not revoke FLAG_PERMISSION_SYSTEM_FIXED. That bit remains
             // sticky once set.
-            mContext.getPackageManager().updatePermissionFlags(permission, packageName,
+            pm.updatePermissionFlags(permission, pkg,
                     PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, 0, user);
         }
     }
@@ -1040,25 +1123,8 @@
                 | PackageManager.FLAG_PERMISSION_SYSTEM_FIXED)) != 0;
     }
 
-    /**
-     * Return the background permission for a permission.
-     *
-     * @param permission The name of the foreground permission
-     *
-     * @return The name of the background permission or {@code null} if the permission has no
-     *         background permission
-     */
-    private @Nullable String getBackgroundPermission(@NonNull String permission) {
-        try {
-            return mContext.getPackageManager().getPermissionInfo(permission,
-                    0).backgroundPermission;
-        } catch (NameNotFoundException e) {
-            return null;
-        }
-    }
-
-    private void grantRuntimePermissions(PackageInfo pkg, Set<String> permissionsWithoutSplits,
-            boolean systemFixed, boolean ignoreSystemPackage,
+    private void grantRuntimePermissions(PackageManagerWrapper pm, PackageInfo pkg,
+            Set<String> permissionsWithoutSplits, boolean systemFixed, boolean ignoreSystemPackage,
             boolean whitelistRestrictedPermissions, int userId) {
         UserHandle user = UserHandle.of(userId);
         if (pkg == null) {
@@ -1072,7 +1138,8 @@
 
         // Intersect the requestedPermissions for a factory image with that of its current update
         // in case the latter one removed a <uses-permission>
-        String[] requestedByNonSystemPackage = getPackageInfo(pkg.packageName).requestedPermissions;
+        String[] requestedByNonSystemPackage = pm.getPackageInfo(pkg.packageName)
+                .requestedPermissions;
         int size = requestedPermissions.length;
         for (int i = 0; i < size; i++) {
             if (!ArrayUtils.contains(requestedByNonSystemPackage, requestedPermissions[i])) {
@@ -1081,14 +1148,6 @@
         }
         requestedPermissions = ArrayUtils.filterNotNull(requestedPermissions, String[]::new);
 
-        PackageManager pm;
-        try {
-            pm = mContext.createPackageContextAsUser(mContext.getPackageName(), 0,
-                    user).getPackageManager();
-        } catch (NameNotFoundException doesNotHappen) {
-            throw new IllegalStateException(doesNotHappen);
-        }
-
         final ArraySet<String> permissions = new ArraySet<>(permissionsWithoutSplits);
         ApplicationInfo applicationInfo = pkg.applicationInfo;
 
@@ -1123,7 +1182,7 @@
         if (!ignoreSystemPackage
                 && applicationInfo != null
                 && applicationInfo.isUpdatedSystemApp()) {
-            final PackageInfo disabledPkg = getSystemPackageInfo(
+            final PackageInfo disabledPkg = pm.getSystemPackageInfo(
                     mServiceInternal.getDisabledSystemPackageName(pkg.packageName));
             if (disabledPkg != null) {
                 if (ArrayUtils.isEmpty(disabledPkg.requestedPermissions)) {
@@ -1145,7 +1204,7 @@
         int numOther = 0;
         for (int i = 0; i < numRequestedPermissions; i++) {
             String permission = requestedPermissions[i];
-            if (getBackgroundPermission(permission) != null) {
+            if (pm.getBackgroundPermission(permission) != null) {
                 sortedRequestedPermissions[numForeground] = permission;
                 numForeground++;
             } else {
@@ -1166,8 +1225,7 @@
             }
 
             if (permissions.contains(permission)) {
-                final int flags = mContext.getPackageManager().getPermissionFlags(
-                        permission, pkg.packageName, user);
+                final int flags = pm.getPermissionFlags(permission, pkg, user);
 
                 // If we are trying to grant as system fixed and already system fixed
                 // then the system can change the system fixed grant state.
@@ -1194,9 +1252,8 @@
                     newFlags |= (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT);
 
                     // If we are whitelisting the permission, update the exempt flag before grant.
-                    if (whitelistRestrictedPermissions && isPermissionRestricted(permission)) {
-                        mContext.getPackageManager().updatePermissionFlags(permission,
-                                pkg.packageName,
+                    if (whitelistRestrictedPermissions && pm.isPermissionRestricted(permission)) {
+                        pm.updatePermissionFlags(permission, pkg,
                                 PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT,
                                 PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, user);
                     }
@@ -1204,82 +1261,15 @@
                     // If the system tries to change a system fixed permission from one fixed
                     // state to another we need to drop the fixed flag to allow the grant.
                     if (changingGrantForSystemFixed) {
-                        mContext.getPackageManager().updatePermissionFlags(permission,
-                                pkg.packageName, flags,
+                        pm.updatePermissionFlags(permission, pkg, flags,
                                 flags & ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, user);
                     }
 
-                    if (pm.checkPermission(permission, pkg.packageName)
-                            != PackageManager.PERMISSION_GRANTED) {
-                        mContext.getPackageManager()
-                                .grantRuntimePermission(pkg.packageName, permission, user);
+                    if (!pm.isGranted(permission, pkg, user)) {
+                        pm.grantPermission(permission, pkg, user);
                     }
 
-                    mContext.getPackageManager().updatePermissionFlags(permission, pkg.packageName,
-                            newFlags, newFlags, user);
-
-                    int uid = UserHandle.getUid(userId,
-                            UserHandle.getAppId(pkg.applicationInfo.uid));
-
-                    List<String> fgPerms = mPermissionManager.getBackgroundPermissions()
-                            .get(permission);
-                    if (fgPerms != null) {
-                        int numFgPerms = fgPerms.size();
-                        for (int fgPermNum = 0; fgPermNum < numFgPerms; fgPermNum++) {
-                            String fgPerm = fgPerms.get(fgPermNum);
-
-                            if (pm.checkPermission(fgPerm, pkg.packageName)
-                                    == PackageManager.PERMISSION_GRANTED) {
-                                // Upgrade the app-op state of the fg permission to allow bg access
-                                // TODO: Dont' call app ops from package manager code.
-                                mContext.getSystemService(AppOpsManager.class).setUidMode(
-                                        AppOpsManager.permissionToOp(fgPerm), uid,
-                                        AppOpsManager.MODE_ALLOWED);
-
-                                break;
-                            }
-                        }
-                    }
-
-                    String bgPerm = getBackgroundPermission(permission);
-                    String op = AppOpsManager.permissionToOp(permission);
-                    if (bgPerm == null) {
-                        if (op != null) {
-                            // TODO: Dont' call app ops from package manager code.
-                            mContext.getSystemService(AppOpsManager.class).setUidMode(op, uid,
-                                    AppOpsManager.MODE_ALLOWED);
-                        }
-                    } else {
-                        int mode;
-                        if (pm.checkPermission(bgPerm, pkg.packageName)
-                                == PackageManager.PERMISSION_GRANTED) {
-                            mode = AppOpsManager.MODE_ALLOWED;
-                        } else {
-                            mode = AppOpsManager.MODE_FOREGROUND;
-                        }
-
-                        mContext.getSystemService(AppOpsManager.class).setUidMode(op, uid, mode);
-                    }
-
-                    if (DEBUG) {
-                        Log.i(TAG, "Granted " + (systemFixed ? "fixed " : "not fixed ")
-                                + permission + " to default handler " + pkg);
-
-                        int appOp = AppOpsManager.permissionToOpCode(permission);
-                        if (appOp != AppOpsManager.OP_NONE
-                                && AppOpsManager.opToDefaultMode(appOp)
-                                        != AppOpsManager.MODE_ALLOWED) {
-                            // Permission has a corresponding appop which is not allowed by default
-                            // We must allow it as well, as it's usually checked alongside the
-                            // permission
-                            if (DEBUG) {
-                                Log.i(TAG, "Granting OP_" + AppOpsManager.opToName(appOp)
-                                        + " to " + pkg.packageName);
-                            }
-                            mContext.getSystemService(AppOpsManager.class).setUidMode(
-                                    appOp, pkg.applicationInfo.uid, AppOpsManager.MODE_ALLOWED);
-                        }
-                    }
+                    pm.updatePermissionFlags(permission, pkg, newFlags, newFlags, user);
                 }
 
                 // If a component gets a permission for being the default handler A
@@ -1291,57 +1281,14 @@
                         Log.i(TAG, "Granted not fixed " + permission + " to default handler "
                                 + pkg);
                     }
-                    mContext.getPackageManager().updatePermissionFlags(permission, pkg.packageName,
+                    pm.updatePermissionFlags(permission, pkg,
                             PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, user);
                 }
             }
         }
     }
 
-    private PackageInfo getSystemPackageInfo(String pkg) {
-        return getPackageInfo(pkg, PackageManager.MATCH_SYSTEM_ONLY);
-    }
-
-    private PackageInfo getPackageInfo(String pkg) {
-        return getPackageInfo(pkg, 0 /* extraFlags */);
-    }
-
-    private PackageInfo getPackageInfo(String pkg,
-            @PackageManager.PackageInfoFlags int extraFlags) {
-        if (pkg == null) {
-            return null;
-        }
-        try {
-            return mContext.getPackageManager().getPackageInfo(pkg,
-                    DEFAULT_PACKAGE_INFO_QUERY_FLAGS | extraFlags);
-        } catch (NameNotFoundException e) {
-            Slog.e(TAG, "PackageNot found: " + pkg, e);
-            return null;
-        }
-    }
-
-    private boolean isSysComponentOrPersistentPlatformSignedPrivApp(PackageInfo pkg) {
-        if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {
-            return true;
-        }
-        if (!pkg.applicationInfo.isPrivilegedApp()) {
-            return false;
-        }
-        final PackageInfo disabledPkg = getSystemPackageInfo(
-                mServiceInternal.getDisabledSystemPackageName(pkg.applicationInfo.packageName));
-        if (disabledPkg != null) {
-            ApplicationInfo disabledPackageAppInfo = disabledPkg.applicationInfo;
-            if (disabledPackageAppInfo != null
-                    && (disabledPackageAppInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
-                return false;
-            }
-        } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
-            return false;
-        }
-        return mServiceInternal.isPlatformSigned(pkg.packageName);
-    }
-
-    private void grantDefaultPermissionExceptions(int userId) {
+    private void grantDefaultPermissionExceptions(PackageManagerWrapper pm, int userId) {
         mHandler.removeMessages(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS);
 
         synchronized (mLock) {
@@ -1350,7 +1297,7 @@
             // performed for every user. If there is an entry then the app
             // is on the system image and supports runtime permissions.
             if (mGrantExceptions == null) {
-                mGrantExceptions = readDefaultPermissionExceptionsLocked();
+                mGrantExceptions = readDefaultPermissionExceptionsLocked(pm);
             }
         }
 
@@ -1358,12 +1305,12 @@
         final int exceptionCount = mGrantExceptions.size();
         for (int i = 0; i < exceptionCount; i++) {
             String packageName = mGrantExceptions.keyAt(i);
-            PackageInfo pkg = getSystemPackageInfo(packageName);
+            PackageInfo pkg = pm.getSystemPackageInfo(packageName);
             List<DefaultPermissionGrant> permissionGrants = mGrantExceptions.valueAt(i);
             final int permissionGrantCount = permissionGrants.size();
             for (int j = 0; j < permissionGrantCount; j++) {
                 DefaultPermissionGrant permissionGrant = permissionGrants.get(j);
-                if (!isPermissionDangerous(permissionGrant.name)) {
+                if (!pm.isPermissionDangerous(permissionGrant.name)) {
                     Log.w(TAG, "Ignoring permission " + permissionGrant.name
                             + " which isn't dangerous");
                     continue;
@@ -1376,7 +1323,7 @@
                 permissions.add(permissionGrant.name);
 
 
-                grantRuntimePermissions(pkg, permissions, permissionGrant.fixed,
+                grantRuntimePermissions(pm, pkg, permissions, permissionGrant.fixed,
                         permissionGrant.whitelisted, true /*whitelistRestrictedPermissions*/,
                         userId);
             }
@@ -1416,7 +1363,7 @@
     }
 
     private @NonNull ArrayMap<String, List<DefaultPermissionGrant>>
-            readDefaultPermissionExceptionsLocked() {
+            readDefaultPermissionExceptionsLocked(PackageManagerWrapper pm) {
         File[] files = getDefaultPermissionFiles();
         if (files == null) {
             return new ArrayMap<>(0);
@@ -1440,7 +1387,7 @@
             ) {
                 XmlPullParser parser = Xml.newPullParser();
                 parser.setInput(str, null);
-                parse(parser, grantExceptions);
+                parse(pm, parser, grantExceptions);
             } catch (XmlPullParserException | IOException e) {
                 Slog.w(TAG, "Error reading default permissions file " + file, e);
             }
@@ -1449,8 +1396,9 @@
         return grantExceptions;
     }
 
-    private void parse(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
-            outGrantExceptions) throws IOException, XmlPullParserException {
+    private void parse(PackageManagerWrapper pm, XmlPullParser parser,
+            Map<String, List<DefaultPermissionGrant>> outGrantExceptions)
+            throws IOException, XmlPullParserException {
         final int outerDepth = parser.getDepth();
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1459,15 +1407,16 @@
                 continue;
             }
             if (TAG_EXCEPTIONS.equals(parser.getName())) {
-                parseExceptions(parser, outGrantExceptions);
+                parseExceptions(pm, parser, outGrantExceptions);
             } else {
                 Log.e(TAG, "Unknown tag " + parser.getName());
             }
         }
     }
 
-    private void parseExceptions(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>>
-            outGrantExceptions) throws IOException, XmlPullParserException {
+    private void parseExceptions(PackageManagerWrapper pm, XmlPullParser parser,
+            Map<String, List<DefaultPermissionGrant>> outGrantExceptions)
+            throws IOException, XmlPullParserException {
         final int outerDepth = parser.getDepth();
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1482,7 +1431,7 @@
                         outGrantExceptions.get(packageName);
                 if (packageExceptions == null) {
                     // The package must be on the system image
-                    PackageInfo packageInfo = getSystemPackageInfo(packageName);
+                    PackageInfo packageInfo = pm.getSystemPackageInfo(packageName);
 
                     if (packageInfo == null) {
                         Log.w(TAG, "No such package:" + packageName);
@@ -1490,7 +1439,7 @@
                         continue;
                     }
 
-                    if (!isSystemPackage(packageInfo)) {
+                    if (!pm.isSystemPackage(packageInfo)) {
                         Log.w(TAG, "Unknown system package:" + packageName);
                         XmlUtils.skipCurrentTag(parser);
                         continue;
@@ -1549,21 +1498,349 @@
                 && pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
     }
 
-    private boolean isPermissionRestricted(String name) {
-        try {
-            return mContext.getPackageManager().getPermissionInfo(name, 0).isRestricted();
-        } catch (NameNotFoundException e) {
-            return false;
+    /**
+     * A wrapper for package manager calls done by this class
+     */
+    private abstract class PackageManagerWrapper {
+        abstract int getPermissionFlags(@NonNull String permission, @NonNull PackageInfo pkg,
+                @NonNull UserHandle user);
+
+        abstract void updatePermissionFlags(@NonNull String permission, @NonNull PackageInfo pkg,
+                int flagMask, int flagValues, @NonNull UserHandle user);
+
+        abstract void grantPermission(@NonNull String permission, @NonNull PackageInfo pkg,
+                @NonNull UserHandle user);
+
+        abstract void revokePermission(@NonNull String permission, @NonNull PackageInfo pkg,
+                @NonNull UserHandle user);
+
+        abstract boolean isGranted(@NonNull String permission, @NonNull PackageInfo pkg,
+                @NonNull UserHandle user);
+
+        abstract @Nullable PermissionInfo getPermissionInfo(@NonNull String permissionName);
+
+        abstract @Nullable PackageInfo getPackageInfo(@NonNull String pkg);
+
+        @Nullable PackageInfo getSystemPackageInfo(@NonNull String pkg) {
+            PackageInfo pi = getPackageInfo(pkg);
+            if (pi == null || !pi.applicationInfo.isSystemApp()) {
+                return null;
+            }
+            return pi;
+        }
+
+        boolean isPermissionRestricted(@NonNull String name) {
+            PermissionInfo pi = getPermissionInfo(name);
+            if (pi == null) {
+                return false;
+            }
+
+            return pi.isRestricted();
+        }
+
+        boolean isPermissionDangerous(@NonNull String name) {
+            PermissionInfo pi = getPermissionInfo(name);
+            if (pi == null) {
+                return false;
+            }
+
+            return pi.getProtection() == PermissionInfo.PROTECTION_DANGEROUS;
+        }
+
+        /**
+         * Return the background permission for a permission.
+         *
+         * @param permission The name of the foreground permission
+         *
+         * @return The name of the background permission or {@code null} if the permission has no
+         *         background permission
+         */
+        @Nullable String getBackgroundPermission(@NonNull String permission) {
+            PermissionInfo pi = getPermissionInfo(permission);
+            if (pi == null) {
+                return null;
+            }
+
+            return pi.backgroundPermission;
+        }
+
+        boolean isSystemPackage(@Nullable String packageName) {
+            return isSystemPackage(getPackageInfo(packageName));
+        }
+
+        boolean isSystemPackage(@Nullable PackageInfo pkg) {
+            if (pkg == null) {
+                return false;
+            }
+            return pkg.applicationInfo.isSystemApp()
+                    && !isSysComponentOrPersistentPlatformSignedPrivApp(pkg);
+        }
+
+        boolean isSysComponentOrPersistentPlatformSignedPrivApp(@NonNull PackageInfo pkg) {
+            if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {
+                return true;
+            }
+            if (!pkg.applicationInfo.isPrivilegedApp()) {
+                return false;
+            }
+            final PackageInfo disabledPkg = getSystemPackageInfo(
+                    mServiceInternal.getDisabledSystemPackageName(pkg.applicationInfo.packageName));
+            if (disabledPkg != null) {
+                ApplicationInfo disabledPackageAppInfo = disabledPkg.applicationInfo;
+                if (disabledPackageAppInfo != null
+                        && (disabledPackageAppInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
+                    return false;
+                }
+            } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
+                return false;
+            }
+            return mServiceInternal.isPlatformSigned(pkg.packageName);
         }
     }
 
-    private boolean isPermissionDangerous(String name) {
-        try {
-            final PermissionInfo pi = mContext.getPackageManager().getPermissionInfo(name, 0);
-            return (pi.getProtection() == PermissionInfo.PROTECTION_DANGEROUS);
-        } catch (NameNotFoundException e) {
-            // When unknown assume it's dangerous to be on the safe side
-            return true;
+    /**
+     * Do package manager calls but cache state and delay any change until {@link #apply()} is
+     * called
+     */
+    private class DelayingPackageManagerCache extends PackageManagerWrapper {
+        /** uid -> permission -> isGranted, flags */
+        private SparseArray<ArrayMap<String, PermissionState>> mDelayedPermissionState =
+                new SparseArray<>();
+        /** userId -> context */
+        private SparseArray<Context> mUserContexts = new SparseArray<>();
+        /** Permission name -> info */
+        private ArrayMap<String, PermissionInfo> mPermissionInfos = new ArrayMap<>();
+        /** Package name -> info */
+        private ArrayMap<String, PackageInfo> mPackageInfos = new ArrayMap<>();
+
+        /**
+         * Apply the cached state
+         */
+        void apply() {
+            PackageManager.corkPackageInfoCache();
+            for (int uidIdx = 0; uidIdx < mDelayedPermissionState.size(); uidIdx++) {
+                for (int permIdx = 0; permIdx < mDelayedPermissionState.valueAt(uidIdx).size();
+                        permIdx++) {
+                    try {
+                        mDelayedPermissionState.valueAt(uidIdx).valueAt(permIdx).apply();
+                    } catch (IllegalArgumentException e) {
+                        Slog.w(TAG, "Cannot set permission " + mDelayedPermissionState.valueAt(
+                                uidIdx).keyAt(permIdx) + " of uid " + mDelayedPermissionState.keyAt(
+                                uidIdx), e);
+                    }
+                }
+            }
+            PackageManager.uncorkPackageInfoCache();
+        }
+
+        void addPackageInfo(@NonNull String packageName, @NonNull PackageInfo pkg) {
+            mPackageInfos.put(packageName, pkg);
+        }
+
+        private @NonNull Context createContextAsUser(@NonNull UserHandle user) {
+            int index = mUserContexts.indexOfKey(user.getIdentifier());
+            if (index >= 0) {
+                return mUserContexts.valueAt(index);
+            }
+
+            Context uc = mContext.createContextAsUser(user, 0);
+
+            mUserContexts.put(user.getIdentifier(), uc);
+
+            return uc;
+        }
+
+        private @NonNull PermissionState getPermissionState(@NonNull String permission,
+                @NonNull PackageInfo pkg, @NonNull UserHandle user) {
+            int uid = UserHandle.getUid(user.getIdentifier(),
+                    UserHandle.getAppId(pkg.applicationInfo.uid));
+            int uidIdx = mDelayedPermissionState.indexOfKey(uid);
+
+            ArrayMap<String, PermissionState> uidState;
+            if (uidIdx >= 0) {
+                uidState = mDelayedPermissionState.valueAt(uidIdx);
+            } else {
+                uidState = new ArrayMap<>();
+                mDelayedPermissionState.put(uid, uidState);
+            }
+
+            int permIdx = uidState.indexOfKey(permission);
+
+            PermissionState permState;
+            if (permIdx >= 0) {
+                permState = uidState.valueAt(permIdx);
+            } else {
+                permState = new PermissionState(permission, pkg, user);
+                uidState.put(permission, permState);
+            }
+
+            return permState;
+        }
+
+        @Override
+        public int getPermissionFlags(@NonNull String permission, @NonNull PackageInfo pkg,
+                @NonNull UserHandle user) {
+            PermissionState state = getPermissionState(permission, pkg, user);
+            state.initFlags();
+            return state.newFlags;
+        }
+
+        @Override
+        public void updatePermissionFlags(@NonNull String permission, @NonNull PackageInfo pkg,
+                int flagMask, int flagValues, @NonNull UserHandle user) {
+            PermissionState state = getPermissionState(permission, pkg, user);
+            state.initFlags();
+            state.newFlags |= flagValues & flagMask;
+        }
+
+        @Override
+        public void grantPermission(@NonNull String permission, @NonNull PackageInfo pkg,
+                @NonNull UserHandle user) {
+            PermissionState state = getPermissionState(permission, pkg, user);
+            state.initGranted();
+            state.newGranted = true;
+        }
+
+        @Override
+        public void revokePermission(@NonNull String permission, @NonNull PackageInfo pkg,
+                @NonNull UserHandle user) {
+            PermissionState state = getPermissionState(permission, pkg, user);
+            state.initGranted();
+            state.newGranted = false;
+        }
+
+        @Override
+        public boolean isGranted(@NonNull String permission, @NonNull PackageInfo pkg,
+                @NonNull UserHandle user) {
+            PermissionState state = getPermissionState(permission, pkg, user);
+            state.initGranted();
+            return state.newGranted;
+        }
+
+        @Override
+        public @Nullable PermissionInfo getPermissionInfo(@NonNull String permissionName) {
+            int index = mPermissionInfos.indexOfKey(permissionName);
+            if (index >= 0) {
+                return mPermissionInfos.valueAt(index);
+            }
+
+            PermissionInfo pi = NO_PM_CACHE.getPermissionInfo(permissionName);
+            mPermissionInfos.put(permissionName, pi);
+
+            return pi;
+        }
+
+        @Override
+        public @Nullable PackageInfo getPackageInfo(@NonNull String pkg) {
+            int index = mPackageInfos.indexOfKey(pkg);
+            if (index >= 0) {
+                return mPackageInfos.valueAt(index);
+            }
+
+            PackageInfo pi = NO_PM_CACHE.getPackageInfo(pkg);
+            mPackageInfos.put(pkg, pi);
+
+            return pi;
+        }
+
+        /**
+         * State of a single permission belonging to a single uid
+         */
+        private class PermissionState {
+            private final @NonNull String mPermission;
+            private final @NonNull PackageInfo mPkgRequestingPerm;
+            private final @NonNull UserHandle mUser;
+
+            /** Permission flags when the state was created */
+            private @Nullable Integer mOriginalFlags;
+            /** Altered permission flags or {@code null} if no change was requested */
+            @Nullable Integer newFlags;
+
+            /** Grant state when the state was created */
+            private @Nullable Boolean mOriginalGranted;
+            /** Altered grant state or {@code null} if no change was requested */
+            @Nullable Boolean newGranted;
+
+            private PermissionState(@NonNull String permission,
+                    @NonNull PackageInfo pkgRequestingPerm, @NonNull UserHandle user) {
+                mPermission = permission;
+                mPkgRequestingPerm = pkgRequestingPerm;
+                mUser = user;
+            }
+
+            /**
+             * Apply the changes to the permission to the system
+             */
+            void apply() {
+                if (DEBUG) {
+                    Slog.i(TAG, "Granting " + mPermission + " to user " + mUser.getIdentifier()
+                            + " pkg=" + mPkgRequestingPerm.packageName + " granted=" + newGranted
+                            + " flags=" + Integer.toBinaryString(newFlags));
+                }
+
+                int flagsToAdd = 0;
+                int flagsToRemove = 0;
+                if (newFlags != null) {
+                    flagsToAdd = newFlags & ~mOriginalFlags;
+                    flagsToRemove = mOriginalFlags & ~newFlags;
+                }
+
+                // Need to remove e.g. SYSTEM_FIXED flags first as otherwise permission cannot be
+                // changed
+                if (flagsToRemove != 0) {
+                    NO_PM_CACHE.updatePermissionFlags(mPermission, mPkgRequestingPerm,
+                            flagsToRemove, 0, mUser);
+                }
+
+                // Need to unrestrict first as otherwise permission grants might fail
+                if ((flagsToAdd & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0) {
+                    int newRestrictionExcemptFlags =
+                            flagsToAdd & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
+
+                    NO_PM_CACHE.updatePermissionFlags(mPermission,
+                            mPkgRequestingPerm, newRestrictionExcemptFlags, -1, mUser);
+                }
+
+                if (newGranted != null && newGranted != mOriginalGranted) {
+                    if (newGranted) {
+                        NO_PM_CACHE.grantPermission(mPermission, mPkgRequestingPerm, mUser);
+                    } else {
+                        NO_PM_CACHE.revokePermission(mPermission, mPkgRequestingPerm, mUser);
+                    }
+                }
+
+                if ((flagsToAdd & ~PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0) {
+                    int newFlags =
+                            flagsToAdd & ~PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
+
+                    NO_PM_CACHE.updatePermissionFlags(mPermission, mPkgRequestingPerm, newFlags,
+                            -1, mUser);
+                }
+            }
+
+            /**
+             * Load the state of the flags before first use
+             */
+            void initFlags() {
+                if (newFlags == null) {
+                    mOriginalFlags = NO_PM_CACHE.getPermissionFlags(mPermission, mPkgRequestingPerm,
+                            mUser);
+                    newFlags = mOriginalFlags;
+                }
+            }
+
+            /**
+             * Load the grant state before first use
+             */
+            void initGranted() {
+                if (newGranted == null) {
+                    // Don't call NO_PM_CACHE here so that contexts are reused
+                    mOriginalGranted = createContextAsUser(mUser).getPackageManager()
+                            .checkPermission(mPermission, mPkgRequestingPerm.packageName)
+                            == PackageManager.PERMISSION_GRANTED;
+                    newGranted = mOriginalGranted;
+                }
+            }
         }
     }
 
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 a18f90b..45ebbb6 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -388,7 +388,7 @@
         Watchdog.getInstance().addThread(mHandler);
 
         mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy(
-                context, mHandlerThread.getLooper(), this);
+                context, mHandlerThread.getLooper());
         SystemConfig systemConfig = SystemConfig.getInstance();
         mSystemPermissions = systemConfig.getSystemPermissions();
         mGlobalGids = systemConfig.getGlobalGids();
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5df84f2..5f8e9ff 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1192,8 +1192,9 @@
         final Configuration newConfig = new Configuration();
         newConfig.setTo(task.getRequestedOverrideConfiguration());
         Rect outBounds = newConfig.windowConfiguration.getBounds();
-        task.adjustForMinimalTaskDimensions(outBounds, outBounds);
-        task.computeConfigResourceOverrides(newConfig, task.getParent().getConfiguration());
+        final Configuration parentConfig = task.getParent().getConfiguration();
+        task.adjustForMinimalTaskDimensions(outBounds, outBounds, parentConfig);
+        task.computeConfigResourceOverrides(newConfig, parentConfig);
     }
 
     Task getTask() {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index d71381e..df1f174 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -2957,8 +2957,8 @@
             final int taskId = activity != null
                     ? mStackSupervisor.getNextTaskIdForUser(activity.mUserId)
                     : mStackSupervisor.getNextTaskIdForUser();
-            task = Task.create(
-                    mAtmService, taskId, info, intent, voiceSession, voiceInteractor, this);
+            task = new ActivityStack(mAtmService, taskId, info, intent, voiceSession,
+                    voiceInteractor, null /* taskDescription */, this);
 
             // add the task to stack first, mTaskPositioner might need the stack association
             addChild(task, toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9868446..72e3838 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1393,6 +1393,13 @@
         final WindowContainer orientationSource = getLastOrientationSource();
         final ActivityRecord r =
                 orientationSource != null ? orientationSource.asActivityRecord() : null;
+        if (r != null && r.getTask() != null
+                && orientation != r.getTask().mLastReportedRequestedOrientation) {
+            final Task task = r.getTask();
+            task.mLastReportedRequestedOrientation = orientation;
+            mAtmService.getTaskChangeNotificationController()
+                    .notifyTaskRequestedOrientationChanged(task.mTaskId, orientation);
+        }
         // Currently there is no use case from non-activity.
         if (r != null && handleTopActivityLaunchingInDifferentOrientation(r)) {
             // Display orientation should be deferred until the top fixed rotation is finished.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 6c872a6..8903609 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -232,11 +232,6 @@
     // Do not move the stack as a part of reparenting
     static final int REPARENT_LEAVE_STACK_IN_PLACE = 2;
 
-    /**
-     * The factory used to create {@link Task}. This allows OEM subclass {@link Task}.
-     */
-    private static TaskFactory sTaskFactory;
-
     String affinity;        // The affinity name for this task, or null; may change identity.
     String rootAffinity;    // Initial base affinity, or null; does not change from initial root.
     String mWindowLayoutAffinity; // Launch param affinity of this task or null. Used when saving
@@ -368,6 +363,14 @@
     @Surface.Rotation
     private int mRotation;
 
+    /**
+     * Last requested orientation reported to DisplayContent. This is different from {@link
+     * #mOrientation} in the sense that this takes activities' requested orientation into
+     * account. Start with {@link ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} so that we don't need
+     * to notify for activities that don't specify any orientation.
+     */
+    int mLastReportedRequestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
     // For comparison with DisplayContent bounds.
     private Rect mTmpRect = new Rect();
     // For handling display rotations.
@@ -1822,22 +1825,16 @@
         }
     }
 
-    void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) {
-        final Rect parentBounds = getParent() != null ? getParent().getBounds() : null;
-        if (bounds == null
-                || (bounds.isEmpty() && (parentBounds == null || parentBounds.isEmpty()))) {
-            return;
-        }
+    void adjustForMinimalTaskDimensions(@NonNull Rect bounds, @NonNull Rect previousBounds,
+            @NonNull Configuration parentConfig) {
         int minWidth = mMinWidth;
         int minHeight = mMinHeight;
         // If the task has no requested minimal size, we'd like to enforce a minimal size
         // so that the user can not render the task too small to manipulate. We don't need
         // to do this for the pinned stack as the bounds are controlled by the system.
-        if (!inPinnedWindowingMode() && getStack() != null) {
+        if (!inPinnedWindowingMode()) {
             final int defaultMinSizeDp = mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp;
-            final DisplayContent display = getDisplayContent();
-            final float density =
-                    (float) display.getConfiguration().densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+            final float density = (float) parentConfig.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
             final int defaultMinSize = (int) (defaultMinSizeDp * density);
 
             if (minWidth == INVALID_MIN_SIZE) {
@@ -1850,6 +1847,7 @@
         if (bounds.isEmpty()) {
             // If inheriting parent bounds, check if parent bounds adhere to minimum size. If they
             // do, we can just skip.
+            final Rect parentBounds = parentConfig.windowConfiguration.getBounds();
             if (parentBounds.width() >= minWidth && parentBounds.height() >= minHeight) {
                 return;
             }
@@ -2444,12 +2442,13 @@
         }
 
         if (isLeafTask()) {
-            resolveLeafOnlyOverrideConfigs(newParentConfig);
+            resolveLeafOnlyOverrideConfigs(newParentConfig, mTmpBounds /* previousBounds */);
         }
         computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
     }
 
-    void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig) {
+    private void resolveLeafOnlyOverrideConfigs(Configuration newParentConfig,
+            Rect previousBounds) {
         int windowingMode =
                 getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
         if (windowingMode == WINDOWING_MODE_UNDEFINED) {
@@ -2462,9 +2461,12 @@
             computeFullscreenBounds(outOverrideBounds, null /* refActivity */,
                     newParentConfig.windowConfiguration.getBounds(),
                     newParentConfig.orientation);
+            // The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if
+            // the parent or display is smaller than the size, the content may be cropped.
+            return;
         }
 
-        adjustForMinimalTaskDimensions(outOverrideBounds, mTmpBounds);
+        adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig);
         if (windowingMode == WINDOWING_MODE_FREEFORM) {
             // by policy, make sure the window remains within parent somewhere
             final float density =
@@ -4062,316 +4064,239 @@
         }
     }
 
-    @VisibleForTesting
-    static TaskFactory getTaskFactory() {
-        if (sTaskFactory == null) {
-            setTaskFactory(new TaskFactory());
-        }
-        return sTaskFactory;
-    }
-
-    static void setTaskFactory(TaskFactory factory) {
-        sTaskFactory = factory;
-    }
-
-    static Task create(ActivityTaskManagerService service, int taskId, int activityType,
-            ActivityInfo info, Intent intent, boolean createdByOrganizer) {
-        return getTaskFactory().create(service, taskId, activityType, info, intent,
-                createdByOrganizer);
-    }
-
-    static Task create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
-            Intent intent, IVoiceInteractionSession voiceSession,
-            IVoiceInteractor voiceInteractor, ActivityStack stack) {
-        return getTaskFactory().create(
-                service, taskId, info, intent, voiceSession, voiceInteractor, stack);
-    }
-
     static Task restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
             throws IOException, XmlPullParserException {
-        return getTaskFactory().restoreFromXml(in, stackSupervisor);
-    }
+        Intent intent = null;
+        Intent affinityIntent = null;
+        ArrayList<ActivityRecord> activities = new ArrayList<>();
+        ComponentName realActivity = null;
+        boolean realActivitySuspended = false;
+        ComponentName origActivity = null;
+        String affinity = null;
+        String rootAffinity = null;
+        boolean hasRootAffinity = false;
+        String windowLayoutAffinity = null;
+        boolean rootHasReset = false;
+        boolean autoRemoveRecents = false;
+        boolean askedCompatMode = false;
+        int taskType = 0;
+        int userId = 0;
+        boolean userSetupComplete = true;
+        int effectiveUid = -1;
+        String lastDescription = null;
+        long lastTimeOnTop = 0;
+        boolean neverRelinquishIdentity = true;
+        int taskId = INVALID_TASK_ID;
+        final int outerDepth = in.getDepth();
+        TaskDescription taskDescription = new TaskDescription();
+        int taskAffiliation = INVALID_TASK_ID;
+        int taskAffiliationColor = 0;
+        int prevTaskId = INVALID_TASK_ID;
+        int nextTaskId = INVALID_TASK_ID;
+        int callingUid = -1;
+        String callingPackage = "";
+        String callingFeatureId = null;
+        int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
+        boolean supportsPictureInPicture = false;
+        Rect lastNonFullscreenBounds = null;
+        int minWidth = INVALID_MIN_SIZE;
+        int minHeight = INVALID_MIN_SIZE;
+        int persistTaskVersion = 0;
 
-    /**
-     * A factory class used to create {@link Task} or its subclass if any. This can be
-     * specified when system boots by setting it with
-     * {@link #setTaskFactory(TaskFactory)}.
-     */
-    static class TaskFactory {
-        Task create(ActivityTaskManagerService service, int taskId, int activityType,
-                ActivityInfo info, Intent intent, boolean createdByOrganizer) {
-            return new ActivityStack(service, taskId, activityType, info, intent,
-                    createdByOrganizer);
-        }
-
-        Task create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
-                Intent intent, IVoiceInteractionSession voiceSession,
-                IVoiceInteractor voiceInteractor, ActivityStack stack) {
-            return new ActivityStack(service, taskId, info, intent, voiceSession, voiceInteractor,
-                    null /*taskDescription*/, stack);
-        }
-
-        /**
-         * Should only be used when we're restoring {@link Task} from storage.
-         */
-        Task create(ActivityTaskManagerService service, int taskId, Intent intent,
-                Intent affinityIntent, String affinity, String rootAffinity,
-                ComponentName realActivity, ComponentName origActivity, boolean rootWasReset,
-                boolean autoRemoveRecents, boolean askedCompatMode, int userId,
-                int effectiveUid, String lastDescription,
-                long lastTimeMoved, boolean neverRelinquishIdentity,
-                TaskDescription lastTaskDescription, int taskAffiliation, int prevTaskId,
-                int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
-                @Nullable String callingFeatureId, int resizeMode,
-                boolean supportsPictureInPicture, boolean realActivitySuspended,
-                boolean userSetupComplete, int minWidth, int minHeight, ActivityStack stack) {
-            return new ActivityStack(service, taskId, intent, affinityIntent, affinity,
-                    rootAffinity, realActivity, origActivity, rootWasReset, autoRemoveRecents,
-                    askedCompatMode, userId, effectiveUid, lastDescription,
-                    lastTimeMoved, neverRelinquishIdentity, lastTaskDescription, taskAffiliation,
-                    prevTaskId, nextTaskId, taskAffiliationColor, callingUid, callingPackage,
-                    callingFeatureId, resizeMode, supportsPictureInPicture, realActivitySuspended,
-                    userSetupComplete, minWidth, minHeight, null /*ActivityInfo*/,
-                    null /*_voiceSession*/, null /*_voiceInteractor*/, stack);
-        }
-
-        Task restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
-                throws IOException, XmlPullParserException {
-            Intent intent = null;
-            Intent affinityIntent = null;
-            ArrayList<ActivityRecord> activities = new ArrayList<>();
-            ComponentName realActivity = null;
-            boolean realActivitySuspended = false;
-            ComponentName origActivity = null;
-            String affinity = null;
-            String rootAffinity = null;
-            boolean hasRootAffinity = false;
-            String windowLayoutAffinity = null;
-            boolean rootHasReset = false;
-            boolean autoRemoveRecents = false;
-            boolean askedCompatMode = false;
-            int taskType = 0;
-            int userId = 0;
-            boolean userSetupComplete = true;
-            int effectiveUid = -1;
-            String lastDescription = null;
-            long lastTimeOnTop = 0;
-            boolean neverRelinquishIdentity = true;
-            int taskId = INVALID_TASK_ID;
-            final int outerDepth = in.getDepth();
-            TaskDescription taskDescription = new TaskDescription();
-            int taskAffiliation = INVALID_TASK_ID;
-            int taskAffiliationColor = 0;
-            int prevTaskId = INVALID_TASK_ID;
-            int nextTaskId = INVALID_TASK_ID;
-            int callingUid = -1;
-            String callingPackage = "";
-            String callingFeatureId = null;
-            int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
-            boolean supportsPictureInPicture = false;
-            Rect lastNonFullscreenBounds = null;
-            int minWidth = INVALID_MIN_SIZE;
-            int minHeight = INVALID_MIN_SIZE;
-            int persistTaskVersion = 0;
-
-            for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
-                final String attrName = in.getAttributeName(attrNdx);
-                final String attrValue = in.getAttributeValue(attrNdx);
-                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "Task: attribute name="
-                        + attrName + " value=" + attrValue);
-                switch (attrName) {
-                    case ATTR_TASKID:
-                        if (taskId == INVALID_TASK_ID) taskId = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_REALACTIVITY:
-                        realActivity = ComponentName.unflattenFromString(attrValue);
-                        break;
-                    case ATTR_REALACTIVITY_SUSPENDED:
-                        realActivitySuspended = Boolean.valueOf(attrValue);
-                        break;
-                    case ATTR_ORIGACTIVITY:
-                        origActivity = ComponentName.unflattenFromString(attrValue);
-                        break;
-                    case ATTR_AFFINITY:
-                        affinity = attrValue;
-                        break;
-                    case ATTR_ROOT_AFFINITY:
-                        rootAffinity = attrValue;
-                        hasRootAffinity = true;
-                        break;
-                    case ATTR_WINDOW_LAYOUT_AFFINITY:
-                        windowLayoutAffinity = attrValue;
-                        break;
-                    case ATTR_ROOTHASRESET:
-                        rootHasReset = Boolean.parseBoolean(attrValue);
-                        break;
-                    case ATTR_AUTOREMOVERECENTS:
-                        autoRemoveRecents = Boolean.parseBoolean(attrValue);
-                        break;
-                    case ATTR_ASKEDCOMPATMODE:
-                        askedCompatMode = Boolean.parseBoolean(attrValue);
-                        break;
-                    case ATTR_USERID:
-                        userId = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_USER_SETUP_COMPLETE:
-                        userSetupComplete = Boolean.parseBoolean(attrValue);
-                        break;
-                    case ATTR_EFFECTIVE_UID:
-                        effectiveUid = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_TASKTYPE:
-                        taskType = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_LASTDESCRIPTION:
-                        lastDescription = attrValue;
-                        break;
-                    case ATTR_LASTTIMEMOVED:
-                        lastTimeOnTop = Long.parseLong(attrValue);
-                        break;
-                    case ATTR_NEVERRELINQUISH:
-                        neverRelinquishIdentity = Boolean.parseBoolean(attrValue);
-                        break;
-                    case ATTR_TASK_AFFILIATION:
-                        taskAffiliation = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_PREV_AFFILIATION:
-                        prevTaskId = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_NEXT_AFFILIATION:
-                        nextTaskId = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_TASK_AFFILIATION_COLOR:
-                        taskAffiliationColor = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_CALLING_UID:
-                        callingUid = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_CALLING_PACKAGE:
-                        callingPackage = attrValue;
-                        break;
-                    case ATTR_CALLING_FEATURE_ID:
-                        callingFeatureId = attrValue;
-                        break;
-                    case ATTR_RESIZE_MODE:
-                        resizeMode = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_SUPPORTS_PICTURE_IN_PICTURE:
-                        supportsPictureInPicture = Boolean.parseBoolean(attrValue);
-                        break;
-                    case ATTR_NON_FULLSCREEN_BOUNDS:
-                        lastNonFullscreenBounds = Rect.unflattenFromString(attrValue);
-                        break;
-                    case ATTR_MIN_WIDTH:
-                        minWidth = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_MIN_HEIGHT:
-                        minHeight = Integer.parseInt(attrValue);
-                        break;
-                    case ATTR_PERSIST_TASK_VERSION:
-                        persistTaskVersion = Integer.parseInt(attrValue);
-                        break;
-                    default:
-                        if (!attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
-                            Slog.w(TAG, "Task: Unknown attribute=" + attrName);
-                        }
-                }
+        for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
+            final String attrName = in.getAttributeName(attrNdx);
+            final String attrValue = in.getAttributeValue(attrNdx);
+            if (TaskPersister.DEBUG) {
+                Slog.d(TaskPersister.TAG, "Task: attribute name=" + attrName + " value="
+                        + attrValue);
             }
-            taskDescription.restoreFromXml(in);
-
-            int event;
-            while (((event = in.next()) != XmlPullParser.END_DOCUMENT)
-                    && (event != XmlPullParser.END_TAG || in.getDepth() >= outerDepth)) {
-                if (event == XmlPullParser.START_TAG) {
-                    final String name = in.getName();
-                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
-                            "Task: START_TAG name=" + name);
-                    if (TAG_AFFINITYINTENT.equals(name)) {
-                        affinityIntent = Intent.restoreFromXml(in);
-                    } else if (TAG_INTENT.equals(name)) {
-                        intent = Intent.restoreFromXml(in);
-                    } else if (TAG_ACTIVITY.equals(name)) {
-                        ActivityRecord activity =
-                                ActivityRecord.restoreFromXml(in, stackSupervisor);
-                        if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "Task: activity="
-                                + activity);
-                        if (activity != null) {
-                            activities.add(activity);
-                        }
-                    } else {
-                        handleUnknownTag(name, in);
+            switch (attrName) {
+                case ATTR_TASKID:
+                    if (taskId == INVALID_TASK_ID) taskId = Integer.parseInt(attrValue);
+                    break;
+                case ATTR_REALACTIVITY:
+                    realActivity = ComponentName.unflattenFromString(attrValue);
+                    break;
+                case ATTR_REALACTIVITY_SUSPENDED:
+                    realActivitySuspended = Boolean.valueOf(attrValue);
+                    break;
+                case ATTR_ORIGACTIVITY:
+                    origActivity = ComponentName.unflattenFromString(attrValue);
+                    break;
+                case ATTR_AFFINITY:
+                    affinity = attrValue;
+                    break;
+                case ATTR_ROOT_AFFINITY:
+                    rootAffinity = attrValue;
+                    hasRootAffinity = true;
+                    break;
+                case ATTR_WINDOW_LAYOUT_AFFINITY:
+                    windowLayoutAffinity = attrValue;
+                    break;
+                case ATTR_ROOTHASRESET:
+                    rootHasReset = Boolean.parseBoolean(attrValue);
+                    break;
+                case ATTR_AUTOREMOVERECENTS:
+                    autoRemoveRecents = Boolean.parseBoolean(attrValue);
+                    break;
+                case ATTR_ASKEDCOMPATMODE:
+                    askedCompatMode = Boolean.parseBoolean(attrValue);
+                    break;
+                case ATTR_USERID:
+                    userId = Integer.parseInt(attrValue);
+                    break;
+                case ATTR_USER_SETUP_COMPLETE:
+                    userSetupComplete = Boolean.parseBoolean(attrValue);
+                    break;
+                case ATTR_EFFECTIVE_UID:
+                    effectiveUid = Integer.parseInt(attrValue);
+                    break;
+                case ATTR_TASKTYPE:
+                    taskType = Integer.parseInt(attrValue);
+                    break;
+                case ATTR_LASTDESCRIPTION:
+                    lastDescription = attrValue;
+                    break;
+                case ATTR_LASTTIMEMOVED:
+                    lastTimeOnTop = Long.parseLong(attrValue);
+                    break;
+                case ATTR_NEVERRELINQUISH:
+                    neverRelinquishIdentity = Boolean.parseBoolean(attrValue);
+                    break;
+                case ATTR_TASK_AFFILIATION:
+                    taskAffiliation = Integer.parseInt(attrValue);
+                    break;
+                case ATTR_PREV_AFFILIATION:
+                    prevTaskId = Integer.parseInt(attrValue);
+                    break;
+                case ATTR_NEXT_AFFILIATION:
+                    nextTaskId = Integer.parseInt(attrValue);
+                    break;
+                case ATTR_TASK_AFFILIATION_COLOR:
+                    taskAffiliationColor = Integer.parseInt(attrValue);
+                    break;
+                case ATTR_CALLING_UID:
+                    callingUid = Integer.parseInt(attrValue);
+                    break;
+                case ATTR_CALLING_PACKAGE:
+                    callingPackage = attrValue;
+                    break;
+                case ATTR_CALLING_FEATURE_ID:
+                    callingFeatureId = attrValue;
+                    break;
+                case ATTR_RESIZE_MODE:
+                    resizeMode = Integer.parseInt(attrValue);
+                    break;
+                case ATTR_SUPPORTS_PICTURE_IN_PICTURE:
+                    supportsPictureInPicture = Boolean.parseBoolean(attrValue);
+                    break;
+                case ATTR_NON_FULLSCREEN_BOUNDS:
+                    lastNonFullscreenBounds = Rect.unflattenFromString(attrValue);
+                    break;
+                case ATTR_MIN_WIDTH:
+                    minWidth = Integer.parseInt(attrValue);
+                    break;
+                case ATTR_MIN_HEIGHT:
+                    minHeight = Integer.parseInt(attrValue);
+                    break;
+                case ATTR_PERSIST_TASK_VERSION:
+                    persistTaskVersion = Integer.parseInt(attrValue);
+                    break;
+                default:
+                    if (!attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
+                        Slog.w(TAG, "Task: Unknown attribute=" + attrName);
                     }
-                }
             }
-            if (!hasRootAffinity) {
-                rootAffinity = affinity;
-            } else if ("@".equals(rootAffinity)) {
-                rootAffinity = null;
-            }
-            if (effectiveUid <= 0) {
-                Intent checkIntent = intent != null ? intent : affinityIntent;
-                effectiveUid = 0;
-                if (checkIntent != null) {
-                    IPackageManager pm = AppGlobals.getPackageManager();
-                    try {
-                        ApplicationInfo ai = pm.getApplicationInfo(
-                                checkIntent.getComponent().getPackageName(),
-                                PackageManager.MATCH_UNINSTALLED_PACKAGES
-                                        | PackageManager.MATCH_DISABLED_COMPONENTS, userId);
-                        if (ai != null) {
-                            effectiveUid = ai.uid;
-                        }
-                    } catch (RemoteException e) {
+        }
+        taskDescription.restoreFromXml(in);
+
+        int event;
+        while (((event = in.next()) != XmlPullParser.END_DOCUMENT)
+                && (event != XmlPullParser.END_TAG || in.getDepth() >= outerDepth)) {
+            if (event == XmlPullParser.START_TAG) {
+                final String name = in.getName();
+                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "Task: START_TAG name=" + name);
+                if (TAG_AFFINITYINTENT.equals(name)) {
+                    affinityIntent = Intent.restoreFromXml(in);
+                } else if (TAG_INTENT.equals(name)) {
+                    intent = Intent.restoreFromXml(in);
+                } else if (TAG_ACTIVITY.equals(name)) {
+                    ActivityRecord activity =
+                            ActivityRecord.restoreFromXml(in, stackSupervisor);
+                    if (TaskPersister.DEBUG) {
+                        Slog.d(TaskPersister.TAG, "Task: activity=" + activity);
                     }
-                }
-                Slog.w(TAG, "Updating task #" + taskId + " for " + checkIntent
-                        + ": effectiveUid=" + effectiveUid);
-            }
-
-            if (persistTaskVersion < 1) {
-                // We need to convert the resize mode of home activities saved before version one if
-                // they are marked as RESIZE_MODE_RESIZEABLE to
-                // RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION since we didn't have that differentiation
-                // before version 1 and the system didn't resize home activities before then.
-                if (taskType == 1 /* old home type */ && resizeMode == RESIZE_MODE_RESIZEABLE) {
-                    resizeMode = RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
-                }
-            } else {
-                // This activity has previously marked itself explicitly as both resizeable and
-                // supporting picture-in-picture.  Since there is no longer a requirement for
-                // picture-in-picture activities to be resizeable, we can mark this simply as
-                // resizeable and supporting picture-in-picture separately.
-                if (resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED) {
-                    resizeMode = RESIZE_MODE_RESIZEABLE;
-                    supportsPictureInPicture = true;
+                    if (activity != null) {
+                        activities.add(activity);
+                    }
+                } else {
+                    Slog.e(TAG, "restoreTask: Unexpected name=" + name);
+                    XmlUtils.skipCurrentTag(in);
                 }
             }
-
-            final Task task = create(stackSupervisor.mService,
-                    taskId, intent, affinityIntent,
-                    affinity, rootAffinity, realActivity, origActivity, rootHasReset,
-                    autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
-                    lastTimeOnTop, neverRelinquishIdentity, taskDescription,
-                    taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid,
-                    callingPackage, callingFeatureId, resizeMode, supportsPictureInPicture,
-                    realActivitySuspended, userSetupComplete, minWidth, minHeight, null /*stack*/);
-            task.mLastNonFullscreenBounds = lastNonFullscreenBounds;
-            task.setBounds(lastNonFullscreenBounds);
-            task.mWindowLayoutAffinity = windowLayoutAffinity;
-
-            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                task.addChild(activities.get(activityNdx));
+        }
+        if (!hasRootAffinity) {
+            rootAffinity = affinity;
+        } else if ("@".equals(rootAffinity)) {
+            rootAffinity = null;
+        }
+        if (effectiveUid <= 0) {
+            Intent checkIntent = intent != null ? intent : affinityIntent;
+            effectiveUid = 0;
+            if (checkIntent != null) {
+                IPackageManager pm = AppGlobals.getPackageManager();
+                try {
+                    ApplicationInfo ai = pm.getApplicationInfo(
+                            checkIntent.getComponent().getPackageName(),
+                            PackageManager.MATCH_UNINSTALLED_PACKAGES
+                                    | PackageManager.MATCH_DISABLED_COMPONENTS, userId);
+                    if (ai != null) {
+                        effectiveUid = ai.uid;
+                    }
+                } catch (RemoteException e) {
+                }
             }
-
-            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task);
-            return task;
+            Slog.w(TAG, "Updating task #" + taskId + " for " + checkIntent
+                    + ": effectiveUid=" + effectiveUid);
         }
 
-        void handleUnknownTag(String name, XmlPullParser in)
-                throws IOException, XmlPullParserException {
-            Slog.e(TAG, "restoreTask: Unexpected name=" + name);
-            XmlUtils.skipCurrentTag(in);
+        if (persistTaskVersion < 1) {
+            // We need to convert the resize mode of home activities saved before version one if
+            // they are marked as RESIZE_MODE_RESIZEABLE to
+            // RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION since we didn't have that differentiation
+            // before version 1 and the system didn't resize home activities before then.
+            if (taskType == 1 /* old home type */ && resizeMode == RESIZE_MODE_RESIZEABLE) {
+                resizeMode = RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+            }
+        } else {
+            // This activity has previously marked itself explicitly as both resizeable and
+            // supporting picture-in-picture.  Since there is no longer a requirement for
+            // picture-in-picture activities to be resizeable, we can mark this simply as
+            // resizeable and supporting picture-in-picture separately.
+            if (resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED) {
+                resizeMode = RESIZE_MODE_RESIZEABLE;
+                supportsPictureInPicture = true;
+            }
         }
+
+        final Task task = new ActivityStack(stackSupervisor.mService, taskId, intent,
+                affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
+                autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
+                lastTimeOnTop, neverRelinquishIdentity, taskDescription, taskAffiliation,
+                prevTaskId, nextTaskId, taskAffiliationColor, callingUid, callingPackage,
+                callingFeatureId, resizeMode, supportsPictureInPicture, realActivitySuspended,
+                userSetupComplete, minWidth, minHeight, null /*ActivityInfo*/,
+                null /*_voiceSession*/, null /*_voiceInteractor*/, null /* stack */);
+        task.mLastNonFullscreenBounds = lastNonFullscreenBounds;
+        task.setBounds(lastNonFullscreenBounds);
+        task.mWindowLayoutAffinity = windowLayoutAffinity;
+
+        for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+            task.addChild(activities.get(activityNdx));
+        }
+
+        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task);
+        return task;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 4b0e293..df0fa9c 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -60,6 +60,7 @@
     private static final int NOTIFY_SINGLE_TASK_DISPLAY_EMPTY = 25;
     private static final int NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG = 26;
     private static final int NOTIFY_TASK_FOCUS_CHANGED_MSG = 27;
+    private static final int NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG = 28;
 
     // Delay in notifying task stack change listeners (in millis)
     private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -178,6 +179,10 @@
         l.onTaskFocusChanged(m.arg1, m.arg2 != 0);
     };
 
+    private final TaskStackConsumer mNotifyTaskRequestedOrientationChanged = (l, m) -> {
+        l.onTaskRequestedOrientationChanged(m.arg1, m.arg2);
+    };
+
     @FunctionalInterface
     public interface TaskStackConsumer {
         void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -269,6 +274,9 @@
                 case NOTIFY_TASK_FOCUS_CHANGED_MSG:
                     forAllRemoteListeners(mNotifyTaskFocusChanged, msg);
                     break;
+                case NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG:
+                    forAllRemoteListeners(mNotifyTaskRequestedOrientationChanged, msg);
+                    break;
             }
             if (msg.obj instanceof SomeArgs) {
                 ((SomeArgs) msg.obj).recycle();
@@ -558,4 +566,12 @@
         forAllLocalListeners(mNotifyTaskFocusChanged, msg);
         msg.sendToTarget();
     }
+
+    /** @see android.app.ITaskStackListener#onTaskRequestedOrientationChanged(int, int) */
+    void notifyTaskRequestedOrientationChanged(int taskId, int requestedOrientation) {
+        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_REQUESTED_ORIENTATION_CHANGED_MSG,
+                taskId, requestedOrientation);
+        forAllLocalListeners(mNotifyTaskRequestedOrientationChanged, msg);
+        msg.sendToTarget();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 3dc6723..75295e6 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -959,7 +959,7 @@
             windowingMode = WINDOWING_MODE_UNDEFINED;
         }
 
-        final ActivityStack stack = (ActivityStack) Task.create(mAtmService, stackId, activityType,
+        final ActivityStack stack = new ActivityStack(mAtmService, stackId, activityType,
                 info, intent, createdByOrganizer);
         if (launchRootTask != null) {
             launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java
index 27fa36b..dc3fa2a 100644
--- a/services/people/java/com/android/server/people/data/ConversationInfo.java
+++ b/services/people/java/com/android/server/people/data/ConversationInfo.java
@@ -62,8 +62,6 @@
 
     private static final int FLAG_DEMOTED = 1 << 6;
 
-    private static final int FLAG_NOTIFICATION_SETTING_CHANGED = 1 << 7;
-
     @IntDef(flag = true, prefix = {"FLAG_"}, value = {
             FLAG_IMPORTANT,
             FLAG_NOTIFICATION_SILENCED,
@@ -72,7 +70,6 @@
             FLAG_PERSON_BOT,
             FLAG_CONTACT_STARRED,
             FLAG_DEMOTED,
-            FLAG_NOTIFICATION_SETTING_CHANGED,
     })
     @Retention(RetentionPolicy.SOURCE)
     private @interface ConversationFlags {
@@ -188,11 +185,6 @@
         return hasConversationFlags(FLAG_CONTACT_STARRED);
     }
 
-    /** Whether the conversation's notification setting has ever been changed by the user. */
-    boolean isNotificationSettingChanged() {
-        return hasConversationFlags(FLAG_NOTIFICATION_SETTING_CHANGED);
-    }
-
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
@@ -499,10 +491,6 @@
             return setConversationFlag(FLAG_CONTACT_STARRED, value);
         }
 
-        Builder setNotificationSettingChanged(boolean value) {
-            return setConversationFlag(FLAG_NOTIFICATION_SETTING_CHANGED, value);
-        }
-
         private Builder setConversationFlag(@ConversationFlags int flags, boolean value) {
             if (value) {
                 return addConversationFlags(flags);
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 8e1141d..d9ca415 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -16,8 +16,6 @@
 
 package com.android.server.people.data;
 
-import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -60,9 +58,11 @@
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ChooserActivity;
 import com.android.internal.content.PackageMonitor;
@@ -106,8 +106,7 @@
     private final SparseArray<BroadcastReceiver> mBroadcastReceivers = new SparseArray<>();
     private final SparseArray<ContentObserver> mContactsContentObservers = new SparseArray<>();
     private final SparseArray<ScheduledFuture<?>> mUsageStatsQueryFutures = new SparseArray<>();
-    private final SparseArray<NotificationListenerService> mNotificationListeners =
-            new SparseArray<>();
+    private final SparseArray<NotificationListener> mNotificationListeners = new SparseArray<>();
     private final SparseArray<PackageMonitor> mPackageMonitors = new SparseArray<>();
     private ContentObserver mCallLogContentObserver;
     private ContentObserver mMmsSmsContentObserver;
@@ -272,6 +271,7 @@
         }
         pruneUninstalledPackageData(userData);
 
+        final NotificationListener notificationListener = mNotificationListeners.get(userId);
         userData.forAllPackages(packageData -> {
             if (signal.isCanceled()) {
                 return;
@@ -284,6 +284,20 @@
                 packageData.getEventStore().deleteEventHistories(EventStore.CATEGORY_SMS);
             }
             packageData.pruneOrphanEvents();
+            if (notificationListener != null) {
+                String packageName = packageData.getPackageName();
+                packageData.forAllConversations(conversationInfo -> {
+                    if (conversationInfo.isShortcutCached()
+                            && conversationInfo.getNotificationChannelId() == null
+                            && !notificationListener.hasActiveNotifications(
+                                    packageName, conversationInfo.getShortcutId())) {
+                        mShortcutServiceInternal.uncacheShortcuts(userId,
+                                mContext.getPackageName(), packageName,
+                                Collections.singletonList(conversationInfo.getShortcutId()),
+                                userId);
+                    }
+                });
+            }
         });
     }
 
@@ -337,7 +351,7 @@
                     Contacts.CONTENT_URI, /* notifyForDescendants= */ true,
                     contactsContentObserver, userId);
 
-            NotificationListener notificationListener = new NotificationListener();
+            NotificationListener notificationListener = new NotificationListener(userId);
             mNotificationListeners.put(userId, notificationListener);
             try {
                 notificationListener.registerAsSystemService(mContext,
@@ -753,14 +767,27 @@
     /** Listener for the notifications and their settings changes. */
     private class NotificationListener extends NotificationListenerService {
 
-        // Conversation shortcut ID -> Number of active notifications
-        private final Map<String, Integer> mActiveNotifCounts = new ArrayMap<>();
+        private final int mUserId;
+
+        // Conversation package name + shortcut ID -> Number of active notifications
+        @GuardedBy("this")
+        private final Map<Pair<String, String>, Integer> mActiveNotifCounts = new ArrayMap<>();
+
+        private NotificationListener(int userId) {
+            mUserId = userId;
+        }
 
         @Override
         public void onNotificationPosted(StatusBarNotification sbn) {
+            if (sbn.getUser().getIdentifier() != mUserId) {
+                return;
+            }
             String shortcutId = sbn.getNotification().getShortcutId();
             PackageData packageData = getPackageIfConversationExists(sbn, conversationInfo -> {
-                mActiveNotifCounts.merge(shortcutId, 1, Integer::sum);
+                synchronized (this) {
+                    mActiveNotifCounts.merge(
+                            Pair.create(sbn.getPackageName(), shortcutId), 1, Integer::sum);
+                }
             });
 
             if (packageData != null) {
@@ -771,26 +798,32 @@
         }
 
         @Override
-        public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
-                int reason) {
+        public synchronized void onNotificationRemoved(StatusBarNotification sbn,
+                RankingMap rankingMap, int reason) {
+            if (sbn.getUser().getIdentifier() != mUserId) {
+                return;
+            }
             String shortcutId = sbn.getNotification().getShortcutId();
             PackageData packageData = getPackageIfConversationExists(sbn, conversationInfo -> {
-                int count = mActiveNotifCounts.getOrDefault(shortcutId, 0) - 1;
-                if (count <= 0) {
-                    mActiveNotifCounts.remove(sbn.getNotification().getShortcutId());
-                    // The shortcut was cached by Notification Manager synchronously when the
-                    // associated notification was posted. Uncache it here when all the associated
-                    // notifications are removed.
-                    if (conversationInfo.isShortcutCached()
-                            && !conversationInfo.isNotificationSettingChanged()) {
-                        int userId = sbn.getUser().getIdentifier();
-                        mShortcutServiceInternal.uncacheShortcuts(userId,
-                                mContext.getPackageName(), sbn.getPackageName(),
-                                Collections.singletonList(conversationInfo.getShortcutId()),
-                                userId);
+                Pair<String, String> conversationKey =
+                        Pair.create(sbn.getPackageName(), shortcutId);
+                synchronized (this) {
+                    int count = mActiveNotifCounts.getOrDefault(conversationKey, 0) - 1;
+                    if (count <= 0) {
+                        mActiveNotifCounts.remove(conversationKey);
+                        // The shortcut was cached by Notification Manager synchronously when the
+                        // associated notification was posted. Uncache it here when all the
+                        // associated notifications are removed.
+                        if (conversationInfo.isShortcutCached()
+                                && conversationInfo.getNotificationChannelId() == null) {
+                            mShortcutServiceInternal.uncacheShortcuts(mUserId,
+                                    mContext.getPackageName(), sbn.getPackageName(),
+                                    Collections.singletonList(conversationInfo.getShortcutId()),
+                                    mUserId);
+                        }
+                    } else {
+                        mActiveNotifCounts.put(conversationKey, count);
                     }
-                } else {
-                    mActiveNotifCounts.put(shortcutId, count);
                 }
             });
 
@@ -806,6 +839,9 @@
         @Override
         public void onNotificationChannelModified(String pkg, UserHandle user,
                 NotificationChannel channel, int modificationType) {
+            if (user.getIdentifier() != mUserId) {
+                return;
+            }
             PackageData packageData = getPackage(pkg, user.getIdentifier());
             String shortcutId = channel.getConversationId();
             if (packageData == null || shortcutId == null) {
@@ -816,16 +852,7 @@
             if (conversationInfo == null) {
                 return;
             }
-            boolean isNotificationSettingChanged =
-                    conversationInfo.isImportant() != channel.isImportantConversation()
-                            || conversationInfo.isDemoted() != channel.isDemoted()
-                            || channel.hasUserSetImportance()
-                            || (channel.getUserLockedFields() & USER_LOCKED_ALLOW_BUBBLE) != 0;
             ConversationInfo.Builder builder = new ConversationInfo.Builder(conversationInfo);
-            if (modificationType == NOTIFICATION_CHANNEL_OR_GROUP_UPDATED
-                    && isNotificationSettingChanged) {
-                builder.setNotificationSettingChanged(true);
-            }
             switch (modificationType) {
                 case NOTIFICATION_CHANNEL_OR_GROUP_ADDED:
                 case NOTIFICATION_CHANNEL_OR_GROUP_UPDATED:
@@ -848,6 +875,28 @@
             }
             conversationStore.addOrUpdate(builder.build());
         }
+
+        synchronized void cleanupCachedShortcuts() {
+            for (Pair<String, String> conversationKey : mActiveNotifCounts.keySet()) {
+                String packageName = conversationKey.first;
+                String shortcutId = conversationKey.second;
+                PackageData packageData = getPackage(packageName, mUserId);
+                ConversationInfo conversationInfo =
+                        packageData != null ? packageData.getConversationInfo(shortcutId) : null;
+                if (conversationInfo != null
+                        && conversationInfo.isShortcutCached()
+                        && conversationInfo.getNotificationChannelId() == null) {
+                    mShortcutServiceInternal.uncacheShortcuts(mUserId,
+                            mContext.getPackageName(), packageName,
+                            Collections.singletonList(shortcutId),
+                            mUserId);
+                }
+            }
+        }
+
+        synchronized boolean hasActiveNotifications(String packageName, String shortcutId) {
+            return mActiveNotifCounts.containsKey(Pair.create(packageName, shortcutId));
+        }
     }
 
     /**
@@ -917,7 +966,16 @@
 
         @Override
         public void onReceive(Context context, Intent intent) {
-            forAllUnlockedUsers(userData -> userData.forAllPackages(PackageData::saveToDisk));
+            forAllUnlockedUsers(userData -> {
+                NotificationListener listener = mNotificationListeners.get(userData.getUserId());
+                // Clean up the cached shortcuts because all the notifications are cleared after
+                // system shutdown. The associated shortcuts need to be uncached to keep in sync
+                // unless the settings are changed by the user.
+                if (listener != null) {
+                    listener.cleanupCachedShortcuts();
+                }
+                userData.forAllPackages(PackageData::saveToDisk);
+            });
         }
     }
 
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 6d15302..8abddc8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -308,7 +308,7 @@
         doReturn(0).when(() -> SurfaceControl.getActiveColorMode(display.token));
         doReturn(new int[] { 0 }).when(
                 () -> SurfaceControl.getDisplayColorModes(display.token));
-        doReturn(new SurfaceControl.DesiredDisplayConfigSpecs(0, 60.f, 60.f))
+        doReturn(new SurfaceControl.DesiredDisplayConfigSpecs(0, 60.f, 60.f, 60.f, 60.f))
                 .when(() -> SurfaceControl.getDesiredDisplayConfigSpecs(display.token));
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 08bd1ee..8137c36 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -16,7 +16,6 @@
 
 package com.android.server.display;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
@@ -30,7 +29,6 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs;
-import com.android.server.display.DisplayModeDirector.RefreshRateRange;
 import com.android.server.display.DisplayModeDirector.Vote;
 
 import com.google.common.truth.Truth;
@@ -79,10 +77,12 @@
         int displayId = 0;
 
         // With no votes present, DisplayModeDirector should allow any refresh rate.
-        assertEquals(new DesiredDisplayModeSpecs(/*baseModeId=*/60,
-                             new RefreshRateRange(0f, Float.POSITIVE_INFINITY)),
+        DesiredDisplayModeSpecs modeSpecs =
                 createDisplayModeDirectorWithDisplayFpsRange(60, 90).getDesiredDisplayModeSpecs(
-                        displayId));
+                        displayId);
+        Truth.assertThat(modeSpecs.baseModeId).isEqualTo(60);
+        Truth.assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(0f);
+        Truth.assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(Float.POSITIVE_INFINITY);
 
         int numPriorities =
                 DisplayModeDirector.Vote.MAX_PRIORITY - DisplayModeDirector.Vote.MIN_PRIORITY + 1;
@@ -101,10 +101,12 @@
                 int priority = Vote.MIN_PRIORITY + i;
                 votes.put(priority, Vote.forRefreshRates(minFps + i, maxFps - i));
                 director.injectVotesByDisplay(votesByDisplay);
-                assertEquals(new DesiredDisplayModeSpecs(
-                                /*baseModeId=*/minFps + i,
-                                new RefreshRateRange(minFps + i, maxFps - i)),
-                        director.getDesiredDisplayModeSpecs(displayId));
+                modeSpecs = director.getDesiredDisplayModeSpecs(displayId);
+                Truth.assertThat(modeSpecs.baseModeId).isEqualTo(minFps + i);
+                Truth.assertThat(modeSpecs.primaryRefreshRateRange.min)
+                        .isEqualTo((float) (minFps + i));
+                Truth.assertThat(modeSpecs.primaryRefreshRateRange.max)
+                        .isEqualTo((float) (maxFps - i));
             }
         }
 
@@ -119,9 +121,10 @@
             votes.put(Vote.MAX_PRIORITY, Vote.forRefreshRates(65, 85));
             votes.put(Vote.MIN_PRIORITY, Vote.forRefreshRates(70, 80));
             director.injectVotesByDisplay(votesByDisplay);
-            assertEquals(new DesiredDisplayModeSpecs(/*baseModeId=*/70,
-                                 new RefreshRateRange(70, 80)),
-                    director.getDesiredDisplayModeSpecs(displayId));
+            modeSpecs = director.getDesiredDisplayModeSpecs(displayId);
+            Truth.assertThat(modeSpecs.baseModeId).isEqualTo(70);
+            Truth.assertThat(modeSpecs.primaryRefreshRateRange.min).isEqualTo(70f);
+            Truth.assertThat(modeSpecs.primaryRefreshRateRange.max).isEqualTo(80f);
         }
     }
 
@@ -140,8 +143,8 @@
         director.injectVotesByDisplay(votesByDisplay);
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
 
-        Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
-        Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+        Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+        Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
         Truth.assertThat(desiredSpecs.baseModeId).isEqualTo(60);
     }
 
@@ -159,34 +162,77 @@
         votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
         director.injectVotesByDisplay(votesByDisplay);
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
-        Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
-        Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+        Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+        Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
 
         votes.clear();
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
         votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
-        Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
-        Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
-
+        Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+        Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
 
         votes.clear();
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
         votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
-        Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
-        Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
+        Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+        Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(90);
 
         votes.clear();
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60));
         votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
-        Truth.assertThat(desiredSpecs.refreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
-        Truth.assertThat(desiredSpecs.refreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+        Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+        Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+    }
 
+    @Test
+    public void testAppRequestRefreshRateRange() {
+        // Confirm that the app request range doesn't include low brightness or min refresh rate
+        // settings, but does include everything else.
+        assertTrue(
+                Vote.PRIORITY_LOW_BRIGHTNESS < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
+        assertTrue(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE
+                < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
+        assertTrue(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE
+                >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
 
+        int displayId = 0;
+        DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90);
+        SparseArray<Vote> votes = new SparseArray<>();
+        SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+        votesByDisplay.put(displayId, votes);
+        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+        director.injectVotesByDisplay(votesByDisplay);
+        DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+        Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
+        Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(60);
+        Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f);
+        Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f);
+
+        votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE,
+                Vote.forRefreshRates(90, Float.POSITIVE_INFINITY));
+        director.injectVotesByDisplay(votesByDisplay);
+        desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+        Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
+        Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isAtLeast(90f);
+        Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.min).isAtMost(60f);
+        Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.max).isAtLeast(90f);
+
+        votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(75, 75));
+        director.injectVotesByDisplay(votesByDisplay);
+        desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
+        Truth.assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(75);
+        Truth.assertThat(desiredSpecs.primaryRefreshRateRange.max).isWithin(FLOAT_TOLERANCE).of(75);
+        Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.min)
+                .isWithin(FLOAT_TOLERANCE)
+                .of(75);
+        Truth.assertThat(desiredSpecs.appRequestRefreshRateRange.max)
+                .isWithin(FLOAT_TOLERANCE)
+                .of(75);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
index ea8aa6b..70d6cf8 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
@@ -54,7 +54,6 @@
                 .setPersonImportant(true)
                 .setPersonBot(true)
                 .setContactStarred(true)
-                .setNotificationSettingChanged(true)
                 .build();
 
         assertEquals(SHORTCUT_ID, conversationInfo.getShortcutId());
@@ -71,7 +70,6 @@
         assertTrue(conversationInfo.isPersonImportant());
         assertTrue(conversationInfo.isPersonBot());
         assertTrue(conversationInfo.isContactStarred());
-        assertTrue(conversationInfo.isNotificationSettingChanged());
     }
 
     @Test
@@ -94,7 +92,6 @@
         assertFalse(conversationInfo.isPersonImportant());
         assertFalse(conversationInfo.isPersonBot());
         assertFalse(conversationInfo.isContactStarred());
-        assertFalse(conversationInfo.isNotificationSettingChanged());
     }
 
     @Test
@@ -112,7 +109,6 @@
                 .setPersonImportant(true)
                 .setPersonBot(true)
                 .setContactStarred(true)
-                .setNotificationSettingChanged(true)
                 .build();
 
         ConversationInfo destination = new ConversationInfo.Builder(source)
@@ -132,6 +128,5 @@
         assertTrue(destination.isPersonImportant());
         assertTrue(destination.isPersonBot());
         assertFalse(destination.isContactStarred());
-        assertTrue(destination.isNotificationSettingChanged());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index e51ab9db..1a2032a 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -49,6 +49,7 @@
 import android.app.prediction.AppTargetEvent;
 import android.app.prediction.AppTargetId;
 import android.app.usage.UsageStatsManagerInternal;
+import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -95,8 +96,6 @@
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
@@ -125,18 +124,19 @@
     @Mock private TelephonyManager mTelephonyManager;
     @Mock private TelecomManager mTelecomManager;
     @Mock private ContentResolver mContentResolver;
-    @Mock private ScheduledExecutorService mExecutorService;
     @Mock private JobScheduler mJobScheduler;
-    @Mock private ScheduledFuture mScheduledFuture;
     @Mock private StatusBarNotification mStatusBarNotification;
     @Mock private Notification mNotification;
 
     @Captor private ArgumentCaptor<ShortcutChangeCallback> mShortcutChangeCallbackCaptor;
+    @Captor private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
 
+    private ScheduledExecutorService mExecutorService;
     private NotificationChannel mNotificationChannel;
     private DataManager mDataManager;
     private CancellationSignal mCancellationSignal;
     private ShortcutChangeCallback mShortcutChangeCallback;
+    private BroadcastReceiver mShutdownBroadcastReceiver;
     private TestInjector mInjector;
 
     @Before
@@ -182,13 +182,7 @@
         when(mContext.getSystemServiceName(JobScheduler.class)).thenReturn(
                 Context.JOB_SCHEDULER_SERVICE);
 
-        when(mExecutorService.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(
-                TimeUnit.class))).thenReturn(mScheduledFuture);
-        doAnswer(ans -> {
-            Runnable runnable = (Runnable) ans.getArguments()[0];
-            runnable.run();
-            return null;
-        }).when(mExecutorService).execute(any(Runnable.class));
+        mExecutorService = new MockScheduledExecutorService();
 
         when(mUserManager.getEnabledProfiles(USER_ID_PRIMARY))
                 .thenReturn(Arrays.asList(
@@ -221,6 +215,9 @@
         verify(mShortcutServiceInternal).addShortcutChangeCallback(
                 mShortcutChangeCallbackCaptor.capture());
         mShortcutChangeCallback = mShortcutChangeCallbackCaptor.getValue();
+
+        verify(mContext).registerReceiver(mBroadcastReceiverCaptor.capture(), any());
+        mShutdownBroadcastReceiver = mBroadcastReceiverCaptor.getValue();
     }
 
     @After
@@ -459,7 +456,7 @@
     }
 
     @Test
-    public void testShortcutNotUncachedIfSettingChanged() {
+    public void testShortcutNotUncachedIfNotificationChannelCreated() {
         mDataManager.onUserUnlocked(USER_ID_PRIMARY);
 
         ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
@@ -473,7 +470,6 @@
         shortcut.setCached();
         mDataManager.addOrUpdateConversationInfo(shortcut);
 
-        mNotificationChannel.setImportantConversation(true);
         listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
                 mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
 
@@ -530,7 +526,6 @@
         assertTrue(conversationInfo.isImportant());
         assertFalse(conversationInfo.isNotificationSilenced());
         assertFalse(conversationInfo.isDemoted());
-        assertTrue(conversationInfo.isNotificationSettingChanged());
     }
 
     @Test
@@ -563,6 +558,51 @@
     }
 
     @Test
+    public void testUncacheShortcutWhenShutdown() {
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+        ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+                buildPerson());
+        mDataManager.addOrUpdateConversationInfo(shortcut);
+
+        NotificationListenerService listenerService =
+                mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+
+        listenerService.onNotificationPosted(mStatusBarNotification);
+        shortcut.setCached();
+        mDataManager.addOrUpdateConversationInfo(shortcut);
+
+        mShutdownBroadcastReceiver.onReceive(mContext, new Intent());
+        verify(mShortcutServiceInternal).uncacheShortcuts(
+                anyInt(), any(), eq(TEST_PKG_NAME),
+                eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY));
+    }
+
+    @Test
+    public void testDoNotUncacheShortcutWhenShutdownIfNotificationChannelCreated() {
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+        ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+                buildPerson());
+        mDataManager.addOrUpdateConversationInfo(shortcut);
+
+        NotificationListenerService listenerService =
+                mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+
+        listenerService.onNotificationPosted(mStatusBarNotification);
+        shortcut.setCached();
+        mDataManager.addOrUpdateConversationInfo(shortcut);
+
+        listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
+                mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+        mShutdownBroadcastReceiver.onReceive(mContext, new Intent());
+        verify(mShortcutServiceInternal, never()).uncacheShortcuts(
+                anyInt(), any(), eq(TEST_PKG_NAME),
+                eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY));
+    }
+
+    @Test
     public void testShortcutAddedOrUpdated() {
         mDataManager.onUserUnlocked(USER_ID_PRIMARY);
 
@@ -722,6 +762,22 @@
     }
 
     @Test
+    public void testPruneInactiveCachedShortcuts() {
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+        ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+                buildPerson());
+        shortcut.setCached();
+        mDataManager.addOrUpdateConversationInfo(shortcut);
+
+        mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
+
+        verify(mShortcutServiceInternal).uncacheShortcuts(
+                anyInt(), any(), eq(TEST_PKG_NAME),
+                eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY));
+    }
+
+    @Test
     public void testBackupAndRestoration()
             throws IntentFilter.MalformedMimeTypeException {
         mDataManager.onUserUnlocked(USER_ID_PRIMARY);
diff --git a/services/tests/servicestests/src/com/android/server/people/data/MockScheduledExecutorService.java b/services/tests/servicestests/src/com/android/server/people/data/MockScheduledExecutorService.java
index aecbc8d..8cb846f 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/MockScheduledExecutorService.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/MockScheduledExecutorService.java
@@ -96,7 +96,6 @@
     @Override
     public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period,
             TimeUnit unit) {
-        Preconditions.checkState(unit == TimeUnit.MILLISECONDS);
         return new MockScheduledFuture<>(command, period, unit);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index 7b1b2d2..6de08fd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -23,6 +23,7 @@
 import android.content.pm.FeatureInfo
 import android.content.pm.InstrumentationInfo
 import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
 import android.content.pm.PackageParser
 import android.content.pm.PackageUserState
 import android.content.pm.PermissionInfo
@@ -168,6 +169,11 @@
 
         private fun <T> tryOrNull(block: () -> T) = try {
             block()
+        } catch (e: PackageParser.PackageParserException) {
+            if (e.error != PackageManager.INSTALL_PARSE_FAILED_SKIPPED) {
+                thrownInSetUp.add(e)
+            }
+            null
         } catch (t: Throwable) {
             thrownInSetUp.add(t)
             null
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index f1c3906..60390dc 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -126,7 +126,7 @@
 
         mIntervalStats.majorVersion = 7;
         mIntervalStats.minorVersion = 8;
-        mIntervalStats.beginTime = time;
+        mIntervalStats.beginTime = time - 1;
         mIntervalStats.interactiveTracker.count = 2;
         mIntervalStats.interactiveTracker.duration = 111111;
         mIntervalStats.nonInteractiveTracker.count = 3;
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 7e7d69a..fdc9401 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -54,6 +54,10 @@
         <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityViewTestActivity" />
         <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ActivityInActivityView"
                   android:resizeableActivity="true" />
+        <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$LandscapeActivity"
+                  android:screenOrientation="sensorLandscape"
+                  android:showWhenLocked="true"
+                  android:turnScreenOn="true" />
         <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity"
                   android:showWhenLocked="true" android:allowEmbedded="true"/>
     </application>
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 60875de..fb24d86 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -49,7 +49,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
@@ -67,16 +66,12 @@
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
-import android.service.voice.IVoiceInteractionSession;
 import android.util.DisplayMetrics;
 import android.util.Xml;
 import android.view.DisplayInfo;
 
 import androidx.test.filters.MediumTest;
 
-import com.android.internal.app.IVoiceInteractor;
-import com.android.server.wm.Task.TaskFactory;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -122,11 +117,6 @@
         assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
     }
 
-    @Test
-    public void testDefaultTaskFactoryNotNull() throws Exception {
-        assertNotNull(Task.getTaskFactory());
-    }
-
     /** Ensure we have no chance to modify the original intent. */
     @Test
     public void testCopyBaseIntentForTaskInfo() {
@@ -139,23 +129,6 @@
     }
 
     @Test
-    public void testCreateTestRecordUsingCustomizedFactory() throws Exception {
-        TestTaskFactory factory = new TestTaskFactory();
-        Task.setTaskFactory(factory);
-
-        try {
-            assertFalse(factory.mCreated);
-
-            Task.create(mService, 0 /*taskId*/, 0 /*activityType*/,
-                    new ActivityInfo(), new Intent(), false /* createdByOrganizer */);
-
-            assertTrue(factory.mCreated);
-        } finally {
-            Task.setTaskFactory(null);
-        }
-    }
-
-    @Test
     public void testReturnsToHomeStack() throws Exception {
         final Task task = createTask(1);
         spyOn(task);
@@ -448,6 +421,21 @@
     }
 
     @Test
+    public void testFullScreenTaskNotAdjustedByMinimalSize() {
+        final Task fullscreenTask = new TaskBuilder(mSupervisor).build();
+        final Rect originalTaskBounds = new Rect(fullscreenTask.getBounds());
+        final ActivityInfo aInfo = new ActivityInfo();
+        aInfo.windowLayout = new ActivityInfo.WindowLayout(0 /* width */, 0 /* widthFraction */,
+                    0 /* height */, 0 /* heightFraction */, 0 /* gravity */,
+                    originalTaskBounds.width() * 2 /* minWidth */,
+                    originalTaskBounds.height() * 2 /* minHeight */);
+        fullscreenTask.setMinDimensions(aInfo);
+        fullscreenTask.onConfigurationChanged(fullscreenTask.getParent().getConfiguration());
+
+        assertEquals(originalTaskBounds, fullscreenTask.getBounds());
+    }
+
+    @Test
     public void testInsetDisregardedWhenFreeformOverlapsNavBar() {
         TaskDisplayArea taskDisplayArea = mService.mRootWindowContainer.getDefaultTaskDisplayArea();
         ActivityStack stack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN,
@@ -525,8 +513,9 @@
         info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME;
         info.targetActivity = targetClassName;
 
-        final Task task = Task.create(mService, 1 /* taskId */, info, intent,
-                null /* voiceSession */, null /* voiceInteractor */, null /*stack*/);
+        final Task task = new ActivityStack(mService, 1 /* taskId */, info, intent,
+                null /* voiceSession */, null /* voiceInteractor */, null /* taskDescriptor */,
+                null /*stack*/);
         assertEquals("The alias activity component should be saved in task intent.", aliasClassName,
                 task.intent.getComponent().getClassName());
 
@@ -1023,48 +1012,4 @@
                 0, null /*ActivityInfo*/, null /*_voiceSession*/, null /*_voiceInteractor*/,
                 null /*stack*/);
     }
-
-    private static class TestTaskFactory extends TaskFactory {
-        private boolean mCreated = false;
-
-        @Override
-        Task create(ActivityTaskManagerService service, int taskId, int activityType,
-                ActivityInfo info, Intent intent, boolean createdByOrganizer) {
-            mCreated = true;
-            return null;
-        }
-
-        @Override
-        Task create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
-                Intent intent, IVoiceInteractionSession voiceSession,
-                IVoiceInteractor voiceInteractor, ActivityStack stack) {
-            mCreated = true;
-            return null;
-        }
-
-        @Override
-        Task create(ActivityTaskManagerService service, int taskId, Intent intent,
-                Intent affinityIntent, String affinity, String rootAffinity,
-                ComponentName realActivity,
-                ComponentName origActivity, boolean rootWasReset, boolean autoRemoveRecents,
-                boolean askedCompatMode, int userId, int effectiveUid, String lastDescription,
-                long lastTimeMoved,
-                boolean neverRelinquishIdentity,
-                ActivityManager.TaskDescription lastTaskDescription,
-                int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor,
-                int callingUid, String callingPackage, String callingFeatureId, int resizeMode,
-                boolean supportsPictureInPicture,
-                boolean realActivitySuspended, boolean userSetupComplete, int minWidth,
-                int minHeight, ActivityStack stack) {
-            mCreated = true;
-            return null;
-        }
-
-        @Override
-        Task restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
-                throws IOException, XmlPullParserException {
-            mCreated = true;
-            return null;
-        }
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index 9872faa..4e92ea0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -22,6 +22,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
@@ -41,6 +42,7 @@
 import android.content.pm.ActivityInfo;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.uiautomator.UiDevice;
 import android.text.TextUtils;
@@ -56,8 +58,10 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
 
 /**
  * Build/Install/Run:
@@ -391,6 +395,42 @@
         }
     };
 
+    @Presubmit
+    @FlakyTest(bugId = 150409355)
+    @Test
+    public void testNotifyTaskRequestedOrientationChanged() throws Exception {
+        final ArrayBlockingQueue<int[]> taskIdAndOrientationQueue = new ArrayBlockingQueue<>(10);
+        registerTaskStackChangedListener(new TaskStackListener() {
+            @Override
+            public void onTaskRequestedOrientationChanged(int taskId, int requestedOrientation) {
+                int[] taskIdAndOrientation = new int[2];
+                taskIdAndOrientation[0] = taskId;
+                taskIdAndOrientation[1] = requestedOrientation;
+                taskIdAndOrientationQueue.offer(taskIdAndOrientation);
+            }
+        });
+
+        final LandscapeActivity activity =
+                (LandscapeActivity) startTestActivity(LandscapeActivity.class);
+
+        int[] taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue,
+                candidate -> candidate[0] == activity.getTaskId());
+        assertNotNull(taskIdAndOrientation);
+        assertEquals(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE, taskIdAndOrientation[1]);
+
+        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
+        taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue,
+                candidate -> candidate[0] == activity.getTaskId());
+        assertNotNull(taskIdAndOrientation);
+        assertEquals(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT, taskIdAndOrientation[1]);
+
+        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+        taskIdAndOrientation = waitForResult(taskIdAndOrientationQueue,
+                candidate -> candidate[0] == activity.getTaskId());
+        assertNotNull(taskIdAndOrientation);
+        assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, taskIdAndOrientation[1]);
+    }
+
     /**
      * Starts the provided activity and returns the started instance.
      */
@@ -432,6 +472,19 @@
         }
     }
 
+    private <T> T waitForResult(ArrayBlockingQueue<T> queue, Predicate<T> predicate) {
+        try {
+            final long timeout = SystemClock.uptimeMillis() + TimeUnit.SECONDS.toMillis(15);
+            T result;
+            do {
+                result = queue.poll(timeout - SystemClock.uptimeMillis(), TimeUnit.MILLISECONDS);
+            } while (result != null && !predicate.test(result));
+            return result;
+        } catch (InterruptedException e) {
+            return null;
+        }
+    }
+
     public static class TestActivity extends Activity {
         boolean mIsResumed = false;
 
@@ -563,4 +616,6 @@
 
     // Activity that has {@link android.R.attr#resizeableActivity} attribute set to {@code true}
     public static class ActivityInActivityView extends TestActivity {}
+
+    public static class LandscapeActivity extends TestActivity {}
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index 9fdb9d8..d65b084 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -204,7 +204,7 @@
                 .when(windowConfiguration).getWindowingMode();
 
         // Prevent adjust task dimensions
-        doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any());
+        doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any(), any());
 
         final Rect stackBounds = new Rect(200, 200, 800, 1000);
         // Update surface position and size by the given bounds.
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 aa68c69..2ea58a0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -507,7 +507,7 @@
         Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
         Configuration c = new Configuration(task1.getRequestedOverrideConfiguration());
         c.windowConfiguration.setBounds(newSize);
-        doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any());
+        doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any(), any());
         task1.onRequestedOverrideConfigurationChanged(c);
         assertEquals(newSize, stack.getBounds());
 
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 8fadf5e..5ee3b48 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -253,10 +253,6 @@
                             }
                             break;
                     }
-                    if (event.mTimeStamp == 0) {
-                        //mTimestamp not set, assume default value 0 plus beginTime
-                        event.mTimeStamp = beginTime;
-                    }
                     return event;
             }
         }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java
index 932784d..463fc37 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProto.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java
@@ -149,10 +149,6 @@
                     break;
             }
         }
-        if (stats.mLastTimeUsed == 0) {
-            // mLastTimeUsed was not assigned, assume default value of 0 plus beginTime;
-            stats.mLastTimeUsed = statsOut.beginTime;
-        }
         proto.end(token);
     }
 
@@ -289,10 +285,6 @@
                     configActive = proto.readBoolean(IntervalStatsProto.Configuration.ACTIVE);
                     break;
                 case ProtoInputStream.NO_MORE_FIELDS:
-                    if (configStats.mLastTimeActive == 0) {
-                        //mLastTimeActive was not assigned, assume default value of 0 plus beginTime
-                        configStats.mLastTimeActive = statsOut.beginTime;
-                    }
                     if (configActive) {
                         statsOut.activeConfiguration = configStats.mConfiguration;
                     }
@@ -336,21 +328,21 @@
             // Package not in Stringpool for some reason, write full string instead
             proto.write(IntervalStatsProto.UsageStats.PACKAGE, usageStats.mPackageName);
         }
-        // Time attributes stored as an offset of the beginTime.
-        proto.write(IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS,
-                usageStats.mLastTimeUsed - stats.beginTime);
+        UsageStatsProtoV2.writeOffsetTimestamp(proto,
+                IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS,
+                usageStats.mLastTimeUsed, stats.beginTime);
         proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS,
                 usageStats.mTotalTimeInForeground);
         proto.write(IntervalStatsProto.UsageStats.LAST_EVENT,
                 usageStats.mLastEvent);
-        // Time attributes stored as an offset of the beginTime.
-        proto.write(IntervalStatsProto.UsageStats.LAST_TIME_SERVICE_USED_MS,
-                usageStats.mLastTimeForegroundServiceUsed - stats.beginTime);
+        UsageStatsProtoV2.writeOffsetTimestamp(proto,
+                IntervalStatsProto.UsageStats.LAST_TIME_SERVICE_USED_MS,
+                usageStats.mLastTimeForegroundServiceUsed, stats.beginTime);
         proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS,
                 usageStats.mTotalTimeForegroundServiceUsed);
-        // Time attributes stored as an offset of the beginTime.
-        proto.write(IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS,
-                usageStats.mLastTimeVisible - stats.beginTime);
+        UsageStatsProtoV2.writeOffsetTimestamp(proto,
+                IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS,
+                usageStats.mLastTimeVisible, stats.beginTime);
         proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS,
                 usageStats.mTotalTimeVisible);
         proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount);
@@ -411,8 +403,9 @@
             throws IllegalArgumentException {
         final long token = proto.start(fieldId);
         configStats.mConfiguration.dumpDebug(proto, IntervalStatsProto.Configuration.CONFIG);
-        proto.write(IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS,
-                configStats.mLastTimeActive - stats.beginTime);
+        UsageStatsProtoV2.writeOffsetTimestamp(proto,
+                IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS,
+                configStats.mLastTimeActive, stats.beginTime);
         proto.write(IntervalStatsProto.Configuration.TOTAL_TIME_ACTIVE_MS,
                 configStats.mTotalTimeActive);
         proto.write(IntervalStatsProto.Configuration.COUNT, configStats.mActivationCount);
@@ -439,7 +432,8 @@
                 proto.write(IntervalStatsProto.Event.CLASS, event.mClass);
             }
         }
-        proto.write(IntervalStatsProto.Event.TIME_MS, event.mTimeStamp - stats.beginTime);
+        UsageStatsProtoV2.writeOffsetTimestamp(proto, IntervalStatsProto.Event.TIME_MS,
+                event.mTimeStamp, stats.beginTime);
         proto.write(IntervalStatsProto.Event.FLAGS, event.mFlags);
         proto.write(IntervalStatsProto.Event.TYPE, event.mEventType);
         proto.write(IntervalStatsProto.Event.INSTANCE_ID, event.mInstanceId);
@@ -566,10 +560,6 @@
                     }
                     break;
                 case ProtoInputStream.NO_MORE_FIELDS:
-                    if (statsOut.endTime == 0) {
-                        // endTime not assigned, assume default value of 0 plus beginTime
-                        statsOut.endTime = statsOut.beginTime;
-                    }
                     statsOut.upgradeIfNeeded();
                     return;
             }
@@ -585,7 +575,8 @@
     public static void write(OutputStream out, IntervalStats stats)
             throws IOException, IllegalArgumentException {
         final ProtoOutputStream proto = new ProtoOutputStream(out);
-        proto.write(IntervalStatsProto.END_TIME_MS, stats.endTime - stats.beginTime);
+        proto.write(IntervalStatsProto.END_TIME_MS,
+                UsageStatsProtoV2.getOffsetTimestamp(stats.endTime, stats.beginTime));
         proto.write(IntervalStatsProto.MAJOR_VERSION, stats.majorVersion);
         proto.write(IntervalStatsProto.MINOR_VERSION, stats.minorVersion);
         // String pool should be written before the rest of the usage stats
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
index e4aa9fe..e6d2841 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java
@@ -30,6 +30,7 @@
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.LinkedList;
+import java.util.concurrent.TimeUnit;
 
 /**
  * UsageStats reader/writer V2 for Protocol Buffer format.
@@ -37,6 +38,8 @@
 final class UsageStatsProtoV2 {
     private static final String TAG = "UsageStatsProtoV2";
 
+    private static final long ONE_HOUR_MS = TimeUnit.HOURS.toMillis(1);
+
     // Static-only utility class.
     private UsageStatsProtoV2() {}
 
@@ -88,10 +91,6 @@
                             UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS);
                     break;
                 case ProtoInputStream.NO_MORE_FIELDS:
-                    // mLastTimeUsed was not read, assume default value of 0 plus beginTime
-                    if (stats.mLastTimeUsed == 0) {
-                        stats.mLastTimeUsed = beginTime;
-                    }
                     return stats;
             }
         }
@@ -219,10 +218,6 @@
                             IntervalStatsObfuscatedProto.Configuration.ACTIVE);
                     break;
                 case ProtoInputStream.NO_MORE_FIELDS:
-                    // mLastTimeActive was not assigned, assume default value of 0 plus beginTime
-                    if (configStats.mLastTimeActive == 0) {
-                        configStats.mLastTimeActive = stats.beginTime;
-                    }
                     if (configActive) {
                         stats.activeConfiguration = configStats.mConfiguration;
                     }
@@ -282,27 +277,40 @@
                             EventObfuscatedProto.LOCUS_ID_TOKEN) - 1;
                     break;
                 case ProtoInputStream.NO_MORE_FIELDS:
-                    // timeStamp was not read, assume default value 0 plus beginTime
-                    if (event.mTimeStamp == 0) {
-                        event.mTimeStamp = beginTime;
-                    }
                     return event.mPackageToken == PackagesTokenData.UNASSIGNED_TOKEN ? null : event;
             }
         }
     }
 
+    static void writeOffsetTimestamp(ProtoOutputStream proto, long fieldId,
+            long timestamp, long beginTime) {
+        // timestamps will only be written if they're after the begin time
+        // a grace period of one hour before the begin time is allowed because of rollover logic
+        final long rolloverGracePeriod = beginTime - ONE_HOUR_MS;
+        if (timestamp > rolloverGracePeriod) {
+            // time attributes are stored as an offset of the begin time (given offset)
+            proto.write(fieldId, getOffsetTimestamp(timestamp, beginTime));
+        }
+    }
+
+    static long getOffsetTimestamp(long timestamp, long offset) {
+        final long offsetTimestamp = timestamp - offset;
+        // add one ms to timestamp if 0 to ensure it's written to proto (default values are ignored)
+        return offsetTimestamp == 0 ? offsetTimestamp + 1 : offsetTimestamp;
+    }
+
     private static void writeUsageStats(ProtoOutputStream proto, final long beginTime,
             final UsageStats stats) throws IllegalArgumentException {
-        // Time attributes stored as an offset of the beginTime.
         proto.write(UsageStatsObfuscatedProto.PACKAGE_TOKEN, stats.mPackageToken + 1);
-        proto.write(UsageStatsObfuscatedProto.LAST_TIME_ACTIVE_MS, stats.mLastTimeUsed - beginTime);
+        writeOffsetTimestamp(proto, UsageStatsObfuscatedProto.LAST_TIME_ACTIVE_MS,
+                stats.mLastTimeUsed, beginTime);
         proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_ACTIVE_MS, stats.mTotalTimeInForeground);
-        proto.write(UsageStatsObfuscatedProto.LAST_TIME_SERVICE_USED_MS,
-                stats.mLastTimeForegroundServiceUsed - beginTime);
+        writeOffsetTimestamp(proto, UsageStatsObfuscatedProto.LAST_TIME_SERVICE_USED_MS,
+                stats.mLastTimeForegroundServiceUsed, beginTime);
         proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_SERVICE_USED_MS,
                 stats.mTotalTimeForegroundServiceUsed);
-        proto.write(UsageStatsObfuscatedProto.LAST_TIME_VISIBLE_MS,
-                stats.mLastTimeVisible - beginTime);
+        writeOffsetTimestamp(proto, UsageStatsObfuscatedProto.LAST_TIME_VISIBLE_MS,
+                stats.mLastTimeVisible, beginTime);
         proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS, stats.mTotalTimeVisible);
         proto.write(UsageStatsObfuscatedProto.APP_LAUNCH_COUNT, stats.mAppLaunchCount);
         try {
@@ -361,8 +369,8 @@
             throws IllegalArgumentException {
         configStats.mConfiguration.dumpDebug(proto,
                 IntervalStatsObfuscatedProto.Configuration.CONFIG);
-        proto.write(IntervalStatsObfuscatedProto.Configuration.LAST_TIME_ACTIVE_MS,
-                configStats.mLastTimeActive - statsBeginTime);
+        writeOffsetTimestamp(proto, IntervalStatsObfuscatedProto.Configuration.LAST_TIME_ACTIVE_MS,
+                configStats.mLastTimeActive, statsBeginTime);
         proto.write(IntervalStatsObfuscatedProto.Configuration.TOTAL_TIME_ACTIVE_MS,
                 configStats.mTotalTimeActive);
         proto.write(IntervalStatsObfuscatedProto.Configuration.COUNT, configStats.mActivationCount);
@@ -375,7 +383,7 @@
         if (event.mClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
             proto.write(EventObfuscatedProto.CLASS_TOKEN, event.mClassToken + 1);
         }
-        proto.write(EventObfuscatedProto.TIME_MS, event.mTimeStamp - statsBeginTime);
+        writeOffsetTimestamp(proto, EventObfuscatedProto.TIME_MS, event.mTimeStamp, statsBeginTime);
         proto.write(EventObfuscatedProto.FLAGS, event.mFlags);
         proto.write(EventObfuscatedProto.TYPE, event.mEventType);
         proto.write(EventObfuscatedProto.INSTANCE_ID, event.mInstanceId);
@@ -489,10 +497,6 @@
                     }
                     break;
                 case ProtoInputStream.NO_MORE_FIELDS:
-                    // endTime not assigned, assume default value of 0 plus beginTime
-                    if (stats.endTime == 0) {
-                        stats.endTime = stats.beginTime;
-                    }
                     // update the begin and end time stamps for all usage stats
                     final int usageStatsSize = stats.packageStatsObfuscated.size();
                     for (int i = 0; i < usageStatsSize; i++) {
@@ -514,7 +518,8 @@
     public static void write(OutputStream out, IntervalStats stats)
             throws IOException, IllegalArgumentException {
         final ProtoOutputStream proto = new ProtoOutputStream(out);
-        proto.write(IntervalStatsObfuscatedProto.END_TIME_MS, stats.endTime - stats.beginTime);
+        proto.write(IntervalStatsObfuscatedProto.END_TIME_MS,
+                getOffsetTimestamp(stats.endTime, stats.beginTime));
         proto.write(IntervalStatsObfuscatedProto.MAJOR_VERSION, stats.majorVersion);
         proto.write(IntervalStatsObfuscatedProto.MINOR_VERSION, stats.minorVersion);
 
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 170bee8..42e2bbf 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -443,6 +443,8 @@
 
             enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
 
+            enforceDetectionPermissions(detectionService);
+
             if (!isInitialized()) return STATUS_ERROR;
             if (DEBUG) {
                 Slog.i(TAG, "startRecognition(): id = " + soundModelId);
@@ -1532,6 +1534,16 @@
         }
     }
 
+    private void enforceDetectionPermissions(ComponentName detectionService) {
+        PackageManager packageManager = mContext.getPackageManager();
+        String packageName = detectionService.getPackageName();
+        if (packageManager.checkPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD, packageName)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(detectionService.getPackageName() + " does not have"
+                    + " permission " + Manifest.permission.CAPTURE_AUDIO_HOTWORD);
+        }
+    }
+
     //=================================================================
     // For logging
 
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index eb553d3..f2f1412 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -399,7 +399,7 @@
      * Optional extra for communicating the call network technology used by a
      * {@link android.telecom.Connection} to Telecom and InCallUI.
      *
-     * @see {@code NETWORK_TYPE_*} in {@link android.telephony.TelephonyManager}.
+     * {@code NETWORK_TYPE_*} in {@link android.telephony.TelephonyManager}.
      */
     public static final String EXTRA_CALL_NETWORK_TYPE =
             "android.telecom.extra.CALL_NETWORK_TYPE";
diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java
index 36fa5cc..3d5c6aa 100644
--- a/telephony/java/android/telephony/TelephonyDisplayInfo.java
+++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java
@@ -62,8 +62,6 @@
      * {@link TelephonyManager#NETWORK_TYPE_LTE} network and has E-UTRA-NR Dual Connectivity(EN-DC)
      * capability or is currently connected to the secondary
      * {@link TelephonyManager#NETWORK_TYPE_NR} cellular network on millimeter wave bands.
-     *
-     * @see AccessNetworkConstants.NgranBands#FREQUENCY_RANGE_GROUP_2
      */
     public static final int OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE = 4;
 
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index bd531da..43db1d9ce 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -140,8 +140,7 @@
     }
 
     /**
-     * Receives IMS capability status updates from the ImsService. This information is also
-     * available via the {@see #isAvailable(int, int)} method below.
+     * Receives IMS capability status updates from the ImsService.
      *
      * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
      * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
@@ -194,8 +193,6 @@
          * If unavailable, the feature is not able to support the unavailable capability at this
          * time.
          *
-         * This information can also be queried using the {@see #isAvailable(int, int)} API.
-         *
          * @param capabilities The new availability of the capabilities.
          */
         public void onCapabilitiesStatusChanged(
@@ -496,8 +493,7 @@
     /**
      * Registers a {@link CapabilityCallback} with the system, which will provide MmTel service
      * availability updates for the subscription specified in
-     * {@link ImsManager#getImsMmTelManager(int)}. The method {@see #isAvailable(int, int)}
-     * can also be used to query this information at any time.
+     * {@link ImsManager#getImsMmTelManager(int)}.
      *
      * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
      * subscription changed events and call
@@ -639,7 +635,6 @@
      * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
      * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
-     * @see #setAdvancedCallingSettingEnabled(boolean)
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
      * @return true if the user's setting for advanced calling is enabled, false otherwise.
@@ -858,7 +853,6 @@
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
      * @return true if the user’s “Video Calling” setting is currently enabled.
-     * @see #setVtSettingEnabled(boolean)
      */
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
@@ -933,7 +927,6 @@
      *
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
-     * @see #setVoWiFiSettingEnabled(boolean)
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(anyOf = {
@@ -1011,7 +1004,6 @@
      * active (SIM is not inserted, ESIM inactive) or invalid.
      * @return true if the user's setting for Voice over WiFi while roaming is enabled, false
      * if disabled.
-     * @see #setVoWiFiRoamingSettingEnabled(boolean)
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(anyOf = {
@@ -1130,7 +1122,6 @@
      * - {@link #WIFI_MODE_WIFI_ONLY}
      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
      * - {@link #WIFI_MODE_WIFI_PREFERRED}
-     * @see #setVoWiFiSettingEnabled(boolean)
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
     @RequiresPermission(anyOf = {
@@ -1315,7 +1306,6 @@
      *
      * @throws IllegalArgumentException if the subscription associated with this operation is not
      * active (SIM is not inserted, ESIM inactive) or invalid.
-     * @see android.telecom.TelecomManager#getCurrentTtyMode
      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
      */
     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java
index 1dbaff5..e085dec 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -270,7 +270,7 @@
      * inactive subscription, it will result in a no-op.
      *
      * @param c The {@link RegistrationCallback} to be removed.
-     * @see SubscriptionManager.OnSubscriptionsChangedListener
+     * @see android.telephony.SubscriptionManager.OnSubscriptionsChangedListener
      * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
      */
     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 0b25d6f..b3b7b20 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -218,13 +218,7 @@
      * {@link MmTelCapabilities#CAPABILITY_TYPE_UT}, and
      * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}.
      *
-     * The capabilities of this MmTelFeature will be set by the framework and can be queried with
-     * {@see #queryCapabilityStatus()}.
-     *
-     * This MmTelFeature can then return the status of each of these capabilities (enabled or not)
-     * by sending a {@see #notifyCapabilitiesStatusChanged} callback to the framework. The current
-     * status can also be queried using {@see #queryCapabilityStatus()}.
-     * @see #isCapable(int)
+     * The capabilities of this MmTelFeature will be set by the framework.
      */
     public static class MmTelCapabilities extends Capabilities {
 
diff --git a/wifi/java/android/net/wifi/WpsInfo.java b/wifi/java/android/net/wifi/WpsInfo.java
index 00cb243..689ace5b 100644
--- a/wifi/java/android/net/wifi/WpsInfo.java
+++ b/wifi/java/android/net/wifi/WpsInfo.java
@@ -22,7 +22,7 @@
 /**
  * A class representing Wi-Fi Protected Setup
  *
- * {@see WifiP2pConfig}
+ * {@see android.net.wifi.p2p.WifiP2pConfig}
  */
 public class WpsInfo implements Parcelable {