Merge "Check fs-verity status in app process." into sc-dev
diff --git a/StubLibraries.bp b/StubLibraries.bp
index d1ad189..49433f1 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -116,7 +116,7 @@
         last_released: {
             api_file: ":android-non-updatable.api.public.latest",
             removed_api_file: ":android-non-updatable-removed.api.public.latest",
-            baseline_file: ":public-api-incompatibilities-with-last-released",
+            baseline_file: ":android-incompatibilities.api.public.latest",
         },
         api_lint: {
             enabled: true,
@@ -168,7 +168,7 @@
         last_released: {
             api_file: ":android-non-updatable.api.system.latest",
             removed_api_file: ":android-non-updatable-removed.api.system.latest",
-            baseline_file: ":system-api-incompatibilities-with-last-released"
+            baseline_file: ":android-incompatibilities.api.system.latest"
         },
         api_lint: {
             enabled: true,
diff --git a/apex/Android.bp b/apex/Android.bp
index 77ff6db..1876110 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -101,6 +101,10 @@
         sdk_version: "module_current",
     },
 
+    // installable implies we'll create a non-apex (platform) variant, which
+    // we shouldn't ordinarily need (and it can create issues), so disable that.
+    installable: false,
+
     // Configure framework module specific metalava options.
     droiddoc_options: [mainline_stubs_args],
 
diff --git a/boot/Android.bp b/boot/Android.bp
new file mode 100644
index 0000000..dd4066a
--- /dev/null
+++ b/boot/Android.bp
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+boot_image {
+    name: "framework-boot-image",
+    image_name: "boot",
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index f49ce1f..61d7aad 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -24225,7 +24225,8 @@
     method public void setCallback(@Nullable android.media.session.MediaSession.Callback, @Nullable android.os.Handler);
     method public void setExtras(@Nullable android.os.Bundle);
     method public void setFlags(int);
-    method public void setMediaButtonReceiver(@Nullable android.app.PendingIntent);
+    method public void setMediaButtonBroadcastReceiver(@Nullable android.content.ComponentName);
+    method @Deprecated public void setMediaButtonReceiver(@Nullable android.app.PendingIntent);
     method public void setMetadata(@Nullable android.media.MediaMetadata);
     method public void setPlaybackState(@Nullable android.media.session.PlaybackState);
     method public void setPlaybackToLocal(android.media.AudioAttributes);
@@ -30191,6 +30192,8 @@
     field @Deprecated public static final String RADIO;
     field @Deprecated public static final String SERIAL;
     field @NonNull public static final String SKU;
+    field @NonNull public static final String SOC_MANUFACTURER;
+    field @NonNull public static final String SOC_MODEL;
     field public static final String[] SUPPORTED_32_BIT_ABIS;
     field public static final String[] SUPPORTED_64_BIT_ABIS;
     field public static final String[] SUPPORTED_ABIS;
@@ -31878,6 +31881,8 @@
   }
 
   public final class UnsafeIntentLaunchViolation extends android.os.strictmode.Violation {
+    ctor public UnsafeIntentLaunchViolation(@NonNull android.content.Intent);
+    method @Nullable public android.content.Intent getIntent();
   }
 
   public final class UntaggedSocketViolation extends android.os.strictmode.Violation {
@@ -36957,6 +36962,7 @@
     field public static final int ORIGIN_IMPORTED = 2; // 0x2
     field public static final int ORIGIN_SECURELY_IMPORTED = 8; // 0x8
     field public static final int ORIGIN_UNKNOWN = 4; // 0x4
+    field public static final int PURPOSE_AGREE_KEY = 64; // 0x40
     field public static final int PURPOSE_DECRYPT = 2; // 0x2
     field public static final int PURPOSE_ENCRYPT = 1; // 0x1
     field public static final int PURPOSE_SIGN = 4; // 0x4
@@ -38570,6 +38576,7 @@
 
   public class SpeechRecognizer {
     method public void cancel();
+    method @NonNull public static android.speech.SpeechRecognizer createOnDeviceSpeechRecognizer(@NonNull android.content.Context);
     method public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context);
     method public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context, android.content.ComponentName);
     method public void destroy();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index b3e78ab..ca798b9 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -217,6 +217,7 @@
     field public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
     field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
     field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
+    field public static final String RESTART_WIFI_SUBSYSTEM = "android.permission.RESTART_WIFI_SUBSYSTEM";
     field public static final String RESTORE_RUNTIME_PERMISSIONS = "android.permission.RESTORE_RUNTIME_PERMISSIONS";
     field public static final String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
     field public static final String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
@@ -2369,6 +2370,7 @@
     field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40
     field public static final int FLAG_PERMISSION_REVOKED_COMPAT = 8; // 0x8
     field @Deprecated public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8
+    field public static final int FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY = 524288; // 0x80000
     field public static final int FLAG_PERMISSION_SYSTEM_FIXED = 16; // 0x10
     field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2
     field public static final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED = 512; // 0x200
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 415105f..bdd541a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2669,6 +2669,10 @@
         dispatchActivityDestroyed();
 
         notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_STOP);
+
+        if (mUiTranslationController != null) {
+            mUiTranslationController.onActivityDestroyed();
+        }
     }
 
     /**
@@ -7877,6 +7881,10 @@
 
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     final void performCreate(Bundle icicle, PersistableBundle persistentState) {
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performCreate:"
+                    + mComponent.getClassName());
+        }
         dispatchActivityPreCreated(icicle);
         mCanEnterPictureInPicture = true;
         // initialize mIsInMultiWindowMode and mIsInPictureInPictureMode before onCreate
@@ -7899,6 +7907,7 @@
         mFragments.dispatchActivityCreated();
         mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
         dispatchActivityPostCreated(icicle);
+        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
     }
 
     final void performNewIntent(@NonNull Intent intent) {
@@ -8004,6 +8013,10 @@
     }
 
     final void performResume(boolean followedByPause, String reason) {
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performResume:"
+                    + mComponent.getClassName());
+        }
         dispatchActivityPreResumed();
         performRestart(true /* start */, reason);
 
@@ -8055,9 +8068,14 @@
                 " did not call through to super.onPostResume()");
         }
         dispatchActivityPostResumed();
+        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
     }
 
     final void performPause() {
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performPause:"
+                    + mComponent.getClassName());
+        }
         dispatchActivityPrePaused();
         mDoReportFullyDrawn = false;
         mFragments.dispatchPause();
@@ -8073,6 +8091,7 @@
                     " did not call through to super.onPause()");
         }
         dispatchActivityPostPaused();
+        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
     }
 
     final void performUserLeaving() {
@@ -8081,6 +8100,10 @@
     }
 
     final void performStop(boolean preserveWindow, String reason) {
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performStop:"
+                    + mComponent.getClassName());
+        }
         mDoReportFullyDrawn = false;
         mFragments.doLoaderStop(mChangingConfigurations /*retain*/);
 
@@ -8126,9 +8149,14 @@
             dispatchActivityPostStopped();
         }
         mResumed = false;
+        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
     }
 
     final void performDestroy() {
+        if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performDestroy:"
+                    + mComponent.getClassName());
+        }
         dispatchActivityPreDestroyed();
         mDestroyed = true;
         mWindow.destroy();
@@ -8141,6 +8169,7 @@
             mVoiceInteractor.detachActivity();
         }
         dispatchActivityPostDestroyed();
+        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
     }
 
     final void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode,
@@ -8450,9 +8479,8 @@
     }
 
     /** @hide */
-    @Override
     @Nullable
-    public final View autofillClientFindViewByAutofillIdTraversal(AutofillId autofillId) {
+    public View findViewByAutofillIdTraversal(@NonNull AutofillId autofillId) {
         final ArrayList<ViewRootImpl> roots =
                 WindowManagerGlobal.getInstance().getRootViews(getActivityToken());
         for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
@@ -8465,12 +8493,18 @@
                 }
             }
         }
-
         return null;
     }
 
     /** @hide */
     @Override
+    @Nullable
+    public final View autofillClientFindViewByAutofillIdTraversal(AutofillId autofillId) {
+        return findViewByAutofillIdTraversal(autofillId);
+    }
+
+    /** @hide */
+    @Override
     public final @NonNull boolean[] autofillClientGetViewVisibility(
             @NonNull AutofillId[] autofillIds) {
         final int autofillIdCount = autofillIds.length;
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index d465b22..401f8cc 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -26,6 +26,8 @@
 import android.util.Singleton;
 import android.view.RemoteAnimationDefinition;
 
+import com.android.internal.policy.IKeyguardDismissCallback;
+
 /**
  * Provides the activity associated operations that communicate with system.
  *
@@ -431,6 +433,37 @@
         }
     }
 
+    /**
+     * Restart the process and activity to adopt the latest configuration for size compat mode.
+     * This only takes effect for visible activity because invisible background activity can be
+     * restarted naturally when it becomes visible.
+     */
+    public void restartActivityProcessIfVisible(IBinder token) {
+        try {
+            getActivityClientController().restartActivityProcessIfVisible(token);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /** Removes the snapshot of home task. */
+    public void invalidateHomeTaskSnapshot(IBinder homeToken) {
+        try {
+            getActivityClientController().invalidateHomeTaskSnapshot(homeToken);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback,
+            CharSequence message) {
+        try {
+            getActivityClientController().dismissKeyguard(token, callback, message);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
     void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) {
         try {
             getActivityClientController().registerRemoteAnimations(token, definition);
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index ebf1027..c2c62c1 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -25,6 +25,8 @@
 import android.os.PersistableBundle;
 import android.view.RemoteAnimationDefinition;
 
+import com.android.internal.policy.IKeyguardDismissCallback;
+
 /**
  * Interface for the callback and request from an activity to system.
  *
@@ -34,7 +36,13 @@
     oneway void activityIdle(in IBinder token, in Configuration config, in boolean stopProfiling);
     oneway void activityResumed(in IBinder token);
     oneway void activityTopResumedStateLost();
-    oneway void activityPaused(in IBinder token);
+    /**
+     * Notifies that the activity has completed paused. This call is not one-way because it can make
+     * consecutive launch in the same process more coherent. About the order of binder call, it
+     * should be fine with other one-way calls because if pause hasn't completed on the server side,
+     * there won't be other lifecycle changes.
+     */
+    void activityPaused(in IBinder token);
     oneway void activityStopped(in IBinder token, in Bundle state,
             in PersistableBundle persistentState, in CharSequence description);
     oneway void activityDestroyed(in IBinder token);
@@ -95,6 +103,27 @@
     /** See {@link android.app.Activity#setDisablePreviewScreenshots}. */
     oneway void setDisablePreviewScreenshots(in IBinder token, boolean disable);
 
+    /**
+     * Restarts the activity by killing its process if it is visible. If the activity is not
+     * visible, the activity will not be restarted immediately and just keep the activity record in
+     * the stack. It also resets the current override configuration so the activity will use the
+     * configuration according to the latest state.
+     *
+     * @param activityToken The token of the target activity to restart.
+     */
+    void restartActivityProcessIfVisible(in IBinder activityToken);
+
+    /**
+     * It should only be called from home activity to remove its outdated snapshot. The home
+     * snapshot is used to speed up entering home from screen off. If the content of home activity
+     * is significantly different from before taking the snapshot, then the home activity can use
+     * this method to avoid inconsistent transition.
+     */
+    void invalidateHomeTaskSnapshot(IBinder homeToken);
+
+    void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback,
+            in CharSequence message);
+
     /** Registers remote animations for a specific activity. */
     void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition);
 
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index b085340..38a3e70 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -72,7 +72,6 @@
 import android.window.IWindowOrganizerController;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.os.IResultReceiver;
-import com.android.internal.policy.IKeyguardDismissCallback;
 
 import java.util.List;
 
@@ -85,8 +84,6 @@
 // TODO(b/174040395): Make this interface private to ActivityTaskManager.java and have external
 // caller go through that call instead. This would help us better separate and control the API
 // surface exposed.
-// TODO(b/174041144): Move callback methods from Activity (Things that take param 'IBinder token')
-// to a separate interface that is only available to the Activity.
 // TODO(b/174041603): Create a builder interface for things like startActivityXXX(...) to reduce
 // interface duplication.
 // TODO(b/174040691): Clean-up/remove all obsolete or unused interfaces like things that should be
@@ -294,9 +291,6 @@
     // Get device configuration
     ConfigurationInfo getDeviceConfigurationInfo();
 
-    void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback,
-            in CharSequence message);
-
     /** Cancels the window transitions for the given task. */
     void cancelTaskWindowTransition(int taskId);
 
@@ -309,14 +303,6 @@
     android.window.TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution);
 
     /**
-     * It should only be called from home activity to remove its outdated snapshot. The home
-     * snapshot is used to speed up entering home from screen off. If the content of home activity
-     * is significantly different from before taking the snapshot, then the home activity can use
-     * this method to avoid inconsistent transition.
-     */
-    void invalidateHomeTaskSnapshot(IBinder homeToken);
-
-    /**
      * Return the user id of last resumed activity.
      */
     int getLastResumedActivityUserId();
@@ -362,14 +348,4 @@
      * Clears launch params for given packages.
      */
     void clearLaunchParamsForPackages(in List<String> packageNames);
-
-    /**
-     * Restarts the activity by killing its process if it is visible. If the activity is not
-     * visible, the activity will not be restarted immediately and just keep the activity record in
-     * the stack. It also resets the current override configuration so the activity will use the
-     * configuration according to the latest state.
-     *
-     * @param activityToken The token of the target activity to restart.
-     */
-    void restartActivityProcessIfVisible(in IBinder activityToken);
 }
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 545c3f7..b6d25cf 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -598,33 +598,29 @@
     @SystemApi
     public void requestDismissKeyguard(@NonNull Activity activity, @Nullable CharSequence message,
             @Nullable KeyguardDismissCallback callback) {
-        try {
-            ActivityTaskManager.getService().dismissKeyguard(
-                    activity.getActivityToken(), new IKeyguardDismissCallback.Stub() {
-                @Override
-                public void onDismissError() throws RemoteException {
-                    if (callback != null && !activity.isDestroyed()) {
-                        activity.mHandler.post(callback::onDismissError);
-                    }
+        ActivityClient.getInstance().dismissKeyguard(
+                activity.getActivityToken(), new IKeyguardDismissCallback.Stub() {
+            @Override
+            public void onDismissError() throws RemoteException {
+                if (callback != null && !activity.isDestroyed()) {
+                    activity.mHandler.post(callback::onDismissError);
                 }
+            }
 
-                @Override
-                public void onDismissSucceeded() throws RemoteException {
-                    if (callback != null && !activity.isDestroyed()) {
-                        activity.mHandler.post(callback::onDismissSucceeded);
-                    }
+            @Override
+            public void onDismissSucceeded() throws RemoteException {
+                if (callback != null && !activity.isDestroyed()) {
+                    activity.mHandler.post(callback::onDismissSucceeded);
                 }
+            }
 
-                @Override
-                public void onDismissCancelled() throws RemoteException {
-                    if (callback != null && !activity.isDestroyed()) {
-                        activity.mHandler.post(callback::onDismissCancelled);
-                    }
+            @Override
+            public void onDismissCancelled() throws RemoteException {
+                if (callback != null && !activity.isDestroyed()) {
+                    activity.mHandler.post(callback::onDismissCancelled);
                 }
-            }, message);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+            }
+        }, message);
     }
 
     /**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c5206d7..0c50446 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -64,7 +64,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.StatFs;
-import android.os.StrictMode;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
@@ -3575,6 +3574,7 @@
             LIGHTS_SERVICE,
             //@hide: PEOPLE_SERVICE,
             //@hide: DEVICE_STATE_SERVICE,
+            //@hide: SPEECH_RECOGNITION_SERVICE,
             UWB_SERVICE,
             MEDIA_METRICS_SERVICE,
     })
@@ -5410,6 +5410,14 @@
     public static final String MEDIA_METRICS_SERVICE = "media_metrics";
 
     /**
+     * Use with {@link #getSystemService(String)} to access system speech recognition service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+    */
+    public static final String SPEECH_RECOGNITION_SERVICE = "speech_recognition";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
@@ -6469,7 +6477,7 @@
      * {@link WindowManager}, {@link android.view.LayoutInflater LayoutInflater} or
      * {@link android.app.WallpaperManager WallpaperManager}. Accessing UI components from non-UI
      * contexts throws {@link android.os.strictmode.Violation} if
-     * {@link StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled.
+     * {@link android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled.
      * <p>
      * Examples of UI contexts are
      * an {@link android.app.Activity Activity}, a context created from
@@ -6479,7 +6487,7 @@
      *
      * @see #getDisplay()
      * @see #getSystemService(String)
-     * @see StrictMode.VmPolicy.Builder#detectIncorrectContextUse()
+     * @see android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse()
      */
     public static boolean isUiContext(@NonNull Context context) {
         return context.isUiContext();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index fcd573e..68792b2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3971,6 +3971,15 @@
     public static final int FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT =  1 << 18;
 
     /**
+     * Permission flag: This location permission is selected as the level of granularity of
+     * location accuracy.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY =  1 << 19;
+
+    /**
      * Permission flags: Reserved for use by the permission controller. The platform and any
      * packages besides the permission controller should not assume any definition about these
      * flags.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 45f072a..fe8bf9d 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -30,6 +30,7 @@
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
@@ -53,8 +54,6 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.split.DefaultSplitAssetLoader;
-import android.content.pm.split.SplitAssetDependencyLoader;
 import android.content.pm.split.SplitAssetLoader;
 import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
@@ -80,6 +79,7 @@
 import android.util.AttributeSet;
 import android.util.Base64;
 import android.util.DisplayMetrics;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.PackageUtils;
 import android.util.Pair;
@@ -118,6 +118,7 @@
 import java.security.spec.X509EncodedKeySpec;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.BitSet;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Iterator;
@@ -8573,4 +8574,410 @@
             this.error = error;
         }
     }
+
+    // Duplicate the SplitAsset related classes with PackageParser.Package/ApkLite here, and
+    // change the original one using new Package/ApkLite. The propose is that we don't want to
+    // have two branches of methods in SplitAsset related classes so we can keep real classes
+    // clean and move all the legacy code to one place.
+
+    /**
+     * A helper class that implements the dependency tree traversal for splits. Callbacks
+     * are implemented by subclasses to notify whether a split has already been constructed
+     * and is cached, and to actually create the split requested.
+     *
+     * This helper is meant to be subclassed so as to reduce the number of allocations
+     * needed to make use of it.
+     *
+     * All inputs and outputs are assumed to be indices into an array of splits.
+     *
+     * @hide
+     * @deprecated Do not use. New changes should use
+     * {@link android.content.pm.split.SplitDependencyLoader} instead.
+     */
+    @Deprecated
+    private abstract static class SplitDependencyLoader<E extends Exception> {
+        private final @NonNull SparseArray<int[]> mDependencies;
+
+        /**
+         * Construct a new SplitDependencyLoader. Meant to be called from the
+         * subclass constructor.
+         * @param dependencies The dependency tree of splits.
+         */
+        protected SplitDependencyLoader(@NonNull SparseArray<int[]> dependencies) {
+            mDependencies = dependencies;
+        }
+
+        /**
+         * Traverses the dependency tree and constructs any splits that are not already
+         * cached. This routine short-circuits and skips the creation of splits closer to the
+         * root if they are cached, as reported by the subclass implementation of
+         * {@link #isSplitCached(int)}. The construction of splits is delegated to the subclass
+         * implementation of {@link #constructSplit(int, int[], int)}.
+         * @param splitIdx The index of the split to load. 0 represents the base Application.
+         */
+        protected void loadDependenciesForSplit(@IntRange(from = 0) int splitIdx) throws E {
+            // Quick check before any allocations are done.
+            if (isSplitCached(splitIdx)) {
+                return;
+            }
+
+            // Special case the base, since it has no dependencies.
+            if (splitIdx == 0) {
+                final int[] configSplitIndices = collectConfigSplitIndices(0);
+                constructSplit(0, configSplitIndices, -1);
+                return;
+            }
+
+            // Build up the dependency hierarchy.
+            final IntArray linearDependencies = new IntArray();
+            linearDependencies.add(splitIdx);
+
+            // Collect all the dependencies that need to be constructed.
+            // They will be listed from leaf to root.
+            while (true) {
+                // Only follow the first index into the array. The others are config splits and
+                // get loaded with the split.
+                final int[] deps = mDependencies.get(splitIdx);
+                if (deps != null && deps.length > 0) {
+                    splitIdx = deps[0];
+                } else {
+                    splitIdx = -1;
+                }
+
+                if (splitIdx < 0 || isSplitCached(splitIdx)) {
+                    break;
+                }
+
+                linearDependencies.add(splitIdx);
+            }
+
+            // Visit each index, from right to left (root to leaf).
+            int parentIdx = splitIdx;
+            for (int i = linearDependencies.size() - 1; i >= 0; i--) {
+                final int idx = linearDependencies.get(i);
+                final int[] configSplitIndices = collectConfigSplitIndices(idx);
+                constructSplit(idx, configSplitIndices, parentIdx);
+                parentIdx = idx;
+            }
+        }
+
+        private @NonNull int[] collectConfigSplitIndices(int splitIdx) {
+            // The config splits appear after the first element.
+            final int[] deps = mDependencies.get(splitIdx);
+            if (deps == null || deps.length <= 1) {
+                return EmptyArray.INT;
+            }
+            return Arrays.copyOfRange(deps, 1, deps.length);
+        }
+
+        /**
+         * Subclass to report whether the split at `splitIdx` is cached and need not be constructed.
+         * It is assumed that if `splitIdx` is cached, any parent of `splitIdx` is also cached.
+         * @param splitIdx The index of the split to check for in the cache.
+         * @return true if the split is cached and does not need to be constructed.
+         */
+        protected abstract boolean isSplitCached(@IntRange(from = 0) int splitIdx);
+
+        /**
+         * Subclass to construct a split at index `splitIdx` with parent split `parentSplitIdx`.
+         * The result is expected to be cached by the subclass in its own structures.
+         * @param splitIdx The index of the split to construct. 0 represents the base Application.
+         * @param configSplitIndices The array of configuration splits to load along with this
+         *                           split. May be empty (length == 0) but never null.
+         * @param parentSplitIdx The index of the parent split. -1 if there is no parent.
+         * @throws E Subclass defined exception representing failure to construct a split.
+         */
+        protected abstract void constructSplit(@IntRange(from = 0) int splitIdx,
+                @NonNull @IntRange(from = 1) int[] configSplitIndices,
+                @IntRange(from = -1) int parentSplitIdx) throws E;
+
+        public static class IllegalDependencyException extends Exception {
+            private IllegalDependencyException(String message) {
+                super(message);
+            }
+        }
+
+        private static int[] append(int[] src, int elem) {
+            if (src == null) {
+                return new int[] { elem };
+            }
+            int[] dst = Arrays.copyOf(src, src.length + 1);
+            dst[src.length] = elem;
+            return dst;
+        }
+
+        public static @NonNull SparseArray<int[]> createDependenciesFromPackage(
+                PackageLite pkg)
+                throws SplitDependencyLoader.IllegalDependencyException {
+            // The data structure that holds the dependencies. In PackageParser, splits are stored
+            // in their own array, separate from the base. We treat all paths as equals, so
+            // we need to insert the base as index 0, and shift all other splits.
+            final SparseArray<int[]> splitDependencies = new SparseArray<>();
+
+            // The base depends on nothing.
+            splitDependencies.put(0, new int[] {-1});
+
+            // First write out the <uses-split> dependencies. These must appear first in the
+            // array of ints, as is convention in this class.
+            for (int splitIdx = 0; splitIdx < pkg.splitNames.length; splitIdx++) {
+                if (!pkg.isFeatureSplits[splitIdx]) {
+                    // Non-feature splits don't have dependencies.
+                    continue;
+                }
+
+                // Implicit dependency on the base.
+                final int targetIdx;
+                final String splitDependency = pkg.usesSplitNames[splitIdx];
+                if (splitDependency != null) {
+                    final int depIdx = Arrays.binarySearch(pkg.splitNames, splitDependency);
+                    if (depIdx < 0) {
+                        throw new SplitDependencyLoader.IllegalDependencyException(
+                                "Split '" + pkg.splitNames[splitIdx] + "' requires split '"
+                                        + splitDependency + "', which is missing.");
+                    }
+                    targetIdx = depIdx + 1;
+                } else {
+                    // Implicitly depend on the base.
+                    targetIdx = 0;
+                }
+                splitDependencies.put(splitIdx + 1, new int[] {targetIdx});
+            }
+
+            // Write out the configForSplit reverse-dependencies. These appear after the
+            // <uses-split> dependencies and are considered leaves.
+            //
+            // At this point, all splits in splitDependencies have the first element in their
+            // array set.
+            for (int splitIdx = 0, size = pkg.splitNames.length; splitIdx < size; splitIdx++) {
+                if (pkg.isFeatureSplits[splitIdx]) {
+                    // Feature splits are not configForSplits.
+                    continue;
+                }
+
+                // Implicit feature for the base.
+                final int targetSplitIdx;
+                final String configForSplit = pkg.configForSplit[splitIdx];
+                if (configForSplit != null) {
+                    final int depIdx = Arrays.binarySearch(pkg.splitNames, configForSplit);
+                    if (depIdx < 0) {
+                        throw new SplitDependencyLoader.IllegalDependencyException(
+                                "Split '" + pkg.splitNames[splitIdx] + "' targets split '"
+                                        + configForSplit + "', which is missing.");
+                    }
+
+                    if (!pkg.isFeatureSplits[depIdx]) {
+                        throw new SplitDependencyLoader.IllegalDependencyException(
+                                "Split '" + pkg.splitNames[splitIdx] + "' declares itself as "
+                                        + "configuration split for a non-feature split '"
+                                        + pkg.splitNames[depIdx] + "'");
+                    }
+                    targetSplitIdx = depIdx + 1;
+                } else {
+                    targetSplitIdx = 0;
+                }
+                splitDependencies.put(targetSplitIdx,
+                        append(splitDependencies.get(targetSplitIdx), splitIdx + 1));
+            }
+
+            // Verify that there are no cycles.
+            final BitSet bitset = new BitSet();
+            for (int i = 0, size = splitDependencies.size(); i < size; i++) {
+                int splitIdx = splitDependencies.keyAt(i);
+
+                bitset.clear();
+                while (splitIdx != -1) {
+                    // Check if this split has been visited yet.
+                    if (bitset.get(splitIdx)) {
+                        throw new SplitDependencyLoader.IllegalDependencyException(
+                                "Cycle detected in split dependencies.");
+                    }
+
+                    // Mark the split so that if we visit it again, we no there is a cycle.
+                    bitset.set(splitIdx);
+
+                    // Follow the first dependency only, the others are leaves by definition.
+                    final int[] deps = splitDependencies.get(splitIdx);
+                    splitIdx = deps != null ? deps[0] : -1;
+                }
+            }
+            return splitDependencies;
+        }
+    }
+
+    /**
+     * Loads the base and split APKs into a single AssetManager.
+     * @hide
+     * @deprecated Do not use. New changes should use
+     * {@link android.content.pm.split.DefaultSplitAssetLoader} instead.
+     */
+    @Deprecated
+    private static class DefaultSplitAssetLoader implements SplitAssetLoader {
+        private final String mBaseCodePath;
+        private final String[] mSplitCodePaths;
+        private final @ParseFlags int mFlags;
+        private AssetManager mCachedAssetManager;
+
+        DefaultSplitAssetLoader(PackageLite pkg, @ParseFlags int flags) {
+            mBaseCodePath = pkg.baseCodePath;
+            mSplitCodePaths = pkg.splitCodePaths;
+            mFlags = flags;
+        }
+
+        private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
+                throws PackageParserException {
+            if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+                        "Invalid package file: " + path);
+            }
+
+            try {
+                return ApkAssets.loadFromPath(path);
+            } catch (IOException e) {
+                throw new PackageParserException(INSTALL_FAILED_INVALID_APK,
+                        "Failed to load APK at path " + path, e);
+            }
+        }
+
+        @Override
+        public AssetManager getBaseAssetManager() throws PackageParserException {
+            if (mCachedAssetManager != null) {
+                return mCachedAssetManager;
+            }
+
+            ApkAssets[] apkAssets = new ApkAssets[(mSplitCodePaths != null
+                    ? mSplitCodePaths.length : 0) + 1];
+
+            // Load the base.
+            int splitIdx = 0;
+            apkAssets[splitIdx++] = loadApkAssets(mBaseCodePath, mFlags);
+
+            // Load any splits.
+            if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
+                for (String apkPath : mSplitCodePaths) {
+                    apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags);
+                }
+            }
+
+            AssetManager assets = new AssetManager();
+            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    Build.VERSION.RESOURCES_SDK_INT);
+            assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
+
+            mCachedAssetManager = assets;
+            return mCachedAssetManager;
+        }
+
+        @Override
+        public AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException {
+            return getBaseAssetManager();
+        }
+
+        @Override
+        public void close() throws Exception {
+            IoUtils.closeQuietly(mCachedAssetManager);
+        }
+    }
+
+    /**
+     * Loads AssetManagers for splits and their dependencies. This SplitAssetLoader implementation
+     * is to be used when an application opts-in to isolated split loading.
+     * @hide
+     * @deprecated Do not use. New changes should use
+     * {@link android.content.pm.split.SplitAssetDependencyLoader} instead.
+     */
+    @Deprecated
+    private static class SplitAssetDependencyLoader extends
+            SplitDependencyLoader<PackageParserException> implements SplitAssetLoader {
+        private final String[] mSplitPaths;
+        private final @ParseFlags int mFlags;
+        private final ApkAssets[][] mCachedSplitApks;
+        private final AssetManager[] mCachedAssetManagers;
+
+        SplitAssetDependencyLoader(PackageLite pkg,
+                SparseArray<int[]> dependencies, @ParseFlags int flags) {
+            super(dependencies);
+
+            // The base is inserted into index 0, so we need to shift all the splits by 1.
+            mSplitPaths = new String[pkg.splitCodePaths.length + 1];
+            mSplitPaths[0] = pkg.baseCodePath;
+            System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length);
+
+            mFlags = flags;
+            mCachedSplitApks = new ApkAssets[mSplitPaths.length][];
+            mCachedAssetManagers = new AssetManager[mSplitPaths.length];
+        }
+
+        @Override
+        protected boolean isSplitCached(int splitIdx) {
+            return mCachedAssetManagers[splitIdx] != null;
+        }
+
+        private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
+                throws PackageParserException {
+            if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+                        "Invalid package file: " + path);
+            }
+
+            try {
+                return ApkAssets.loadFromPath(path);
+            } catch (IOException e) {
+                throw new PackageParserException(PackageManager.INSTALL_FAILED_INVALID_APK,
+                        "Failed to load APK at path " + path, e);
+            }
+        }
+
+        private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
+            final AssetManager assets = new AssetManager();
+            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                    Build.VERSION.RESOURCES_SDK_INT);
+            assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
+            return assets;
+        }
+
+        @Override
+        protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
+                int parentSplitIdx) throws PackageParserException {
+            final ArrayList<ApkAssets> assets = new ArrayList<>();
+
+            // Include parent ApkAssets.
+            if (parentSplitIdx >= 0) {
+                Collections.addAll(assets, mCachedSplitApks[parentSplitIdx]);
+            }
+
+            // Include this ApkAssets.
+            assets.add(loadApkAssets(mSplitPaths[splitIdx], mFlags));
+
+            // Load and include all config splits for this feature.
+            for (int configSplitIdx : configSplitIndices) {
+                assets.add(loadApkAssets(mSplitPaths[configSplitIdx], mFlags));
+            }
+
+            // Cache the results.
+            mCachedSplitApks[splitIdx] = assets.toArray(new ApkAssets[assets.size()]);
+            mCachedAssetManagers[splitIdx] = createAssetManagerWithAssets(
+                    mCachedSplitApks[splitIdx]);
+        }
+
+        @Override
+        public AssetManager getBaseAssetManager() throws PackageParserException {
+            loadDependenciesForSplit(0);
+            return mCachedAssetManagers[0];
+        }
+
+        @Override
+        public AssetManager getSplitAssetManager(int idx) throws PackageParserException {
+            // Since we insert the base at position 0, and PackageParser keeps splits separate from
+            // the base, we need to adjust the index.
+            loadDependenciesForSplit(idx + 1);
+            return mCachedAssetManagers[idx + 1];
+        }
+
+        @Override
+        public void close() throws Exception {
+            for (AssetManager assets : mCachedAssetManagers) {
+                IoUtils.closeQuietly(assets);
+            }
+        }
+    }
 }
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
index 982fce9..bf35c4d 100644
--- a/core/java/android/content/pm/dex/DexMetadataHelper.java
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -17,11 +17,11 @@
 package android.content.pm.dex;
 
 import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_DEX_METADATA;
-import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
+import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION;
 
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
 import android.util.ArrayMap;
 import android.util.jar.StrictJarFile;
 
@@ -87,7 +87,7 @@
      * NOTE: involves I/O checks.
      */
     private static Map<String, String> getPackageDexMetadata(PackageLite pkg) {
-        return buildPackageApkToDexMetadataMap(pkg.getAllCodePaths());
+        return buildPackageApkToDexMetadataMap(pkg.getAllApkPaths());
     }
 
     /**
@@ -125,7 +125,7 @@
      * @throws IllegalArgumentException if the code path is not an .apk.
      */
     public static String buildDexMetadataPathForApk(String codePath) {
-        if (!PackageParser.isApkPath(codePath)) {
+        if (!ApkLiteParseUtils.isApkPath(codePath)) {
             throw new IllegalStateException(
                     "Corrupted package. Code path is not an apk " + codePath);
         }
@@ -140,7 +140,7 @@
      * extension (e.g. 'foo.dm' will match 'foo' or 'foo.apk').
      */
     private static String buildDexMetadataPathForFile(File targetFile) {
-        return PackageParser.isApkFile(targetFile)
+        return ApkLiteParseUtils.isApkFile(targetFile)
                 ? buildDexMetadataPathForApk(targetFile.getPath())
                 : targetFile.getPath() + DEX_METADATA_FILE_EXTENSION;
     }
@@ -179,7 +179,7 @@
     public static void validateDexPaths(String[] paths) {
         ArrayList<String> apks = new ArrayList<>();
         for (int i = 0; i < paths.length; i++) {
-            if (PackageParser.isApkPath(paths[i])) {
+            if (ApkLiteParseUtils.isApkPath(paths[i])) {
                 apks.add(paths[i]);
             }
         }
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 51b81b6..a3c2cbc 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -66,6 +66,8 @@
     private static final int PARSE_DEFAULT_INSTALL_LOCATION =
             PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
 
+    private static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
+
     public static final String APK_FILE_EXTENSION = ".apk";
 
     /**
@@ -79,7 +81,7 @@
      *
      * @see PackageParser#parsePackage(File, int)
      */
-    public static ParseResult<PackageParser.PackageLite> parsePackageLite(ParseInput input,
+    public static ParseResult<PackageLite> parsePackageLite(ParseInput input,
             File packageFile, int flags) {
         if (packageFile.isDirectory()) {
             return parseClusterPackageLite(input, packageFile, flags);
@@ -88,26 +90,32 @@
         }
     }
 
-    public static ParseResult<PackageParser.PackageLite> parseMonolithicPackageLite(
-            ParseInput input, File packageFile, int flags) {
+    /**
+     * Parse lightweight details about a single APK files.
+     */
+    public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input,
+            File packageFile, int flags) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
         try {
-            ParseResult<PackageParser.ApkLite> result = parseApkLite(input, packageFile, flags);
+            final ParseResult<ApkLite> result = parseApkLite(input, packageFile, flags);
             if (result.isError()) {
                 return input.error(result);
             }
 
-            final PackageParser.ApkLite baseApk = result.getResult();
+            final ApkLite baseApk = result.getResult();
             final String packagePath = packageFile.getAbsolutePath();
             return input.success(
-                    new PackageParser.PackageLite(packagePath, baseApk.codePath, baseApk, null,
+                    new PackageLite(packagePath, baseApk.getPath(), baseApk, null,
                             null, null, null, null, null));
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
     }
 
-    public static ParseResult<PackageParser.PackageLite> parseClusterPackageLite(ParseInput input,
+    /**
+     * Parse lightweight details about a directory of APKs.
+     */
+    public static ParseResult<PackageLite> parseClusterPackageLite(ParseInput input,
             File packageDir, int flags) {
         final File[] files = packageDir.listFiles();
         if (ArrayUtils.isEmpty(files)) {
@@ -122,39 +130,39 @@
         String packageName = null;
         int versionCode = 0;
 
-        final ArrayMap<String, PackageParser.ApkLite> apks = new ArrayMap<>();
+        final ArrayMap<String, ApkLite> apks = new ArrayMap<>();
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
         try {
             for (File file : files) {
-                if (PackageParser.isApkFile(file)) {
-                    ParseResult<PackageParser.ApkLite> result = parseApkLite(input, file, flags);
+                if (isApkFile(file)) {
+                    final ParseResult<ApkLite> result = parseApkLite(input, file, flags);
                     if (result.isError()) {
                         return input.error(result);
                     }
 
-                    final PackageParser.ApkLite lite = result.getResult();
+                    final ApkLite lite = result.getResult();
                     // Assert that all package names and version codes are
                     // consistent with the first one we encounter.
                     if (packageName == null) {
-                        packageName = lite.packageName;
-                        versionCode = lite.versionCode;
+                        packageName = lite.getPackageName();
+                        versionCode = lite.getVersionCode();
                     } else {
-                        if (!packageName.equals(lite.packageName)) {
+                        if (!packageName.equals(lite.getPackageName())) {
                             return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
-                                    "Inconsistent package " + lite.packageName + " in " + file
+                                    "Inconsistent package " + lite.getPackageName() + " in " + file
                                             + "; expected " + packageName);
                         }
-                        if (versionCode != lite.versionCode) {
+                        if (versionCode != lite.getVersionCode()) {
                             return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
-                                    "Inconsistent version " + lite.versionCode + " in " + file
+                                    "Inconsistent version " + lite.getVersionCode() + " in " + file
                                             + "; expected " + versionCode);
                         }
                     }
 
                     // Assert that each split is defined only oncuses-static-libe
-                    if (apks.put(lite.splitName, lite) != null) {
+                    if (apks.put(lite.getSplitName(), lite) != null) {
                         return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
-                                "Split name " + lite.splitName
+                                "Split name " + lite.getSplitName()
                                         + " defined more than once; most recent was " + file);
                     }
                 }
@@ -163,7 +171,7 @@
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
 
-        final PackageParser.ApkLite baseApk = apks.remove(null);
+        final ApkLite baseApk = apks.remove(null);
         return composePackageLiteFromApks(input, packageDir, baseApk, apks);
     }
 
@@ -176,9 +184,8 @@
      * @param splitApks Parsed split APKs
      * @return PackageLite
      */
-    public static ParseResult<PackageParser.PackageLite> composePackageLiteFromApks(
-            ParseInput input, File packageDir, PackageParser.ApkLite baseApk,
-            ArrayMap<String, PackageParser.ApkLite> splitApks) {
+    public static ParseResult<PackageLite> composePackageLiteFromApks(ParseInput input,
+            File packageDir, ApkLite baseApk, ArrayMap<String, ApkLite> splitApks) {
         return composePackageLiteFromApks(input, packageDir, baseApk, splitApks, false);
     }
 
@@ -192,9 +199,9 @@
      * @param apkRenamed Indicate whether the APKs are renamed after parsed.
      * @return PackageLite
      */
-    public static ParseResult<PackageParser.PackageLite> composePackageLiteFromApks(
-            ParseInput input, File packageDir, PackageParser.ApkLite baseApk,
-            ArrayMap<String, PackageParser.ApkLite> splitApks, boolean apkRenamed) {
+    public static ParseResult<PackageLite> composePackageLiteFromApks(
+            ParseInput input, File packageDir, ApkLite baseApk,
+            ArrayMap<String, ApkLite> splitApks, boolean apkRenamed) {
         if (baseApk == null) {
             return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
                     "Missing base APK in " + packageDir);
@@ -217,26 +224,25 @@
             splitRevisionCodes = new int[size];
 
             splitNames = splitApks.keySet().toArray(splitNames);
-            Arrays.sort(splitNames, PackageParser.sSplitNameComparator);
+            Arrays.sort(splitNames, sSplitNameComparator);
 
             for (int i = 0; i < size; i++) {
-                final PackageParser.ApkLite apk = splitApks.get(splitNames[i]);
-                usesSplitNames[i] = apk.usesSplitName;
-                isFeatureSplits[i] = apk.isFeatureSplit;
-                configForSplits[i] = apk.configForSplit;
+                final ApkLite apk = splitApks.get(splitNames[i]);
+                usesSplitNames[i] = apk.getUsesSplitName();
+                isFeatureSplits[i] = apk.isFeatureSplit();
+                configForSplits[i] = apk.getConfigForSplit();
                 splitCodePaths[i] = apkRenamed ? new File(packageDir,
-                        splitNameToFileName(apk)).getAbsolutePath() : apk.codePath;
-                splitRevisionCodes[i] = apk.revisionCode;
+                        splitNameToFileName(apk)).getAbsolutePath() : apk.getPath();
+                splitRevisionCodes[i] = apk.getRevisionCode();
             }
         }
 
         final String codePath = packageDir.getAbsolutePath();
         final String baseCodePath = apkRenamed ? new File(packageDir,
-                splitNameToFileName(baseApk)).getAbsolutePath() : baseApk.codePath;
+                splitNameToFileName(baseApk)).getAbsolutePath() : baseApk.getPath();
         return input.success(
-                new PackageParser.PackageLite(codePath, baseCodePath, baseApk, splitNames,
-                        isFeatureSplits, usesSplitNames, configForSplits, splitCodePaths,
-                        splitRevisionCodes));
+                new PackageLite(codePath, baseCodePath, baseApk, splitNames, isFeatureSplits,
+                        usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes));
     }
 
     /**
@@ -245,9 +251,9 @@
      * @param apk Parsed APK
      * @return The canonical file name
      */
-    public static String splitNameToFileName(@NonNull PackageParser.ApkLite apk) {
+    public static String splitNameToFileName(@NonNull ApkLite apk) {
         Objects.requireNonNull(apk);
-        final String fileName = apk.splitName == null ? "base" : "split_" + apk.splitName;
+        final String fileName = apk.getSplitName() == null ? "base" : "split_" + apk.getSplitName();
         return fileName + APK_FILE_EXTENSION;
     }
 
@@ -257,10 +263,9 @@
      *
      * @param apkFile path to a single APK
      * @param flags optional parse flags, such as
-     *            {@link PackageParser#PARSE_COLLECT_CERTIFICATES}
+     *            {@link ParsingPackageUtils#PARSE_COLLECT_CERTIFICATES}
      */
-    public static ParseResult<PackageParser.ApkLite> parseApkLite(ParseInput input, File apkFile,
-            int flags) {
+    public static ParseResult<ApkLite> parseApkLite(ParseInput input, File apkFile, int flags) {
         return parseApkLiteInner(input, apkFile, null, null, flags);
     }
 
@@ -271,14 +276,14 @@
      * @param fd already open file descriptor of an apk file
      * @param debugPathName arbitrary text name for this file, for debug output
      * @param flags optional parse flags, such as
-     *            {@link PackageParser#PARSE_COLLECT_CERTIFICATES}
+     *            {@link ParsingPackageUtils#PARSE_COLLECT_CERTIFICATES}
      */
-    public static ParseResult<PackageParser.ApkLite> parseApkLite(ParseInput input,
+    public static ParseResult<ApkLite> parseApkLite(ParseInput input,
             FileDescriptor fd, String debugPathName, int flags) {
         return parseApkLiteInner(input, null, fd, debugPathName, flags);
     }
 
-    private static ParseResult<PackageParser.ApkLite> parseApkLiteInner(ParseInput input,
+    private static ParseResult<ApkLite> parseApkLiteInner(ParseInput input,
             File apkFile, FileDescriptor fd, String debugPathName, int flags) {
         final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
 
@@ -294,11 +299,11 @@
                         "Failed to parse " + apkPath, e);
             }
 
-            parser = apkAssets.openXml(PackageParser.ANDROID_MANIFEST_FILENAME);
+            parser = apkAssets.openXml(ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
 
             final PackageParser.SigningDetails signingDetails;
-            if ((flags & PackageParser.PARSE_COLLECT_CERTIFICATES) != 0) {
-                final boolean skipVerify = (flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0;
+            if ((flags & ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES) != 0) {
+                final boolean skipVerify = (flags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
                 try {
                     ParseResult<PackageParser.SigningDetails> result =
@@ -335,9 +340,8 @@
         }
     }
 
-    private static ParseResult<PackageParser.ApkLite> parseApkLite(ParseInput input,
-            String codePath, XmlPullParser parser, AttributeSet attrs,
-            PackageParser.SigningDetails signingDetails)
+    private static ParseResult<ApkLite> parseApkLite(ParseInput input, String codePath,
+            XmlPullParser parser, AttributeSet attrs, PackageParser.SigningDetails signingDetails)
             throws IOException, XmlPullParserException {
         ParseResult<Pair<String, String>> result = parsePackageSplitNames(input, parser, attrs);
         if (result.isError()) {
@@ -421,12 +425,12 @@
                 continue;
             }
 
-            if (PackageParser.TAG_PACKAGE_VERIFIER.equals(parser.getName())) {
+            if (ParsingPackageUtils.TAG_PACKAGE_VERIFIER.equals(parser.getName())) {
                 final VerifierInfo verifier = parseVerifier(attrs);
                 if (verifier != null) {
                     verifiers.add(verifier);
                 }
-            } else if (PackageParser.TAG_APPLICATION.equals(parser.getName())) {
+            } else if (ParsingPackageUtils.TAG_APPLICATION.equals(parser.getName())) {
                 for (int i = 0; i < attrs.getAttributeCount(); ++i) {
                     final String attr = attrs.getAttributeName(i);
                     switch (attr) {
@@ -464,7 +468,7 @@
                         continue;
                     }
 
-                    if (PackageParser.TAG_PROFILEABLE.equals(parser.getName())) {
+                    if (ParsingPackageUtils.TAG_PROFILEABLE.equals(parser.getName())) {
                         for (int i = 0; i < attrs.getAttributeCount(); ++i) {
                             final String attr = attrs.getAttributeName(i);
                             if ("shell".equals(attr)) {
@@ -474,7 +478,7 @@
                         }
                     }
                 }
-            } else if (PackageParser.TAG_OVERLAY.equals(parser.getName())) {
+            } else if (ParsingPackageUtils.TAG_OVERLAY.equals(parser.getName())) {
                 for (int i = 0; i < attrs.getAttributeCount(); ++i) {
                     final String attr = attrs.getAttributeName(i);
                     if ("requiredSystemPropertyName".equals(attr)) {
@@ -489,7 +493,7 @@
                         overlayPriority = attrs.getAttributeIntValue(i, 0);
                     }
                 }
-            } else if (PackageParser.TAG_USES_SPLIT.equals(parser.getName())) {
+            } else if (ParsingPackageUtils.TAG_USES_SPLIT.equals(parser.getName())) {
                 if (usesSplitName != null) {
                     Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others.");
                     continue;
@@ -500,7 +504,7 @@
                     return input.error(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                             "<uses-split> tag requires 'android:name' attribute");
                 }
-            } else if (PackageParser.TAG_USES_SDK.equals(parser.getName())) {
+            } else if (ParsingPackageUtils.TAG_USES_SDK.equals(parser.getName())) {
                 for (int i = 0; i < attrs.getAttributeCount(); ++i) {
                     final String attr = attrs.getAttributeName(i);
                     if ("targetSdkVersion".equals(attr)) {
@@ -526,8 +530,8 @@
         }
 
         return input.success(
-                new PackageParser.ApkLite(codePath, packageSplit.first, packageSplit.second,
-                        isFeatureSplit, configForSplit, usesSplitName, isSplitRequired, versionCode,
+                new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
+                        configForSplit, usesSplitName, isSplitRequired, versionCode,
                         versionCodeMajor, revisionCode, installLocation, verifiers, signingDetails,
                         coreApp, debuggable, profilableByShell, multiArch, use32bitAbi,
                         useEmbeddedDex, extractNativeLibs, isolatedSplits, targetPackage,
@@ -546,7 +550,7 @@
             return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                     "No start tag found");
         }
-        if (!parser.getName().equals(PackageParser.TAG_MANIFEST)) {
+        if (!parser.getName().equals(ParsingPackageUtils.TAG_MANIFEST)) {
             return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                     "No <manifest> tag");
         }
@@ -625,4 +629,24 @@
             }
         }
     }
+
+    /**
+     * Check if the given file is an APK file.
+     *
+     * @param file the file to check.
+     * @return {@code true} if the given file is an APK file.
+     */
+    public static boolean isApkFile(File file) {
+        return isApkPath(file.getName());
+    }
+
+    /**
+     * Check if the given path ends with APK file extension.
+     *
+     * @param path the path to check.
+     * @return {@code true} if the given path ends with APK file extension.
+     */
+    public static boolean isApkPath(String path) {
+        return path.endsWith(APK_FILE_EXTENSION);
+    }
 }
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index f8fd4a5..b7365b3 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -385,7 +385,7 @@
         }
 
         // CompatibilityMode is global state.
-        if (!PackageParser.sCompatibilityModeEnabled) {
+        if (!ParsingPackageUtils.sCompatibilityModeEnabled) {
             ai.disableCompatibilityMode();
         }
 
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 38d3940..51ec297 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -335,7 +335,7 @@
 
     private int fullBackupContent;
     private int iconRes;
-    private int installLocation = PackageParser.PARSE_DEFAULT_INSTALL_LOCATION;
+    private int installLocation = ParsingPackageUtils.PARSE_DEFAULT_INSTALL_LOCATION;
     private int labelRes;
     private int largestWidthLimitDp;
     private int logo;
@@ -1013,7 +1013,8 @@
         // TODO(b/135203078): See ParsingPackageImpl#getHiddenApiEnforcementPolicy
 //        appInfo.mHiddenApiPolicy
 //        appInfo.hiddenUntilInstalled
-        appInfo.icon = (PackageParser.sUseRoundIcon && roundIconRes != 0) ? roundIconRes : iconRes;
+        appInfo.icon =
+                (ParsingPackageUtils.sUseRoundIcon && roundIconRes != 0) ? roundIconRes : iconRes;
         appInfo.iconRes = iconRes;
         appInfo.roundIconRes = roundIconRes;
         appInfo.installLocation = installLocation;
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 13ae7a2..a102e82 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -66,7 +66,7 @@
 
     /**
      * The names of packages to adopt ownership of permissions from, parsed under
-     * {@link PackageParser#TAG_ADOPT_PERMISSIONS}.
+     * {@link ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}.
      * @see R.styleable#AndroidManifestOriginalPackage_name
      */
     @NonNull
@@ -105,7 +105,7 @@
 
     /**
      * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in
-     * {@link PackageParser#TAG_KEY_SETS}.
+     * {@link ParsingPackageUtils#TAG_KEY_SETS}.
      * @see R.styleable#AndroidManifestKeySet
      * @see R.styleable#AndroidManifestPublicKey
      */
@@ -816,7 +816,7 @@
 
     /**
      * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in
-     * {@link PackageParser#TAG_KEY_SETS}.
+     * {@link ParsingPackageUtils#TAG_KEY_SETS}.
      * @see R.styleable#AndroidManifestUpgradeKeySet
      */
     @NonNull
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 494b3cc..b054304 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -189,6 +189,12 @@
             PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
     public static final int PARSE_DEFAULT_TARGET_SANDBOX = 1;
 
+    /** If set to true, we will only allow package files that exactly match
+     *  the DTD. Otherwise, we try to get as much from the package as we
+     *  can without failing. This should normally be set to false, to
+     *  support extensions to the DTD in future versions. */
+    public static final boolean RIGID_PARSER = false;
+
     public static final int PARSE_MUST_BE_APK = 1 << 0;
     public static final int PARSE_IGNORE_PROCESSES = 1 << 1;
     public static final int PARSE_EXTERNAL_STORAGE = 1 << 3;
@@ -220,7 +226,7 @@
      */
     @NonNull
     public static ParseResult<ParsingPackage> parseDefaultOneTime(File file,
-            @PackageParser.ParseFlags int parseFlags, boolean collectCertificates) {
+            @ParseFlags int parseFlags, boolean collectCertificates) {
         ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
         return parseDefault(input, file, parseFlags, collectCertificates);
     }
@@ -232,7 +238,7 @@
      */
     @NonNull
     public static ParseResult<ParsingPackage> parseDefault(ParseInput input, File file,
-            @PackageParser.ParseFlags int parseFlags, boolean collectCertificates) {
+            @ParseFlags int parseFlags, boolean collectCertificates) {
         ParseResult<ParsingPackage> result;
 
         ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, new Callback() {
@@ -334,14 +340,14 @@
      */
     private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
             int flags) {
-        ParseResult<PackageParser.PackageLite> liteResult =
+        final ParseResult<PackageLite> liteResult =
                 ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, 0);
         if (liteResult.isError()) {
             return input.error(liteResult);
         }
 
-        final PackageParser.PackageLite lite = liteResult.getResult();
-        if (mOnlyCoreApps && !lite.coreApp) {
+        final PackageLite lite = liteResult.getResult();
+        if (mOnlyCoreApps && !lite.isCoreApp()) {
             return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
                     "Not a coreApp: " + packageDir);
         }
@@ -349,7 +355,7 @@
         // Build the split dependency tree.
         SparseArray<int[]> splitDependencies = null;
         final SplitAssetLoader assetLoader;
-        if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
+        if (lite.isIsolatedSplits() && !ArrayUtils.isEmpty(lite.getSplitNames())) {
             try {
                 splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
                 assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
@@ -362,22 +368,22 @@
 
         try {
             final AssetManager assets = assetLoader.getBaseAssetManager();
-            final File baseApk = new File(lite.baseCodePath);
-            ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
-                    lite.codePath, assets, flags);
+            final File baseApk = new File(lite.getBaseApkPath());
+            final ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
+                    lite.getPath(), assets, flags);
             if (result.isError()) {
                 return input.error(result);
             }
 
             ParsingPackage pkg = result.getResult();
-            if (!ArrayUtils.isEmpty(lite.splitNames)) {
+            if (!ArrayUtils.isEmpty(lite.getSplitNames())) {
                 pkg.asSplit(
-                        lite.splitNames,
-                        lite.splitCodePaths,
-                        lite.splitRevisionCodes,
+                        lite.getSplitNames(),
+                        lite.getSplitApkPaths(),
+                        lite.getSplitRevisionCodes(),
                         splitDependencies
                 );
-                final int num = lite.splitNames.length;
+                final int num = lite.getSplitNames().length;
 
                 for (int i = 0; i < num; i++) {
                     final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
@@ -385,11 +391,11 @@
                 }
             }
 
-            pkg.setUse32BitAbi(lite.use32bitAbi);
+            pkg.setUse32BitAbi(lite.isUse32bitAbi());
             return input.success(pkg);
         } catch (PackageParserException e) {
             return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
-                    "Failed to load assets: " + lite.baseCodePath, e);
+                    "Failed to load assets: " + lite.getBaseApkPath(), e);
         } finally {
             IoUtils.closeQuietly(assetLoader);
         }
@@ -403,21 +409,21 @@
      */
     private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
             int flags) throws PackageParserException {
-        ParseResult<PackageParser.PackageLite> liteResult =
+        final ParseResult<PackageLite> liteResult =
                 ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags);
         if (liteResult.isError()) {
             return input.error(liteResult);
         }
 
-        final PackageParser.PackageLite lite = liteResult.getResult();
-        if (mOnlyCoreApps && !lite.coreApp) {
+        final PackageLite lite = liteResult.getResult();
+        if (mOnlyCoreApps && !lite.isCoreApp()) {
             return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
                     "Not a coreApp: " + apkFile);
         }
 
         final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
         try {
-            ParseResult<ParsingPackage> result = parseBaseApk(input,
+            final ParseResult<ParsingPackage> result = parseBaseApk(input,
                     apkFile,
                     apkFile.getCanonicalPath(),
                     assetLoader.getBaseAssetManager(), flags);
@@ -426,7 +432,7 @@
             }
 
             return input.success(result.getResult()
-                    .setUse32BitAbi(lite.use32bitAbi));
+                    .setUse32BitAbi(lite.isUse32bitAbi()));
         } catch (IOException e) {
             return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                     "Failed to get path: " + apkFile, e);
@@ -440,12 +446,12 @@
         final String apkPath = apkFile.getAbsolutePath();
 
         String volumeUuid = null;
-        if (apkPath.startsWith(PackageParser.MNT_EXPAND)) {
-            final int end = apkPath.indexOf('/', PackageParser.MNT_EXPAND.length());
-            volumeUuid = apkPath.substring(PackageParser.MNT_EXPAND.length(), end);
+        if (apkPath.startsWith(MNT_EXPAND)) {
+            final int end = apkPath.indexOf('/', MNT_EXPAND.length());
+            volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
         }
 
-        if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
+        if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
 
         final int cookie = assets.findCookieForPath(apkPath);
         if (cookie == 0) {
@@ -454,7 +460,7 @@
         }
 
         try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
-                PackageParser.ANDROID_MANIFEST_FILENAME)) {
+                ANDROID_MANIFEST_FILENAME)) {
             final Resources res = new Resources(assets, mDisplayMetrics, null);
 
             ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
@@ -495,7 +501,7 @@
 
             pkg.setVolumeUuid(volumeUuid);
 
-            if ((flags & PackageParser.PARSE_COLLECT_CERTIFICATES) != 0) {
+            if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
                 pkg.setSigningDetails(getSigningDetails(pkg, false));
             } else {
                 pkg.setSigningDetails(SigningDetails.UNKNOWN);
@@ -512,7 +518,7 @@
             ParsingPackage pkg, int splitIndex, AssetManager assets, int flags) {
         final String apkPath = pkg.getSplitCodePaths()[splitIndex];
 
-        if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
+        if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
 
         // This must always succeed, as the path has been added to the AssetManager before.
         final int cookie = assets.findCookieForPath(apkPath);
@@ -521,7 +527,7 @@
                     "Failed adding asset path: " + apkPath);
         }
         try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
-                PackageParser.ANDROID_MANIFEST_FILENAME)) {
+                ANDROID_MANIFEST_FILENAME)) {
             Resources res = new Resources(assets, mDisplayMetrics, null);
             ParseResult<ParsingPackage> parseResult = parseSplitApk(input, pkg, res,
                     parser, flags, splitIndex);
@@ -620,9 +626,9 @@
 
             final ParseResult result;
             String tagName = parser.getName();
-            if (PackageParser.TAG_APPLICATION.equals(tagName)) {
+            if (TAG_APPLICATION.equals(tagName)) {
                 if (foundApp) {
-                    if (PackageParser.RIGID_PARSER) {
+                    if (RIGID_PARSER) {
                         result = input.error("<manifest> has more than one <application>");
                     } else {
                         Slog.w(TAG, "<manifest> has more than one <application>");
@@ -701,7 +707,7 @@
                     ParseResult<ParsedActivity> activityResult =
                             ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
                                     res,
-                                    parser, flags, PackageParser.sUseRoundIcon, input);
+                                    parser, flags, sUseRoundIcon, input);
                     if (activityResult.isSuccess()) {
                         ParsedActivity activity = activityResult.getResult();
                         if (isActivity) {
@@ -716,7 +722,7 @@
                 case "service":
                     ParseResult<ParsedService> serviceResult = ParsedServiceUtils.parseService(
                             mSeparateProcesses, pkg, res, parser, flags,
-                            PackageParser.sUseRoundIcon, input);
+                            sUseRoundIcon, input);
                     if (serviceResult.isSuccess()) {
                         ParsedService service = serviceResult.getResult();
                         pkg.addService(service);
@@ -727,7 +733,7 @@
                 case "provider":
                     ParseResult<ParsedProvider> providerResult =
                             ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
-                                    flags, PackageParser.sUseRoundIcon, input);
+                                    flags, sUseRoundIcon, input);
                     if (providerResult.isSuccess()) {
                         ParsedProvider provider = providerResult.getResult();
                         pkg.addProvider(provider);
@@ -737,7 +743,7 @@
                     break;
                 case "activity-alias":
                     activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res, parser,
-                            PackageParser.sUseRoundIcon, input);
+                            sUseRoundIcon, input);
                     if (activityResult.isSuccess()) {
                         ParsedActivity activity = activityResult.getResult();
                         pkg.addActivity(activity);
@@ -815,12 +821,12 @@
             return sharedUserResult;
         }
 
-        pkg.setInstallLocation(anInteger(PackageParser.PARSE_DEFAULT_INSTALL_LOCATION,
+        pkg.setInstallLocation(anInteger(PARSE_DEFAULT_INSTALL_LOCATION,
                 R.styleable.AndroidManifest_installLocation, sa))
-                .setTargetSandboxVersion(anInteger(PackageParser.PARSE_DEFAULT_TARGET_SANDBOX,
+                .setTargetSandboxVersion(anInteger(PARSE_DEFAULT_TARGET_SANDBOX,
                         R.styleable.AndroidManifest_targetSandboxVersion, sa))
                 /* Set the global "on SD card" flag */
-                .setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0);
+                .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0);
 
         boolean foundApp = false;
         final int depth = parser.getDepth();
@@ -836,9 +842,9 @@
             final ParseResult result;
 
             // <application> has special logic, so it's handled outside the general method
-            if (PackageParser.TAG_APPLICATION.equals(tagName)) {
+            if (TAG_APPLICATION.equals(tagName)) {
                 if (foundApp) {
-                    if (PackageParser.RIGID_PARSER) {
+                    if (RIGID_PARSER) {
                         result = input.error("<manifest> has more than one <application>");
                     } else {
                         Slog.w(TAG, "<manifest> has more than one <application>");
@@ -897,51 +903,51 @@
             ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
             throws IOException, XmlPullParserException {
         switch (tag) {
-            case PackageParser.TAG_OVERLAY:
+            case TAG_OVERLAY:
                 return parseOverlay(input, pkg, res, parser);
-            case PackageParser.TAG_KEY_SETS:
+            case TAG_KEY_SETS:
                 return parseKeySets(input, pkg, res, parser);
             case "feature": // TODO moltmann: Remove
-            case PackageParser.TAG_ATTRIBUTION:
+            case TAG_ATTRIBUTION:
                 return parseAttribution(input, pkg, res, parser);
-            case PackageParser.TAG_PERMISSION_GROUP:
+            case TAG_PERMISSION_GROUP:
                 return parsePermissionGroup(input, pkg, res, parser);
-            case PackageParser.TAG_PERMISSION:
+            case TAG_PERMISSION:
                 return parsePermission(input, pkg, res, parser);
-            case PackageParser.TAG_PERMISSION_TREE:
+            case TAG_PERMISSION_TREE:
                 return parsePermissionTree(input, pkg, res, parser);
-            case PackageParser.TAG_USES_PERMISSION:
-            case PackageParser.TAG_USES_PERMISSION_SDK_M:
-            case PackageParser.TAG_USES_PERMISSION_SDK_23:
+            case TAG_USES_PERMISSION:
+            case TAG_USES_PERMISSION_SDK_M:
+            case TAG_USES_PERMISSION_SDK_23:
                 return parseUsesPermission(input, pkg, res, parser);
-            case PackageParser.TAG_USES_CONFIGURATION:
+            case TAG_USES_CONFIGURATION:
                 return parseUsesConfiguration(input, pkg, res, parser);
-            case PackageParser.TAG_USES_FEATURE:
+            case TAG_USES_FEATURE:
                 return parseUsesFeature(input, pkg, res, parser);
-            case PackageParser.TAG_FEATURE_GROUP:
+            case TAG_FEATURE_GROUP:
                 return parseFeatureGroup(input, pkg, res, parser);
-            case PackageParser.TAG_USES_SDK:
+            case TAG_USES_SDK:
                 return parseUsesSdk(input, pkg, res, parser);
-            case PackageParser.TAG_SUPPORT_SCREENS:
+            case TAG_SUPPORT_SCREENS:
                 return parseSupportScreens(input, pkg, res, parser);
-            case PackageParser.TAG_PROTECTED_BROADCAST:
+            case TAG_PROTECTED_BROADCAST:
                 return parseProtectedBroadcast(input, pkg, res, parser);
-            case PackageParser.TAG_INSTRUMENTATION:
+            case TAG_INSTRUMENTATION:
                 return parseInstrumentation(input, pkg, res, parser);
-            case PackageParser.TAG_ORIGINAL_PACKAGE:
+            case TAG_ORIGINAL_PACKAGE:
                 return parseOriginalPackage(input, pkg, res, parser);
-            case PackageParser.TAG_ADOPT_PERMISSIONS:
+            case TAG_ADOPT_PERMISSIONS:
                 return parseAdoptPermissions(input, pkg, res, parser);
-            case PackageParser.TAG_USES_GL_TEXTURE:
-            case PackageParser.TAG_COMPATIBLE_SCREENS:
-            case PackageParser.TAG_SUPPORTS_INPUT:
-            case PackageParser.TAG_EAT_COMMENT:
+            case TAG_USES_GL_TEXTURE:
+            case TAG_COMPATIBLE_SCREENS:
+            case TAG_SUPPORTS_INPUT:
+            case TAG_EAT_COMMENT:
                 // Just skip this tag
                 XmlUtils.skipCurrentTag(parser);
                 return input.success(pkg);
-            case PackageParser.TAG_RESTRICT_UPDATE:
+            case TAG_RESTRICT_UPDATE:
                 return parseRestrictUpdateHash(flags, input, pkg, res, parser);
-            case PackageParser.TAG_QUERIES:
+            case TAG_QUERIES:
                 return parseQueries(input, pkg, res, parser);
             default:
                 return ParsingUtils.unknownTag("<manifest>", pkg, parser, input);
@@ -1125,7 +1131,7 @@
             ParsingPackage pkg, Resources res, XmlResourceParser parser)
             throws XmlPullParserException, IOException {
         ParseResult<ParsedPermissionGroup> result = ParsedPermissionUtils.parsePermissionGroup(
-                pkg, res, parser, PackageParser.sUseRoundIcon, input);
+                pkg, res, parser, sUseRoundIcon, input);
         if (result.isError()) {
             return input.error(result);
         }
@@ -1136,7 +1142,7 @@
             ParsingPackage pkg, Resources res, XmlResourceParser parser)
             throws XmlPullParserException, IOException {
         ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermission(
-                pkg, res, parser, PackageParser.sUseRoundIcon, input);
+                pkg, res, parser, sUseRoundIcon, input);
         if (result.isError()) {
             return input.error(result);
         }
@@ -1147,7 +1153,7 @@
             ParsingPackage pkg, Resources res, XmlResourceParser parser)
             throws XmlPullParserException, IOException {
         ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermissionTree(
-                pkg, res, parser, PackageParser.sUseRoundIcon, input);
+                pkg, res, parser, sUseRoundIcon, input);
         if (result.isError()) {
             return input.error(result);
         }
@@ -1405,7 +1411,7 @@
     private static ParseResult<ParsingPackage> parseUsesSdk(ParseInput input,
             ParsingPackage pkg, Resources res, XmlResourceParser parser)
             throws IOException, XmlPullParserException {
-        if (PackageParser.SDK_VERSION > 0) {
+        if (SDK_VERSION > 0) {
             TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdk);
             try {
                 int minVers = 1;
@@ -1440,7 +1446,7 @@
                 }
 
                 ParseResult<Integer> targetSdkVersionResult = computeTargetSdkVersion(
-                        targetVers, targetCode, PackageParser.SDK_CODENAMES, input);
+                        targetVers, targetCode, SDK_CODENAMES, input);
                 if (targetSdkVersionResult.isError()) {
                     return input.error(targetSdkVersionResult);
                 }
@@ -1454,7 +1460,7 @@
                 }
 
                 ParseResult<Integer> minSdkVersionResult = computeMinSdkVersion(minVers, minCode,
-                        PackageParser.SDK_VERSION, PackageParser.SDK_CODENAMES, input);
+                        SDK_VERSION, SDK_CODENAMES, input);
                 if (minSdkVersionResult.isError()) {
                     return input.error(minSdkVersionResult);
                 }
@@ -1637,7 +1643,7 @@
 
     private static ParseResult<ParsingPackage> parseRestrictUpdateHash(int flags, ParseInput input,
             ParsingPackage pkg, Resources res, XmlResourceParser parser) {
-        if ((flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
+        if ((flags & PARSE_IS_SYSTEM_DIR) != 0) {
             TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestRestrictUpdate);
             try {
                 final String hash = sa.getNonConfigurationString(
@@ -1846,7 +1852,7 @@
                         return input.error("Empty class name in package " + pkgName);
                     }
 
-                    if (PackageParser.DEBUG_BACKUP) {
+                    if (DEBUG_BACKUP) {
                         Slog.v(TAG, "android:backupAgent = " + backupAgentName
                                 + " from " + pkgName + "+" + backupAgent);
                     }
@@ -1870,7 +1876,7 @@
                     fullBackupContent = v.resourceId;
 
                     if (v.resourceId == 0) {
-                        if (PackageParser.DEBUG_BACKUP) {
+                        if (DEBUG_BACKUP) {
                             Slog.v(TAG, "fullBackupContent specified as boolean=" +
                                     (v.data == 0 ? "false" : "true"));
                         }
@@ -1880,7 +1886,7 @@
 
                     pkg.setFullBackupContent(fullBackupContent);
                 }
-                if (PackageParser.DEBUG_BACKUP) {
+                if (DEBUG_BACKUP) {
                     Slog.v(TAG, "fullBackupContent=" + fullBackupContent + " for " + pkgName);
                 }
             }
@@ -1994,7 +2000,7 @@
                 case "receiver":
                     ParseResult<ParsedActivity> activityResult =
                             ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
-                                    res, parser, flags, PackageParser.sUseRoundIcon, input);
+                                    res, parser, flags, sUseRoundIcon, input);
 
                     if (activityResult.isSuccess()) {
                         ParsedActivity activity = activityResult.getResult();
@@ -2012,7 +2018,7 @@
                 case "service":
                     ParseResult<ParsedService> serviceResult =
                             ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
-                                    flags, PackageParser.sUseRoundIcon, input);
+                                    flags, sUseRoundIcon, input);
                     if (serviceResult.isSuccess()) {
                         ParsedService service = serviceResult.getResult();
                         hasServiceOrder |= (service.getOrder() != 0);
@@ -2024,7 +2030,7 @@
                 case "provider":
                     ParseResult<ParsedProvider> providerResult =
                             ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
-                                    flags, PackageParser.sUseRoundIcon, input);
+                                    flags, sUseRoundIcon, input);
                     if (providerResult.isSuccess()) {
                         pkg.addProvider(providerResult.getResult());
                     }
@@ -2033,7 +2039,7 @@
                     break;
                 case "activity-alias":
                     activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
-                            parser, PackageParser.sUseRoundIcon, input);
+                            parser, sUseRoundIcon, input);
                     if (activityResult.isSuccess()) {
                         ParsedActivity activity = activityResult.getResult();
                         hasActivityOrder |= (activity.getOrder() != 0);
@@ -2505,8 +2511,7 @@
     private static void setMaxAspectRatio(ParsingPackage pkg) {
         // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater.
         // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD.
-        float maxAspectRatio = pkg.getTargetSdkVersion() < O
-                ? PackageParser.DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
+        float maxAspectRatio = pkg.getTargetSdkVersion() < O ? DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
 
         float packageMaxAspectRatio = pkg.getMaxAspectRatio();
         if (packageMaxAspectRatio != 0) {
@@ -2514,10 +2519,8 @@
             maxAspectRatio = packageMaxAspectRatio;
         } else {
             Bundle appMetaData = pkg.getMetaData();
-            if (appMetaData != null && appMetaData.containsKey(
-                    PackageParser.METADATA_MAX_ASPECT_RATIO)) {
-                maxAspectRatio = appMetaData.getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO,
-                        maxAspectRatio);
+            if (appMetaData != null && appMetaData.containsKey(METADATA_MAX_ASPECT_RATIO)) {
+                maxAspectRatio = appMetaData.getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio);
             }
         }
 
@@ -2536,8 +2539,7 @@
             // process the meta data here since this method is called at the end of processing
             // the application and all meta data is guaranteed.
             final float activityAspectRatio = activity.getMetaData() != null
-                    ? activity.getMetaData().getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO,
-                    maxAspectRatio)
+                    ? activity.getMetaData().getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio)
                     : maxAspectRatio;
 
             activity.setMaxAspectRatio(activity.getResizeMode(), activityAspectRatio);
@@ -2565,7 +2567,7 @@
     private void setSupportsSizeChanges(ParsingPackage pkg) {
         final Bundle appMetaData = pkg.getMetaData();
         final boolean supportsSizeChanges = appMetaData != null
-                && appMetaData.getBoolean(PackageParser.METADATA_SUPPORTS_SIZE_CHANGES, false);
+                && appMetaData.getBoolean(METADATA_SUPPORTS_SIZE_CHANGES, false);
 
         List<ParsedActivity> activities = pkg.getActivities();
         int activitiesSize = activities.size();
@@ -2573,7 +2575,7 @@
             ParsedActivity activity = activities.get(index);
             if (supportsSizeChanges || (activity.getMetaData() != null
                     && activity.getMetaData().getBoolean(
-                            PackageParser.METADATA_SUPPORTS_SIZE_CHANGES, false))) {
+                            METADATA_SUPPORTS_SIZE_CHANGES, false))) {
                 activity.setSupportsSizeChanges(true);
             }
         }
@@ -2674,7 +2676,7 @@
             ParsingPackage pkg, Resources res, XmlResourceParser parser)
             throws XmlPullParserException, IOException {
         ParseResult<ParsedInstrumentation> result = ParsedInstrumentationUtils.parseInstrumentation(
-                pkg, res, parser, PackageParser.sUseRoundIcon, input);
+                pkg, res, parser, sUseRoundIcon, input);
         if (result.isError()) {
             return input.error(result);
         }
@@ -2860,7 +2862,7 @@
                     } else if (v.type == TypedValue.TYPE_FLOAT) {
                         property = new Property(name, v.getFloat(), packageName, className);
                     } else {
-                        if (!PackageParser.RIGID_PARSER) {
+                        if (!RIGID_PARSER) {
                             Slog.w(TAG,
                                     tagName + " only supports string, integer, float, color, "
                                             + "boolean, and resource reference types: "
diff --git a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
index d65f8ff..0403a25 100644
--- a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
+++ b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
@@ -22,9 +22,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Intent;
-import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
 import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
@@ -77,7 +77,7 @@
     @NonNull
     public static ParseResult<String> buildProcessName(@NonNull String pkg, String defProc,
             CharSequence procSeq, int flags, String[] separateProcesses, ParseInput input) {
-        if ((flags & PackageParser.PARSE_IGNORE_PROCESSES) != 0 && !"system".contentEquals(
+        if ((flags & ParsingPackageUtils.PARSE_IGNORE_PROCESSES) != 0 && !"system".contentEquals(
                 procSeq)) {
             return input.success(defProc != null ? defProc : pkg);
         }
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
index 1915028..2ea24f7 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivity.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java
@@ -28,7 +28,6 @@
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -127,7 +126,7 @@
         activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
         activity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NONE;
         activity.maxRecents = ActivityTaskManager.getDefaultAppRecentsLimitStatic();
-        activity.configChanges = PackageParser.getActivityConfigChanges(0, 0);
+        activity.configChanges = ParsedActivityUtils.getActivityConfigChanges(0, 0);
         activity.softInputMode = 0;
         activity.persistableMode = ActivityInfo.PERSIST_NEVER;
         activity.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index f96bd54..f821e08 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -23,8 +23,8 @@
 import android.app.ActivityTaskManager;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.content.pm.PackageParser;
 import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseInput.DeferredError;
@@ -67,6 +67,12 @@
         SAFE_BROADCASTS.add(Intent.ACTION_BOOT_COMPLETED);
     }
 
+    /**
+     * Bit mask of all the valid bits that can be set in recreateOnConfigChanges.
+     */
+    private static final int RECREATE_ON_CONFIG_CHANGES_MASK =
+            ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
+
     @NonNull
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public static ParseResult<ParsedActivity> parseActivityOrReceiver(String[] separateProcesses,
@@ -153,7 +159,7 @@
                 activity.rotationAnimation = sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED);
                 activity.softInputMode = sa.getInt(R.styleable.AndroidManifestActivity_windowSoftInputMode, 0);
 
-                activity.configChanges = PackageParser.getActivityConfigChanges(
+                activity.configChanges = getActivityConfigChanges(
                         sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
                         sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0));
 
@@ -345,7 +351,7 @@
                     if (intent != null) {
                         activity.order = Math.max(intent.getOrder(), activity.order);
                         activity.addIntent(intent);
-                        if (PackageParser.LOG_UNSAFE_BROADCASTS && isReceiver
+                        if (LOG_UNSAFE_BROADCASTS && isReceiver
                                 && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O) {
                             int actionCount = intent.countActions();
                             for (int i = 0; i < actionCount; i++) {
@@ -354,7 +360,7 @@
                                     continue;
                                 }
 
-                                if (!PackageParser.SAFE_BROADCASTS.contains(action)) {
+                                if (!SAFE_BROADCASTS.contains(action)) {
                                     Slog.w(TAG,
                                             "Broadcast " + action + " may never be delivered to "
                                                     + pkg.getPackageName() + " as requested at: "
@@ -532,7 +538,7 @@
             ParsedActivity activity, ParseInput input) {
         // There isn't a metadata for us to fall back. Whatever is in layout is correct.
         if (activity.metaData == null || !activity.metaData.containsKey(
-                PackageParser.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
+                ParsingPackageUtils.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
             return input.success(activity.windowLayout);
         }
 
@@ -542,7 +548,7 @@
         }
 
         String windowLayoutAffinity = activity.metaData.getString(
-                PackageParser.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY);
+                ParsingPackageUtils.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY);
         ActivityInfo.WindowLayout layout = activity.windowLayout;
         if (layout == null) {
             layout = new ActivityInfo.WindowLayout(-1 /* width */, -1 /* widthFraction */,
@@ -553,4 +559,14 @@
         }
         return input.success(layout);
     }
+
+    /**
+     * @param configChanges The bit mask of configChanges fetched from AndroidManifest.xml.
+     * @param recreateOnConfigChanges The bit mask recreateOnConfigChanges fetched from
+     *                                AndroidManifest.xml.
+     * @hide
+     */
+    static int getActivityConfigChanges(int configChanges, int recreateOnConfigChanges) {
+        return configChanges | ((~recreateOnConfigChanges) & RECREATE_ON_CONFIG_CHANGES_MASK);
+    }
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
index 368dcfd..939e77f 100644
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
@@ -21,6 +21,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageParser;
 import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.ParsingUtils;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
@@ -65,7 +66,7 @@
                 }
             }
 
-            if (PackageParser.sUseRoundIcon) {
+            if (ParsingPackageUtils.sUseRoundIcon) {
                 intentInfo.icon = sa.getResourceId(
                         R.styleable.AndroidManifestIntentFilter_roundIcon, 0);
             }
@@ -141,7 +142,7 @@
 
         intentInfo.hasDefault = intentInfo.hasCategory(Intent.CATEGORY_DEFAULT);
 
-        if (PackageParser.DEBUG_PARSER) {
+        if (DEBUG) {
             final StringBuilder cats = new StringBuilder("Intent d=");
             cats.append(intentInfo.isHasDefault());
             cats.append(", cat=");
diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
index 9e3a8f4..f3caf60 100644
--- a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
+++ b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
@@ -18,9 +18,11 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
 
-import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.ParseFlags;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.ParsingPackageUtils.ParseFlags;
 import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
 import android.os.Build;
@@ -36,20 +38,21 @@
  * @hide
  */
 public class DefaultSplitAssetLoader implements SplitAssetLoader {
-    private final String mBaseCodePath;
-    private final String[] mSplitCodePaths;
+    private final String mBaseApkPath;
+    private final String[] mSplitApkPaths;
     private final @ParseFlags int mFlags;
     private AssetManager mCachedAssetManager;
 
-    public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, @ParseFlags int flags) {
-        mBaseCodePath = pkg.baseCodePath;
-        mSplitCodePaths = pkg.splitCodePaths;
+    public DefaultSplitAssetLoader(PackageLite pkg, @ParseFlags int flags) {
+        mBaseApkPath = pkg.getBaseApkPath();
+        mSplitApkPaths = pkg.getSplitApkPaths();
         mFlags = flags;
     }
 
     private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
             throws PackageParserException {
-        if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+        if ((flags & ParsingPackageUtils.PARSE_MUST_BE_APK) != 0
+                && !ApkLiteParseUtils.isApkPath(path)) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                     "Invalid package file: " + path);
         }
@@ -68,16 +71,16 @@
             return mCachedAssetManager;
         }
 
-        ApkAssets[] apkAssets = new ApkAssets[(mSplitCodePaths != null
-                ? mSplitCodePaths.length : 0) + 1];
+        ApkAssets[] apkAssets = new ApkAssets[(mSplitApkPaths != null
+                ? mSplitApkPaths.length : 0) + 1];
 
         // Load the base.
         int splitIdx = 0;
-        apkAssets[splitIdx++] = loadApkAssets(mBaseCodePath, mFlags);
+        apkAssets[splitIdx++] = loadApkAssets(mBaseApkPath, mFlags);
 
         // Load any splits.
-        if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
-            for (String apkPath : mSplitCodePaths) {
+        if (!ArrayUtils.isEmpty(mSplitApkPaths)) {
+            for (String apkPath : mSplitApkPaths) {
                 apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags);
             }
         }
diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
index 58eaabf..523ca40 100644
--- a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
+++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
@@ -19,9 +19,11 @@
 
 import android.annotation.NonNull;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.ParseFlags;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.ParsingPackageUtils.ParseFlags;
 import android.content.res.ApkAssets;
 import android.content.res.AssetManager;
 import android.os.Build;
@@ -45,14 +47,14 @@
     private final ApkAssets[][] mCachedSplitApks;
     private final AssetManager[] mCachedAssetManagers;
 
-    public SplitAssetDependencyLoader(PackageParser.PackageLite pkg,
+    public SplitAssetDependencyLoader(PackageLite pkg,
             SparseArray<int[]> dependencies, @ParseFlags int flags) {
         super(dependencies);
 
         // The base is inserted into index 0, so we need to shift all the splits by 1.
-        mSplitPaths = new String[pkg.splitCodePaths.length + 1];
-        mSplitPaths[0] = pkg.baseCodePath;
-        System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length);
+        mSplitPaths = new String[pkg.getSplitApkPaths().length + 1];
+        mSplitPaths[0] = pkg.getBaseApkPath();
+        System.arraycopy(pkg.getSplitApkPaths(), 0, mSplitPaths, 1, pkg.getSplitApkPaths().length);
 
         mFlags = flags;
         mCachedSplitApks = new ApkAssets[mSplitPaths.length][];
@@ -66,7 +68,8 @@
 
     private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
             throws PackageParserException {
-        if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+        if ((flags & ParsingPackageUtils.PARSE_MUST_BE_APK) != 0
+                && !ApkLiteParseUtils.isApkPath(path)) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                     "Invalid package file: " + path);
         }
diff --git a/core/java/android/content/pm/split/SplitDependencyLoader.java b/core/java/android/content/pm/split/SplitDependencyLoader.java
index 3586546..3e68132 100644
--- a/core/java/android/content/pm/split/SplitDependencyLoader.java
+++ b/core/java/android/content/pm/split/SplitDependencyLoader.java
@@ -17,7 +17,7 @@
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.PackageLite;
 import android.util.IntArray;
 import android.util.SparseArray;
 
@@ -149,10 +149,19 @@
         return dst;
     }
 
-    public static @NonNull SparseArray<int[]> createDependenciesFromPackage(
-            PackageParser.PackageLite pkg) throws IllegalDependencyException {
-        // The data structure that holds the dependencies. In PackageParser, splits are stored
-        // in their own array, separate from the base. We treat all paths as equals, so
+    /**
+     * Build the split dependency tree by the given package
+     *
+     * @param pkg The package to retrieve the dependency tree
+     * @return The dependency tree of splits
+     * @throws IllegalDependencyException if the requires split is missing, targets split is
+     *         missing, it declares itself as configuration split for a non-feature split, or
+     *         cycle detected in split dependencies.
+     */
+    public static @NonNull SparseArray<int[]> createDependenciesFromPackage(PackageLite pkg)
+            throws IllegalDependencyException {
+        // The data structure that holds the dependencies. In ParsingPackageUtils, splits are
+        // stored in their own array, separate from the base. We treat all paths as equals, so
         // we need to insert the base as index 0, and shift all other splits.
         final SparseArray<int[]> splitDependencies = new SparseArray<>();
 
@@ -161,19 +170,19 @@
 
         // First write out the <uses-split> dependencies. These must appear first in the
         // array of ints, as is convention in this class.
-        for (int splitIdx = 0; splitIdx < pkg.splitNames.length; splitIdx++) {
-            if (!pkg.isFeatureSplits[splitIdx]) {
+        for (int splitIdx = 0; splitIdx < pkg.getSplitNames().length; splitIdx++) {
+            if (!pkg.getIsFeatureSplits()[splitIdx]) {
                 // Non-feature splits don't have dependencies.
                 continue;
             }
 
             // Implicit dependency on the base.
             final int targetIdx;
-            final String splitDependency = pkg.usesSplitNames[splitIdx];
+            final String splitDependency = pkg.getUsesSplitNames()[splitIdx];
             if (splitDependency != null) {
-                final int depIdx = Arrays.binarySearch(pkg.splitNames, splitDependency);
+                final int depIdx = Arrays.binarySearch(pkg.getSplitNames(), splitDependency);
                 if (depIdx < 0) {
-                    throw new IllegalDependencyException("Split '" + pkg.splitNames[splitIdx]
+                    throw new IllegalDependencyException("Split '" + pkg.getSplitNames()[splitIdx]
                             + "' requires split '" + splitDependency + "', which is missing.");
                 }
                 targetIdx = depIdx + 1;
@@ -188,26 +197,26 @@
         // dependencies and are considered leaves.
         //
         // At this point, all splits in splitDependencies have the first element in their array set.
-        for (int splitIdx = 0; splitIdx < pkg.splitNames.length; splitIdx++) {
-            if (pkg.isFeatureSplits[splitIdx]) {
+        for (int splitIdx = 0, size = pkg.getSplitNames().length; splitIdx < size; splitIdx++) {
+            if (pkg.getIsFeatureSplits()[splitIdx]) {
                 // Feature splits are not configForSplits.
                 continue;
             }
 
             // Implicit feature for the base.
             final int targetSplitIdx;
-            final String configForSplit = pkg.configForSplit[splitIdx];
+            final String configForSplit = pkg.getConfigForSplit()[splitIdx];
             if (configForSplit != null) {
-                final int depIdx = Arrays.binarySearch(pkg.splitNames, configForSplit);
+                final int depIdx = Arrays.binarySearch(pkg.getSplitNames(), configForSplit);
                 if (depIdx < 0) {
-                    throw new IllegalDependencyException("Split '" + pkg.splitNames[splitIdx]
+                    throw new IllegalDependencyException("Split '" + pkg.getSplitNames()[splitIdx]
                             + "' targets split '" + configForSplit + "', which is missing.");
                 }
 
-                if (!pkg.isFeatureSplits[depIdx]) {
-                    throw new IllegalDependencyException("Split '" + pkg.splitNames[splitIdx]
+                if (!pkg.getIsFeatureSplits()[depIdx]) {
+                    throw new IllegalDependencyException("Split '" + pkg.getSplitNames()[splitIdx]
                             + "' declares itself as configuration split for a non-feature split '"
-                            + pkg.splitNames[depIdx] + "'");
+                            + pkg.getSplitNames()[depIdx] + "'");
                 }
                 targetSplitIdx = depIdx + 1;
             } else {
diff --git a/core/java/android/graphics/fonts/FontManager.java b/core/java/android/graphics/fonts/FontManager.java
index ea6cf2f..eca56b3 100644
--- a/core/java/android/graphics/fonts/FontManager.java
+++ b/core/java/android/graphics/fonts/FontManager.java
@@ -16,6 +16,7 @@
 
 package android.graphics.fonts;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -28,6 +29,8 @@
 
 import com.android.internal.graphics.fonts.IFontManager;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
@@ -41,6 +44,116 @@
     private static final String TAG = "FontManager";
     private final @NonNull IFontManager mIFontManager;
 
+    /** @hide */
+    @IntDef(prefix = "ERROR_CODE_",
+            value = { ERROR_CODE_OK, ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
+                    ERROR_CODE_VERIFICATION_FAILURE, ERROR_CODE_FONT_NAME_MISMATCH,
+                    ERROR_CODE_INVALID_FONT_FILE, ERROR_CODE_MISSING_POST_SCRIPT_NAME,
+                    ERROR_CODE_DOWNGRADING, ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE,
+                    ERROR_CODE_FONT_UPDATER_DISABLED })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ErrorCode {}
+
+    /**
+     * Indicates an operation has processed successfully.
+     * @hide
+     */
+    public static final int ERROR_CODE_OK = 0;
+
+    /**
+     * Indicates a failure of writing font files.
+     * @hide
+     */
+    public static final int ERROR_CODE_FAILED_TO_WRITE_FONT_FILE = -1;
+
+    /**
+     * Indicates a failure of fs-verity setup.
+     * @hide
+     */
+    public static final int ERROR_CODE_VERIFICATION_FAILURE = -2;
+
+    /**
+     * Indicates a failure of verifying the font name with PostScript name.
+     * @hide
+     */
+    public static final int ERROR_CODE_FONT_NAME_MISMATCH = -3;
+
+    /**
+     * Indicates a failure of placing fonts due to unexpected font contents.
+     * @hide
+     */
+    public static final int ERROR_CODE_INVALID_FONT_FILE = -4;
+
+    /**
+     * Indicates a failure due to missing PostScript name in name table.
+     * @hide
+     */
+    public static final int ERROR_CODE_MISSING_POST_SCRIPT_NAME = -5;
+
+    /**
+     * Indicates a failure of placing fonts due to downgrading.
+     * @hide
+     */
+    public static final int ERROR_CODE_DOWNGRADING = -6;
+
+    /**
+     * Indicates a failure of writing system font configuration XML file.
+     * @hide
+     */
+    public static final int ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE = -7;
+
+    /**
+     * Indicates a failure due to disabled font updater.
+     * @hide
+     */
+    public static final int ERROR_CODE_FONT_UPDATER_DISABLED = -8;
+
+    /**
+     * Indicates a failure of opening font file.
+     *
+     * This error code is only used with the shell command interaction.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_FAILED_TO_OPEN_FONT_FILE = -10001;
+
+    /**
+     * Indicates a failure of opening signature file.
+     *
+     * This error code is only used with the shell command interaction.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_FAILED_TO_OPEN_SIGNATURE_FILE = -10002;
+
+    /**
+     * Indicates a failure of invalid shell command arguments.
+     *
+     * This error code is only used with the shell command interaction.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_INVALID_SHELL_ARGUMENT = -10003;
+
+    /**
+     * Indicates a failure of reading signature file.
+     *
+     * This error code is only used with the shell command interaction.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_INVALID_SIGNATURE_FILE = -10004;
+
+    /**
+     * Indicates a failure due to exceeding allowed signature file size (8kb).
+     *
+     * This error code is only used with the shell command interaction.
+     *
+     * @hide
+     */
+    public static final int ERROR_CODE_SIGNATURE_TOO_LARGE = -10005;
+
+
     private FontManager(@NonNull IFontManager iFontManager) {
         mIFontManager = iFontManager;
     }
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 5a03ade..95f1d12 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -465,4 +465,46 @@
     public interface DisplayTransactionListener {
         void onDisplayTransaction(Transaction t);
     }
+
+    /**
+     * Called when there are changes to {@link com.android.server.display.DisplayGroup
+     * DisplayGroups}.
+     */
+    public interface DisplayGroupListener {
+        /**
+         * A new display group with the provided {@code groupId} was added.
+         *
+         * <ol>
+         *     <li>The {@code groupId} is applied to all appropriate {@link Display displays}.
+         *     <li>This method is called.
+         *     <li>{@link android.hardware.display.DisplayManager.DisplayListener DisplayListeners}
+         *     are informed of any corresponding changes.
+         * </ol>
+         */
+        void onDisplayGroupAdded(int groupId);
+
+        /**
+         * The display group with the provided {@code groupId} was removed.
+         *
+         * <ol>
+         *     <li>All affected {@link Display displays} have their group IDs updated appropriately.
+         *     <li>{@link android.hardware.display.DisplayManager.DisplayListener DisplayListeners}
+         *     are informed of any corresponding changes.
+         *     <li>This method is called.
+         * </ol>
+         */
+        void onDisplayGroupRemoved(int groupId);
+
+        /**
+         * The display group with the provided {@code groupId} has changed.
+         *
+         * <ol>
+         *     <li>All affected {@link Display displays} have their group IDs updated appropriately.
+         *     <li>{@link android.hardware.display.DisplayManager.DisplayListener DisplayListeners}
+         *     are informed of any corresponding changes.
+         *     <li>This method is called.
+         * </ol>
+         */
+        void onDisplayGroupChanged(int groupId);
+    }
 }
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index e829821..46141e0 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -22,6 +22,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -381,7 +382,13 @@
         // Query a property of the underlying socket to ensure that the socket's file descriptor
         // exists, is available to bind to a network and is not closed.
         socket.getReuseAddress();
-        bindSocket(socket.getFileDescriptor$());
+        final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket);
+        bindSocket(pfd.getFileDescriptor());
+        // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
+        // dup share the underlying socket in the kernel. The socket is never truly closed until the
+        // last fd pointing to the socket being closed. So close the dup one after binding the
+        // socket to control the lifetime of the dup fd.
+        pfd.close();
     }
 
     /**
@@ -393,7 +400,13 @@
         // Query a property of the underlying socket to ensure that the socket's file descriptor
         // exists, is available to bind to a network and is not closed.
         socket.getReuseAddress();
-        bindSocket(socket.getFileDescriptor$());
+        final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
+        bindSocket(pfd.getFileDescriptor());
+        // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
+        // dup share the underlying socket in the kernel. The socket is never truly closed until the
+        // last fd pointing to the socket being closed. So close the dup one after binding the
+        // socket to control the lifetime of the dup fd.
+        pfd.close();
     }
 
     /**
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 0a895b9..3843b9a 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -204,6 +204,7 @@
             NET_CAPABILITY_TEMPORARILY_NOT_METERED,
             NET_CAPABILITY_OEM_PRIVATE,
             NET_CAPABILITY_VEHICLE_INTERNAL,
+            NET_CAPABILITY_NOT_VCN_MANAGED,
     })
     public @interface NetCapability { }
 
@@ -399,8 +400,16 @@
     @SystemApi
     public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27;
 
+    /**
+     * Indicates that this network is not managed by a Virtual Carrier Network (VCN).
+     *
+     * TODO(b/177299683): Add additional clarifying javadoc.
+     * @hide
+     */
+    public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28;
+
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
-    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_VEHICLE_INTERNAL;
+    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VCN_MANAGED;
 
     /**
      * Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -417,7 +426,8 @@
             | (1 << NET_CAPABILITY_NOT_CONGESTED)
             | (1 << NET_CAPABILITY_NOT_SUSPENDED)
             | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY)
-            | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+            | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+            | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
 
     /**
      * Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -426,16 +436,21 @@
      * can get into a cycle where the NetworkFactory endlessly churns out NetworkAgents that then
      * get immediately torn down because they do not have the requested capability.
      */
+    // Note that as a historical exception, the TRUSTED and NOT_VCN_MANAGED capabilities
+    // are mutable but requestable. Factories are responsible for not getting
+    // in an infinite loop about these.
     private static final long NON_REQUESTABLE_CAPABILITIES =
-            MUTABLE_CAPABILITIES & ~(1 << NET_CAPABILITY_TRUSTED);
+            MUTABLE_CAPABILITIES
+            & ~(1 << NET_CAPABILITY_TRUSTED)
+            & ~(1 << NET_CAPABILITY_NOT_VCN_MANAGED);
 
     /**
      * Capabilities that are set by default when the object is constructed.
      */
     private static final long DEFAULT_CAPABILITIES =
-            (1 << NET_CAPABILITY_NOT_RESTRICTED) |
-            (1 << NET_CAPABILITY_TRUSTED) |
-            (1 << NET_CAPABILITY_NOT_VPN);
+            (1 << NET_CAPABILITY_NOT_RESTRICTED)
+            | (1 << NET_CAPABILITY_TRUSTED)
+            | (1 << NET_CAPABILITY_NOT_VPN);
 
     /**
      * Capabilities that suggest that a network is restricted.
@@ -495,7 +510,8 @@
             | (1 << NET_CAPABILITY_NOT_VPN)
             | (1 << NET_CAPABILITY_NOT_ROAMING)
             | (1 << NET_CAPABILITY_NOT_CONGESTED)
-            | (1 << NET_CAPABILITY_NOT_SUSPENDED);
+            | (1 << NET_CAPABILITY_NOT_SUSPENDED)
+            | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
 
     /**
      * Adds the given capability to this {@code NetworkCapability} instance.
@@ -1982,6 +1998,7 @@
             case NET_CAPABILITY_TEMPORARILY_NOT_METERED:    return "TEMPORARILY_NOT_METERED";
             case NET_CAPABILITY_OEM_PRIVATE:          return "OEM_PRIVATE";
             case NET_CAPABILITY_VEHICLE_INTERNAL:     return "NET_CAPABILITY_VEHICLE_INTERNAL";
+            case NET_CAPABILITY_NOT_VCN_MANAGED:      return "NOT_VCN_MANAGED";
             default:                                  return Integer.toString(capability);
         }
     }
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 66b99b9..c4d1b09 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -353,7 +353,9 @@
          *                         NetworkSpecifier.
          */
         public Builder setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
-            MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(networkSpecifier);
+            if (networkSpecifier instanceof MatchAllNetworkSpecifier) {
+                throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted");
+            }
             mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
             return this;
         }
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index 950d393..9c9fed1 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -355,7 +355,7 @@
                     port = in.readInt();
                 }
                 String exclList = in.readString();
-                String[] parsedExclList = in.readStringArray();
+                String[] parsedExclList = in.createStringArray();
                 ProxyInfo proxyProperties = new ProxyInfo(host, port, exclList, parsedExclList);
                 return proxyProperties;
             }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index e504946..74df1b2 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -27,6 +27,7 @@
 import android.app.Application;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.sysprop.SocProperties;
 import android.sysprop.TelephonyProperties;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -88,6 +89,14 @@
     /** The end-user-visible name for the end product. */
     public static final String MODEL = getString("ro.product.model");
 
+    /** The manufacturer of the device's primary system-on-chip. */
+    @NonNull
+    public static final String SOC_MANUFACTURER = SocProperties.soc_manufacturer().orElse(UNKNOWN);
+
+    /** The model name of the device's primary system-on-chip. */
+    @NonNull
+    public static final String SOC_MODEL = SocProperties.soc_model().orElse(UNKNOWN);
+
     /** The system bootloader version number. */
     public static final String BOOTLOADER = getString("ro.bootloader");
 
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 1c1f5c0..102f525 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -45,6 +45,7 @@
     @VisibleForTesting
     static final int FLAG_ALLOW_FDS = 1 << 10;
 
+    /** An unmodifiable {@code Bundle} that is always {@link #isEmpty() empty}. */
     public static final Bundle EMPTY;
 
     /**
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index 5e3a34d..e5e9b5f 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -47,6 +47,8 @@
 public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable,
         XmlUtils.WriteMapCallback {
     private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
+
+    /** An unmodifiable {@code PersistableBundle} that is always {@link #isEmpty() empty}. */
     public static final PersistableBundle EMPTY;
 
     static {
diff --git a/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java b/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java
index 891fb59..f0f3cef 100644
--- a/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java
+++ b/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java
@@ -17,10 +17,13 @@
 package android.os.strictmode;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.net.Uri;
 
+import java.util.Objects;
+
 /**
  * Violation raised when your app launches an {@link Intent} which originated
  * from outside your app.
@@ -46,8 +49,20 @@
  * not protected, your app is likely vulnerable to malicious apps.
  */
 public final class UnsafeIntentLaunchViolation extends Violation {
-    /** @hide */
+    private transient Intent mIntent;
+
     public UnsafeIntentLaunchViolation(@NonNull Intent intent) {
         super("Launch of unsafe intent: " + intent);
+        mIntent = Objects.requireNonNull(intent);
+    }
+
+    /**
+     * Return the {@link Intent} which caused this violation to be raised. Note
+     * that this value is not available if this violation has been serialized
+     * since intents cannot be serialized.
+     */
+    @SuppressWarnings("IntentBuilderName")
+    public @Nullable Intent getIntent() {
+        return mIntent;
     }
 }
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index db55e1c..b1b2925 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -88,10 +88,8 @@
     private static final long DEFAULT_RECENT_TIME_MS = 30000L;
 
     private static boolean shouldShowIndicators() {
-        return true;
-        // TODO ntmyren: remove true set when device config is configured correctly
-        //DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-        //PROPERTY_CAMERA_MIC_ICONS_ENABLED, true);
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                PROPERTY_CAMERA_MIC_ICONS_ENABLED, true);
     }
 
     private static boolean shouldShowLocationIndicator() {
@@ -142,7 +140,7 @@
     }
 
     private Context mContext;
-    private Map<UserHandle, Context> mUserContexts;
+    private ArrayMap<UserHandle, Context> mUserContexts;
     private PackageManager mPkgManager;
     private AppOpsManager mAppOpsManager;
 
@@ -154,7 +152,8 @@
         mContext = context;
         mPkgManager = context.getPackageManager();
         mAppOpsManager = context.getSystemService(AppOpsManager.class);
-        mUserContexts = Map.of(Process.myUserHandle(), mContext);
+        mUserContexts = new ArrayMap<>();
+        mUserContexts.put(Process.myUserHandle(), mContext);
     }
 
     private Context getUserContext(UserHandle user) {
diff --git a/core/java/android/rotationresolver/OWNERS b/core/java/android/rotationresolver/OWNERS
new file mode 100644
index 0000000..81b6f05
--- /dev/null
+++ b/core/java/android/rotationresolver/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/rotationresolver/OWNERS
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 017f405..f994d29 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -177,6 +177,7 @@
     public static final int KM_PURPOSE_SIGN = KeyPurpose.SIGN;
     public static final int KM_PURPOSE_VERIFY = KeyPurpose.VERIFY;
     public static final int KM_PURPOSE_WRAP = KeyPurpose.WRAP_KEY;
+    public static final int KM_PURPOSE_AGREE_KEY = KeyPurpose.AGREE_KEY;
 
     // Key formats.
     public static final int KM_KEY_FORMAT_X509 = KeyFormat.X509;
diff --git a/core/java/android/service/rotationresolver/OWNERS b/core/java/android/service/rotationresolver/OWNERS
new file mode 100644
index 0000000..e381d17
--- /dev/null
+++ b/core/java/android/service/rotationresolver/OWNERS
@@ -0,0 +1,10 @@
+# Bug component: 814982
+
+asalo@google.com
+augale@google.com
+bquezada@google.com
+eejiang@google.com
+payamp@google.com
+siddikap@google.com
+svetoslavganov@google.com
+tgadh@google.com
diff --git a/core/java/android/speech/IRecognitionServiceManager.aidl b/core/java/android/speech/IRecognitionServiceManager.aidl
new file mode 100644
index 0000000..7158ba2
--- /dev/null
+++ b/core/java/android/speech/IRecognitionServiceManager.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.speech;
+
+import android.speech.IRecognitionServiceManagerCallback;
+
+/**
+ * Binder service allowing speech recognition proxied by the system.
+ *
+ * {@hide}
+ */
+interface IRecognitionServiceManager {
+    void createSession(in IRecognitionServiceManagerCallback callback);
+}
diff --git a/core/java/android/speech/IRecognitionServiceManagerCallback.aidl b/core/java/android/speech/IRecognitionServiceManagerCallback.aidl
new file mode 100644
index 0000000..d760810
--- /dev/null
+++ b/core/java/android/speech/IRecognitionServiceManagerCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.speech;
+
+import android.speech.IRecognitionService;
+
+/**
+ * Callback for the service allowing speech recognition proxied by the system.
+ *
+ * {@hide}
+ */
+oneway interface IRecognitionServiceManagerCallback {
+    void onSuccess(in IRecognitionService service);
+    void onError();
+}
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 5fd192a..c97dbfe 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -28,6 +28,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
+import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -50,7 +51,9 @@
     /**
      * Name under which a RecognitionService component publishes information about itself.
      * This meta-data should reference an XML resource containing a
-     * <code>&lt;{@link android.R.styleable#RecognitionService recognition-service}&gt;</code> tag.
+     * <code>&lt;{@link android.R.styleable#RecognitionService recognition-service}&gt;</code> or
+     * <code>&lt;{@link android.R.styleable#RecognitionService on-device-recognition-service}
+     * &gt;</code> tag.
      */
     public static final String SERVICE_META_DATA = "android.speech";
 
@@ -182,6 +185,13 @@
     private boolean checkPermissions(IRecognitionListener listener, boolean forDataDelivery,
             @NonNull String packageName, @Nullable String featureId) {
         if (DBG) Log.d(TAG, "checkPermissions");
+
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid == Process.SYSTEM_UID) {
+            // Assuming system has verified permissions of the caller.
+            return true;
+        }
+
         if (forDataDelivery) {
             if (PermissionChecker.checkCallingOrSelfPermissionForDataDelivery(this,
                     android.Manifest.permission.RECORD_AUDIO, packageName, featureId,
@@ -342,6 +352,7 @@
          * Return the Linux uid assigned to the process that sent you the current transaction that
          * is being processed. This is obtained from {@link Binder#getCallingUid()}.
          */
+        // TODO(b/176578753): need to make sure this is fixed when proxied through system.
         public int getCallingUid() {
             return mCallingUid;
         }
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index aea94bf..de879c6 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -16,6 +16,7 @@
 
 package android.speech;
 
+import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -27,6 +28,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
@@ -38,8 +40,9 @@
 /**
  * This class provides access to the speech recognition service. This service allows access to the
  * speech recognizer. Do not instantiate this class directly, instead, call
- * {@link SpeechRecognizer#createSpeechRecognizer(Context)}. This class's methods must be
- * invoked only from the main application thread. 
+ * {@link SpeechRecognizer#createSpeechRecognizer(Context)}, or
+ * {@link SpeechRecognizer#createOnDeviceSpeechRecognizer(Context)}. This class's methods must be
+ * invoked only from the main application thread.
  *
  * <p>The implementation of this API is likely to stream audio to remote servers to perform speech
  * recognition. As such this API is not intended to be used for continuous recognition, which would
@@ -122,8 +125,13 @@
     /** Component to direct service intent to */
     private final ComponentName mServiceComponent;
 
+    /** Whether to use on-device speech recognizer. */
+    private final boolean mOnDevice;
+
+    private IRecognitionServiceManager mManagerService;
+
     /** Handler that will execute the main tasks */
-    private Handler mHandler = new Handler() {
+    private Handler mHandler = new Handler(Looper.getMainLooper()) {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -159,6 +167,17 @@
     private SpeechRecognizer(final Context context, final ComponentName serviceComponent) {
         mContext = context;
         mServiceComponent = serviceComponent;
+        mOnDevice = false;
+    }
+
+    /**
+     * The right way to create a {@code SpeechRecognizer} is by using
+     * {@link #createOnDeviceSpeechRecognizer} static factory method
+     */
+    private SpeechRecognizer(final Context context, boolean onDevice) {
+        mContext = context;
+        mServiceComponent = null;
+        mOnDevice = onDevice;
     }
 
     /**
@@ -194,6 +213,7 @@
      * @return {@code true} if recognition is available, {@code false} otherwise
      */
     public static boolean isRecognitionAvailable(final Context context) {
+        // TODO(b/176578753): make sure this works well with system speech recognizers.
         final List<ResolveInfo> list = context.getPackageManager().queryIntentServices(
                 new Intent(RecognitionService.SERVICE_INTERFACE), 0);
         return list != null && list.size() != 0;
@@ -231,13 +251,32 @@
     public static SpeechRecognizer createSpeechRecognizer(final Context context,
             final ComponentName serviceComponent) {
         if (context == null) {
-            throw new IllegalArgumentException("Context cannot be null)");
+            throw new IllegalArgumentException("Context cannot be null");
         }
         checkIsCalledFromMainThread();
         return new SpeechRecognizer(context, serviceComponent);
     }
 
     /**
+     * Factory method to create a new {@code SpeechRecognizer}.
+     *
+     * <p>Please note that {@link #setRecognitionListener(RecognitionListener)} should be called
+     * before dispatching any command to the created {@code SpeechRecognizer}, otherwise no
+     * notifications will be received.
+     *
+     * @param context in which to create {@code SpeechRecognizer}
+     * @return a new on-device {@code SpeechRecognizer}.
+     */
+    @NonNull
+    public static SpeechRecognizer createOnDeviceSpeechRecognizer(@NonNull final Context context) {
+        if (context == null) {
+            throw new IllegalArgumentException("Context cannot be null");
+        }
+        checkIsCalledFromMainThread();
+        return new SpeechRecognizer(context, /* onDevice */ true);
+    }
+
+    /**
      * Sets the listener that will receive all the callbacks. The previous unfinished commands will
      * be executed with the old listener, while any following command will be executed with the new
      * listener.
@@ -265,36 +304,74 @@
         }
         checkIsCalledFromMainThread();
         if (mConnection == null) { // first time connection
-            mConnection = new Connection();
-            
-            Intent serviceIntent = new Intent(RecognitionService.SERVICE_INTERFACE);
-            
-            if (mServiceComponent == null) {
-                String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(),
-                        Settings.Secure.VOICE_RECOGNITION_SERVICE);
-                
-                if (TextUtils.isEmpty(serviceComponent)) {
-                    Log.e(TAG, "no selected voice recognition service");
-                    mListener.onError(ERROR_CLIENT);
-                    return;
-                }
-                
-                serviceIntent.setComponent(ComponentName.unflattenFromString(serviceComponent));                
+            // TODO(b/176578753): both flows should go through system service.
+            if (mOnDevice) {
+                connectToSystemService();
             } else {
-                serviceIntent.setComponent(mServiceComponent);
-            }
-            if (!mContext.bindService(serviceIntent, mConnection,
-                    Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES)) {
-                Log.e(TAG, "bind to recognition service failed");
-                mConnection = null;
-                mService = null;
-                mListener.onError(ERROR_CLIENT);
-                return;
+                connectToService();
             }
         }
         putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent));
     }
 
+    private void connectToSystemService() {
+        mManagerService = IRecognitionServiceManager.Stub.asInterface(
+                ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE));
+
+        if (mManagerService == null) {
+            mListener.onError(ERROR_CLIENT);
+            return;
+        }
+
+        try {
+            // TODO(b/176578753): this has to supply information on whether to use on-device impl.
+            mManagerService.createSession(new IRecognitionServiceManagerCallback.Stub(){
+                @Override
+                public void onSuccess(IRecognitionService service) throws RemoteException {
+                    mService = service;
+                }
+
+                @Override
+                public void onError() throws RemoteException {
+                    Log.e(TAG, "Bind to system recognition service failed");
+                    mListener.onError(ERROR_CLIENT);
+                }
+            });
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    private void connectToService() {
+        mConnection = new Connection();
+
+        Intent serviceIntent = new Intent(RecognitionService.SERVICE_INTERFACE);
+
+        if (mServiceComponent == null) {
+            String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(),
+                    Settings.Secure.VOICE_RECOGNITION_SERVICE);
+
+            if (TextUtils.isEmpty(serviceComponent)) {
+                Log.e(TAG, "no selected voice recognition service");
+                mListener.onError(ERROR_CLIENT);
+                return;
+            }
+
+            serviceIntent.setComponent(
+                    ComponentName.unflattenFromString(serviceComponent));
+        } else {
+            serviceIntent.setComponent(mServiceComponent);
+        }
+        if (!mContext.bindService(serviceIntent, mConnection,
+                Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES)) {
+            Log.e(TAG, "bind to recognition service failed");
+            mConnection = null;
+            mService = null;
+            mListener.onError(ERROR_CLIENT);
+            return;
+        }
+    }
+
     /**
      * Stops listening for speech. Speech captured so far will be recognized as if the user had
      * stopped speaking at this point. Note that in the default case, this does not need to be
@@ -378,7 +455,7 @@
             mListener.onError(ERROR_CLIENT);
         }
     }
-    
+
     private boolean checkOpenConnection() {
         if (mService != null) {
             return true;
@@ -433,7 +510,7 @@
         private final static int MSG_RMS_CHANGED = 8;
         private final static int MSG_ON_EVENT = 9;
 
-        private final Handler mInternalHandler = new Handler() {
+        private final Handler mInternalHandler = new Handler(Looper.getMainLooper()) {
             @Override
             public void handleMessage(Message msg) {
                 if (mInternalListener == null) {
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 82d7399..53fe1ba 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -53,6 +53,8 @@
 public final class FontConfig implements Parcelable {
     private final @NonNull List<FontFamily> mFamilies;
     private final @NonNull List<Alias> mAliases;
+    private final long mLastModifiedDate;
+    private final int mConfigVersion;
 
     /**
      * Construct a FontConfig instance.
@@ -62,9 +64,12 @@
      *
      * @hide Only system server can create this instance and passed via IPC.
      */
-    public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases) {
+    public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases,
+            long lastModifiedDate, @IntRange(from = 0) int configVersion) {
         mFamilies = families;
         mAliases = aliases;
+        mLastModifiedDate = lastModifiedDate;
+        mConfigVersion = configVersion;
     }
 
     /**
@@ -88,6 +93,26 @@
     }
 
     /**
+     * Returns the last modified date as Java epoch seconds.
+     *
+     * If there is no update, this return 0.
+     * @hide
+     */
+    public long getLastModifiedDate() {
+        return mLastModifiedDate;
+    }
+
+    /**
+     * Returns the monotonically increasing config version value.
+     *
+     * The config version is reset to 0 when the system is restarted.
+     * @hide
+     */
+    public @IntRange(from = 0) int getConfigVersion() {
+        return mConfigVersion;
+    }
+
+    /**
      * Returns the ordered list of families included in the system fonts.
      * @deprecated Use getFontFamilies instead.
      * @hide
@@ -107,6 +132,8 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeParcelableList(mFamilies, flags);
         dest.writeParcelableList(mAliases, flags);
+        dest.writeLong(mLastModifiedDate);
+        dest.writeInt(mConfigVersion);
     }
 
     public static final @NonNull Creator<FontConfig> CREATOR = new Creator<FontConfig>() {
@@ -116,7 +143,9 @@
                     FontFamily.class.getClassLoader());
             List<Alias> aliases = source.readParcelableList(new ArrayList<>(),
                     Alias.class.getClassLoader());
-            return new FontConfig(families, aliases);
+            long lastModifiedDate = source.readLong();
+            int configVersion = source.readInt();
+            return new FontConfig(families, aliases, lastModifiedDate, configVersion);
         }
 
         @Override
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index 02edb7e..696271c 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -27,6 +27,7 @@
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.Signature;
+import android.content.pm.parsing.ParsingPackageUtils;
 import android.os.Build;
 import android.os.Trace;
 import android.util.jar.StrictJarFile;
@@ -361,7 +362,7 @@
             // Gather certs from AndroidManifest.xml, which every APK must have, as an optimization
             // to not need to verify the whole APK when verifyFUll == false.
             final ZipEntry manifestEntry = jarFile.findEntry(
-                    PackageParser.ANDROID_MANIFEST_FILENAME);
+                    ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
             if (manifestEntry == null) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                         "Package " + apkPath + " has no manifest");
@@ -370,7 +371,7 @@
             if (ArrayUtils.isEmpty(lastCerts)) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Package "
                         + apkPath + " has no certificates at entry "
-                        + PackageParser.ANDROID_MANIFEST_FILENAME);
+                        + ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
             }
             lastSigs = convertToSignatures(lastCerts);
 
@@ -383,7 +384,7 @@
 
                     final String entryName = entry.getName();
                     if (entryName.startsWith("META-INF/")) continue;
-                    if (entryName.equals(PackageParser.ANDROID_MANIFEST_FILENAME)) continue;
+                    if (entryName.equals(ParsingPackageUtils.ANDROID_MANIFEST_FILENAME)) continue;
 
                     toVerify.add(entry);
                 }
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 0e5fb2c..c664ccb 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -121,6 +121,19 @@
     public static final int INVALID_DISPLAY = -1;
 
     /**
+     * The default display group id, which is the display group id of the primary display assuming
+     * there is one.
+     * @hide
+     */
+    public static final int DEFAULT_DISPLAY_GROUP = 0;
+
+    /**
+     * Invalid display group id.
+     * @hide
+     */
+    public static final int INVALID_DISPLAY_GROUP = -1;
+
+    /**
      * Display flag: Indicates that the display supports compositing content
      * that is stored in protected graphics buffers.
      * <p>
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index fc42cd0..d200a328 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -66,6 +66,11 @@
     public int displayId;
 
     /**
+     * Display Group identifier.
+     */
+    public int displayGroupId;
+
+    /**
      * Display address, or null if none.
      * Interpretation varies by display type.
      */
@@ -331,6 +336,7 @@
                 && flags == other.flags
                 && type == other.type
                 && displayId == other.displayId
+                && displayGroupId == other.displayGroupId
                 && Objects.equals(address, other.address)
                 && Objects.equals(deviceProductInfo, other.deviceProductInfo)
                 && Objects.equals(uniqueId, other.uniqueId)
@@ -376,6 +382,7 @@
         flags = other.flags;
         type = other.type;
         displayId = other.displayId;
+        displayGroupId = other.displayGroupId;
         address = other.address;
         deviceProductInfo = other.deviceProductInfo;
         name = other.name;
@@ -418,6 +425,7 @@
         flags = source.readInt();
         type = source.readInt();
         displayId = source.readInt();
+        displayGroupId = source.readInt();
         address = source.readParcelable(null);
         deviceProductInfo = source.readParcelable(null);
         name = source.readString8();
@@ -468,6 +476,7 @@
         dest.writeInt(this.flags);
         dest.writeInt(type);
         dest.writeInt(displayId);
+        dest.writeInt(displayGroupId);
         dest.writeParcelable(address, flags);
         dest.writeParcelable(deviceProductInfo, flags);
         dest.writeString8(name);
@@ -547,16 +556,17 @@
      * Returns the id of the "default" mode with the given refresh rate, or {@code 0} if no suitable
      * mode could be found.
      */
-    public int findDefaultModeByRefreshRate(float refreshRate) {
+    @Nullable
+    public Display.Mode findDefaultModeByRefreshRate(float refreshRate) {
         Display.Mode[] modes = supportedModes;
         Display.Mode defaultMode = getDefaultMode();
         for (int i = 0; i < modes.length; i++) {
             if (modes[i].matches(
                     defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), refreshRate)) {
-                return modes[i].getModeId();
+                return modes[i];
             }
         }
-        return 0;
+        return null;
     }
 
     /**
@@ -661,6 +671,8 @@
         sb.append(name);
         sb.append("\", displayId ");
         sb.append(displayId);
+        sb.append("\", displayGroupId ");
+        sb.append(displayGroupId);
         sb.append(flagsToString(flags));
         sb.append(", real ");
         sb.append(logicalWidth);
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index d68e903..bf377b0 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -106,9 +106,7 @@
     public static final int ITYPE_NAVIGATION_BAR = 1;
     public static final int ITYPE_CAPTION_BAR = 2;
 
-    // The always visible types are visible to all windows regardless of the z-order.
-    public static final int FIRST_ALWAYS_VISIBLE_TYPE = 3;
-    public static final int ITYPE_TOP_GESTURES = FIRST_ALWAYS_VISIBLE_TYPE;
+    public static final int ITYPE_TOP_GESTURES = 3;
     public static final int ITYPE_BOTTOM_GESTURES = 4;
     public static final int ITYPE_LEFT_GESTURES = 5;
     public static final int ITYPE_RIGHT_GESTURES = 6;
@@ -119,16 +117,15 @@
     public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9;
     public static final int ITYPE_RIGHT_MANDATORY_GESTURES = 10;
 
-    public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 11;
-    public static final int ITYPE_TOP_DISPLAY_CUTOUT = 12;
-    public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 13;
-    public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 14;
-    public static final int LAST_ALWAYS_VISIBLE_TYPE = ITYPE_BOTTOM_DISPLAY_CUTOUT;
+    public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 11;
+    public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 12;
+    public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 13;
+    public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 14;
 
-    public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 15;
-    public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 16;
-    public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 17;
-    public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 18;
+    public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 15;
+    public static final int ITYPE_TOP_DISPLAY_CUTOUT = 16;
+    public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 17;
+    public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 18;
 
     /** Input method window. */
     public static final int ITYPE_IME = 19;
@@ -185,18 +182,6 @@
     }
 
     /**
-     * Mirror the always visible sources from the other state. They will share the same object for
-     * the always visible types.
-     *
-     * @param other the state to mirror the mirrored sources from.
-     */
-    public void mirrorAlwaysVisibleInsetsSources(InsetsState other) {
-        for (int type = FIRST_ALWAYS_VISIBLE_TYPE; type <= LAST_ALWAYS_VISIBLE_TYPE; type++) {
-            mSources[type] = other.mSources[type];
-        }
-    }
-
-    /**
      * Calculates {@link WindowInsets} based on the current source configuration.
      *
      * @param frame The frame to calculate the insets relative to.
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index a2777fe..24bc308 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -218,6 +218,15 @@
     public static final int FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1;
 
     /**
+     * This surface belongs to an app on the High Refresh Rate Deny list, and needs the display
+     * to operate at the exact frame rate.
+     *
+     * This is used internally by the platform and should not be used by apps.
+     * @hide
+     */
+    public static final int FRAME_RATE_COMPATIBILITY_EXACT = 100;
+
+    /**
      * Create an empty surface, which will later be filled in by readFromParcel().
      * @hide
      */
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 9932b2a..106e392 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -339,8 +339,6 @@
      */
     public long mNativeObject;
     private long mNativeHandle;
-    private boolean mDebugRelease = false;
-    private Throwable mReleaseStack = null;
 
     // TODO: Move width/height to native and fix locking through out.
     private final Object mLock = new Object();
@@ -588,13 +586,6 @@
         }
         mNativeObject = nativeObject;
         mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0;
-        if (mNativeObject == 0) {
-            if (mDebugRelease) {
-                mReleaseStack = new Throwable("assigned zero nativeObject here");
-            }
-        } else {
-            mReleaseStack = null;
-        }
     }
 
     /**
@@ -605,7 +596,6 @@
         mWidth = other.mWidth;
         mHeight = other.mHeight;
         mLocalOwnerView = other.mLocalOwnerView;
-        mDebugRelease = other.mDebugRelease;
         assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject), callsite);
     }
 
@@ -1435,7 +1425,6 @@
         mName = in.readString8();
         mWidth = in.readInt();
         mHeight = in.readInt();
-        mDebugRelease = in.readBoolean();
 
         long object = 0;
         if (in.readInt() != 0) {
@@ -1454,12 +1443,8 @@
         dest.writeString8(mName);
         dest.writeInt(mWidth);
         dest.writeInt(mHeight);
-        dest.writeBoolean(mDebugRelease);
         if (mNativeObject == 0) {
             dest.writeInt(0);
-            if (mReleaseStack != null) {
-                Log.w(TAG, "Sending invalid " + this + " caused by:", mReleaseStack);
-            }
         } else {
             dest.writeInt(1);
         }
@@ -1471,13 +1456,6 @@
     }
 
     /**
-     * @hide
-     */
-    public void setDebugRelease(boolean debug) {
-        mDebugRelease = debug;
-    }
-
-    /**
      * Checks whether two {@link SurfaceControl} objects represent the same surface.
      *
      * @param other The other object to check
@@ -1547,9 +1525,6 @@
             nativeRelease(mNativeObject);
             mNativeObject = 0;
             mNativeHandle = 0;
-            if (mDebugRelease) {
-                mReleaseStack = new Throwable("released here");
-            }
             mCloseGuard.close();
         }
     }
@@ -1565,11 +1540,8 @@
     }
 
     private void checkNotReleased() {
-        if (mNativeObject == 0) {
-            Log.wtf(TAG, "Invalid " + this + " caused by:", mReleaseStack);
-            throw new NullPointerException(
-                "mNativeObject of " + this + " is null. Have you called release() already?");
-        }
+        if (mNativeObject == 0) throw new NullPointerException(
+                "Invalid " + this + ", mNativeObject is null. Have you called release() already?");
     }
 
     /**
@@ -2417,7 +2389,6 @@
     public static SurfaceControl mirrorSurface(SurfaceControl mirrorOf) {
         long nativeObj = nativeMirrorSurface(mirrorOf.mNativeObject);
         SurfaceControl sc = new SurfaceControl();
-        sc.mDebugRelease = mirrorOf.mDebugRelease;
         sc.assignNativeObject(nativeObj, "mirrorSurface");
         return sc;
     }
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 2168dd0..7a5561c 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -240,7 +240,7 @@
 
     /**
      * Creates a indexOf(type) -> inset map for which the {@code insets} is just mapped to
-     * {@link InsetsType#statusBars()} and {@link InsetsType#navigationBars()}, depending on the
+     * {@link Type#statusBars()} and {@link Type#navigationBars()}, depending on the
      * location of the inset.
      */
     private static Insets[] createCompatTypeMap(@Nullable Rect insets) {
@@ -321,9 +321,9 @@
 
     /**
      * Returns the insets of a specific set of windows causing insets, denoted by the
-     * {@code typeMask} bit mask of {@link InsetsType}s.
+     * {@code typeMask} bit mask of {@link Type}s.
      *
-     * @param typeMask Bit mask of {@link InsetsType}s to query the insets for.
+     * @param typeMask Bit mask of {@link Type}s to query the insets for.
      * @return The insets.
      */
     @NonNull
@@ -333,7 +333,7 @@
 
     /**
      * Returns the insets a specific set of windows can cause, denoted by the
-     * {@code typeMask} bit mask of {@link InsetsType}s, regardless of whether that type is
+     * {@code typeMask} bit mask of {@link Type}s, regardless of whether that type is
      * currently visible or not.
      *
      * <p>The insets represents the area of a a window that that <b>may</b> be partially
@@ -342,7 +342,7 @@
      * normally shown, but temporarily hidden, the inset returned here will still provide the inset
      * associated with the status bar being shown.</p>
      *
-     * @param typeMask Bit mask of {@link InsetsType}s to query the insets for.
+     * @param typeMask Bit mask of {@link Type}s to query the insets for.
      * @return The insets.
      *
      * @throws IllegalArgumentException If the caller tries to query {@link Type#ime()}. Insets are
@@ -362,7 +362,7 @@
      * Returns whether a set of windows that may cause insets is currently visible on screen,
      * regardless of whether it actually overlaps with this window.
      *
-     * @param typeMask Bit mask of {@link Type.InsetsType}s to query visibility status.
+     * @param typeMask Bit mask of {@link Type}s to query visibility status.
      * @return {@code true} if and only if all windows included in {@code typeMask} are currently
      *         visible on screen.
      */
@@ -1148,7 +1148,7 @@
          *
          * @see #getInsets(int)
          *
-         * @param typeMask The bitmask of {@link InsetsType} to set the insets for.
+         * @param typeMask The bitmask of {@link Type} to set the insets for.
          * @param insets The insets to set.
          *
          * @return itself
@@ -1172,7 +1172,7 @@
          *
          * @see #getInsetsIgnoringVisibility(int)
          *
-         * @param typeMask The bitmask of {@link InsetsType} to set the insets for.
+         * @param typeMask The bitmask of {@link Type} to set the insets for.
          * @param insets The insets to set.
          *
          * @return itself
@@ -1201,7 +1201,7 @@
          *
          * @see #isVisible(int)
          *
-         * @param typeMask The bitmask of {@link InsetsType} to set the visibility for.
+         * @param typeMask The bitmask of {@link Type} to set the visibility for.
          * @param visible Whether to mark the windows as visible or not.
          *
          * @return itself
diff --git a/core/java/android/view/WindowInsetsAnimation.java b/core/java/android/view/WindowInsetsAnimation.java
index cf5e7e3..ab5b5ba 100644
--- a/core/java/android/view/WindowInsetsAnimation.java
+++ b/core/java/android/view/WindowInsetsAnimation.java
@@ -61,7 +61,7 @@
     }
 
     /**
-     * @return The bitmask of {@link WindowInsets.Type.InsetsType}s that are animating.
+     * @return The bitmask of {@link WindowInsets.Type}s that are animating.
      */
     @WindowInsets.Type.InsetsType
     public int getTypeMask() {
@@ -140,7 +140,7 @@
     }
 
     /**
-     * Set fraction of the progress if {@link WindowInsets.Type.InsetsType} animation is
+     * Set fraction of the progress if {@link WindowInsets.Type} animation is
      * controlled by the app.
      * <p>
      * Note: This should only be used for testing, as the system fills in the fraction for the
@@ -159,7 +159,7 @@
     /**
      * Retrieves the translucency of the windows that are animating.
      *
-     * @return Alpha of windows that cause insets of type {@link WindowInsets.Type.InsetsType}.
+     * @return Alpha of windows that cause insets of type {@link WindowInsets.Type}.
      */
     @FloatRange(from = 0f, to = 1f)
     public float getAlpha() {
@@ -174,8 +174,7 @@
      * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)} is being
      * used.
      * </p>
-     * @param alpha Alpha of windows that cause insets of type
-     *              {@link WindowInsets.Type.InsetsType}.
+     * @param alpha Alpha of windows that cause insets of type {@link WindowInsets.Type}.
      * @see #getAlpha()
      */
     public void setAlpha(@FloatRange(from = 0f, to = 1f) float alpha) {
diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java
index b61fa36..140a9a8 100644
--- a/core/java/android/view/WindowInsetsAnimationControlListener.java
+++ b/core/java/android/view/WindowInsetsAnimationControlListener.java
@@ -46,8 +46,8 @@
      * to redraw because of an {@link EditorInfo} change, or when the window is starting up.
      *
      * @param controller The controller to control the inset animation.
-     * @param types The {@link InsetsType}s it was able to gain control over. Note that this may be
-     *              different than the types passed into
+     * @param types The {@link WindowInsets.Type}s it was able to gain control over. Note that this
+     *              may be different than the types passed into
      *              {@link WindowInsetsController#controlWindowInsetsAnimation} in case the window
      *              wasn't able to gain the controls because it wasn't the IME target or not
      *              currently the window that's controlling the system bars.
diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java
index 792b974..6578e9b 100644
--- a/core/java/android/view/WindowInsetsAnimationController.java
+++ b/core/java/android/view/WindowInsetsAnimationController.java
@@ -99,7 +99,7 @@
     float getCurrentAlpha();
 
     /**
-     * @return The {@link InsetsType}s this object is currently controlling.
+     * @return The {@link WindowInsets.Type}s this object is currently controlling.
      */
     @InsetsType int getTypes();
 
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index fb9bcbd..991ed55 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -142,7 +142,7 @@
      * change as soon as the window gains control. The app can listen to the event by observing
      * {@link View#onApplyWindowInsets} and checking visibility with {@link WindowInsets#isVisible}.
      *
-     * @param types A bitmask of {@link InsetsType} specifying what windows the app
+     * @param types A bitmask of {@link WindowInsets.Type} specifying what windows the app
      *              would like to make appear on screen.
      */
     void show(@InsetsType int types);
@@ -154,7 +154,7 @@
      * change as soon as the window gains control. The app can listen to the event by observing
      * {@link View#onApplyWindowInsets} and checking visibility with {@link WindowInsets#isVisible}.
      *
-     * @param types A bitmask of {@link InsetsType} specifying what windows the app
+     * @param types A bitmask of {@link WindowInsets.Type} specifying what windows the app
      *              would like to make disappear.
      */
     void hide(@InsetsType int types);
@@ -163,7 +163,7 @@
      * Lets the application control window inset animations in a frame-by-frame manner by modifying
      * the position of the windows in the system causing insets directly.
      *
-     * @param types The {@link InsetsType}s the application has requested to control.
+     * @param types The {@link WindowInsets.Type}s the application has requested to control.
      * @param durationMillis Duration of animation in
      *                       {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the
      *                       animation doesn't have a predetermined duration. This value will be
@@ -199,7 +199,7 @@
      * setSystemBarsAppearance(0, APPEARANCE_LIGHT_STATUS_BARS)
      * </pre>
      *
-     * @param appearance Bitmask of {@link Appearance} flags.
+     * @param appearance Bitmask of appearance flags.
      * @param mask Specifies which flags of appearance should be changed.
      * @see #getSystemBarsAppearance
      */
@@ -280,11 +280,11 @@
             @NonNull OnControllableInsetsChangedListener listener);
 
     /**
-     * Listener to be notified when the set of controllable {@link InsetsType} controlled by a
-     * {@link WindowInsetsController} changes.
+     * Listener to be notified when the set of controllable {@link WindowInsets.Type} controlled by
+     * a {@link WindowInsetsController} changes.
      * <p>
-     * Once a {@link InsetsType} becomes controllable, the app will be able to control the window
-     * that is causing this type of insets by calling {@link #controlWindowInsetsAnimation}.
+     * Once a {@link WindowInsets.Type} becomes controllable, the app will be able to control the
+     * window that is causing this type of insets by calling {@link #controlWindowInsetsAnimation}.
      * <p>
      * Note: When listening to controllability of the {@link Type#ime},
      * {@link #controlWindowInsetsAnimation} may still fail in case the {@link InputMethodService}
@@ -297,12 +297,12 @@
     interface OnControllableInsetsChangedListener {
 
         /**
-         * Called when the set of controllable {@link InsetsType} changes.
+         * Called when the set of controllable {@link WindowInsets.Type} changes.
          *
-         * @param controller The controller for which the set of controllable {@link InsetsType}s
-         *                   are changing.
-         * @param typeMask Bitwise type-mask of the {@link InsetsType}s the controller is currently
-         *                 able to control.
+         * @param controller The controller for which the set of controllable
+         *                   {@link WindowInsets.Type}s are changing.
+         * @param typeMask Bitwise type-mask of the {@link WindowInsets.Type}s the controller is
+         *                 currently able to control.
          */
         void onControllableInsetsChanged(@NonNull WindowInsetsController controller,
                 @InsetsType int typeMask);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1327f9c..7faa222 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3326,8 +3326,8 @@
         /**
          * Specifies types of insets that this window should avoid overlapping during layout.
          *
-         * @param types which types of insets that this window should avoid. The initial value of
-         *              this object includes all system bars.
+         * @param types which {@link WindowInsets.Type}s of insets that this window should avoid.
+         *              The initial value of this object includes all system bars.
          */
         public void setFitInsetsTypes(@InsetsType int types) {
             mFitInsetsTypes = types;
@@ -3401,7 +3401,7 @@
         }
 
         /**
-         * @return the insets types that this window is avoiding overlapping.
+         * @return the {@link WindowInsets.Type}s that this window is avoiding overlapping.
          */
         public @InsetsType int getFitInsetsTypes() {
             return mFitInsetsTypes;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 5140c09..90c8e17 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2307,7 +2307,9 @@
     public void removeImeSurface(IBinder windowToken) {
         synchronized (mH) {
             try {
-                mService.removeImeSurfaceFromWindow(windowToken);
+                final Completable.Void value = Completable.createVoid();
+                mService.removeImeSurfaceFromWindow(windowToken, ResultCallbacks.of(value));
+                Completable.getResult(value);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -3239,7 +3241,9 @@
     @Deprecated
     public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
         try {
-            mService.setAdditionalInputMethodSubtypes(imiId, subtypes);
+            final Completable.Void value = Completable.createVoid();
+            mService.setAdditionalInputMethodSubtypes(imiId, subtypes, ResultCallbacks.of(value));
+            Completable.getResult(value);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java
index 675f32b..22c3e57 100644
--- a/core/java/android/view/translation/Translator.java
+++ b/core/java/android/view/translation/Translator.java
@@ -28,6 +28,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.service.translation.ITranslationCallback;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -36,9 +37,11 @@
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 
 /**
  * The {@link Translator} for translation, defined by a source and a dest {@link TranslationSpec}.
@@ -295,4 +298,49 @@
     }
 
     // TODO: add methods for UI-toolkit case.
+    /** @hide */
+    public void requestUiTranslate(@NonNull List<TranslationRequest> requests,
+            @NonNull Consumer<TranslationResponse> responseCallback) {
+        if (mDirectServiceBinder == null) {
+            Log.wtf(TAG, "Translator created without proper initialization.");
+            return;
+        }
+        final android.service.translation.TranslationRequest request =
+                new android.service.translation.TranslationRequest
+                        .Builder(getNextRequestId(), mSourceSpec, mDestSpec, requests)
+                        .build();
+        final ITranslationCallback callback =
+                new TranslationResponseCallbackImpl(responseCallback);
+        try {
+            mDirectServiceBinder.onTranslationRequest(request, mId, callback, null);
+        } catch (RemoteException e) {
+            Log.w(TAG, "RemoteException calling flushRequest");
+        }
+    }
+
+    private static class TranslationResponseCallbackImpl extends ITranslationCallback.Stub {
+
+        private final WeakReference<Consumer<TranslationResponse>> mResponseCallback;
+
+        TranslationResponseCallbackImpl(Consumer<TranslationResponse> responseCallback) {
+            mResponseCallback = new WeakReference<>(responseCallback);
+        }
+
+        @Override
+        public void onTranslationComplete(TranslationResponse response) throws RemoteException {
+            provideTranslationResponse(response);
+        }
+
+        @Override
+        public void onError() throws RemoteException {
+            provideTranslationResponse(null);
+        }
+
+        private void provideTranslationResponse(TranslationResponse response) {
+            final Consumer<TranslationResponse> responseCallback = mResponseCallback.get();
+            if (responseCallback != null) {
+                responseCallback.accept(response);
+            }
+        }
+    }
 }
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index a810c2e..fa46146 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -16,35 +16,274 @@
 
 package android.view.translation;
 
+import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_FINISHED;
+import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_PAUSED;
+import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_RESUMED;
+import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_STARTED;
+
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
 import android.app.Activity;
 import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+import android.view.View;
 import android.view.autofill.AutofillId;
+import android.view.translation.UiTranslationManager.UiTranslationState;
 
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Consumer;
 
 /**
- * A controller to manage the ui translation requests.
+ * A controller to manage the ui translation requests for the {@link Activity}.
  *
  * @hide
  */
 public class UiTranslationController {
 
     private static final String TAG = "UiTranslationController";
-
+    @NonNull
     private final Activity mActivity;
-
+    @NonNull
     private final Context mContext;
+    @NonNull
+    private final Object mLock = new Object();
+
+    // Each Translator is distinguished by sourceSpec and desSepc.
+    @NonNull
+    private final ArrayMap<Pair<TranslationSpec, TranslationSpec>, Translator> mTranslators;
+    @NonNull
+    private final ArrayMap<AutofillId, WeakReference<View>> mViews;
+    @NonNull
+    private final HandlerThread mWorkerThread;
+    @NonNull
+    private final Handler mWorkerHandler;
 
     public UiTranslationController(Activity activity, Context context) {
         mActivity = activity;
         mContext = context;
+        mViews = new ArrayMap<>();
+        mTranslators = new ArrayMap<>();
+
+        mWorkerThread =
+                new HandlerThread("UiTranslationController_" + mActivity.getComponentName(),
+                        Process.THREAD_PRIORITY_FOREGROUND);
+        mWorkerThread.start();
+        mWorkerHandler = mWorkerThread.getThreadHandler();
     }
 
     /**
      * Update the Ui translation state.
      */
-    public void updateUiTranslationState(int state, TranslationSpec sourceSpec,
+    public void updateUiTranslationState(@UiTranslationState int state, TranslationSpec sourceSpec,
             TranslationSpec destSpec, List<AutofillId> views) {
-        // Implement it. Deal with the each states
+        if (!mActivity.isResumed()) {
+            return;
+        }
+        switch (state) {
+            case STATE_UI_TRANSLATION_STARTED:
+                final Pair<TranslationSpec, TranslationSpec> specs =
+                        new Pair<>(sourceSpec, destSpec);
+                if (!mTranslators.containsKey(specs)) {
+                    mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
+                            UiTranslationController::createTranslatorAndStart,
+                            UiTranslationController.this, sourceSpec, destSpec, views));
+                } else {
+                    onUiTranslationStarted(mTranslators.get(specs), views);
+                }
+                break;
+            case STATE_UI_TRANSLATION_PAUSED:
+                runForEachView((view) -> view.onPauseUiTranslation(), STATE_UI_TRANSLATION_PAUSED);
+                break;
+            case STATE_UI_TRANSLATION_RESUMED:
+                runForEachView((view) -> view.onRestoreUiTranslation(),
+                        STATE_UI_TRANSLATION_PAUSED);
+                break;
+            case STATE_UI_TRANSLATION_FINISHED:
+                destroyTranslators();
+                runForEachView((view) -> view.onFinishUiTranslation(), STATE_UI_TRANSLATION_PAUSED);
+                break;
+            default:
+                Log.w(TAG, "onAutoTranslationStateChange(): unknown state: " + state);
+        }
+    }
+
+    /**
+     * Called when the Activity is destroyed.
+     */
+    public void onActivityDestroyed() {
+        synchronized (mLock) {
+            mViews.clear();
+            destroyTranslators();
+            mWorkerThread.quitSafely();
+        }
+    }
+
+    /**
+     * The method is used by {@link Translator}, it will be called when the translation is done. The
+     * translation result can be get from here.
+     */
+    public void onTranslationCompleted(TranslationResponse response) {
+        if (response == null || response.getTranslationStatus()
+                != TranslationResponse.TRANSLATION_STATUS_SUCCESS) {
+            Log.w(TAG, "Fail result from TranslationService, response: " + response);
+            return;
+        }
+        final List<TranslationRequest> translatedResult = response.getTranslations();
+        onTranslationCompleted(translatedResult);
+    }
+
+    private void onTranslationCompleted(List<TranslationRequest> translatedResult) {
+        if (!mActivity.isResumed()) {
+            return;
+        }
+        final int resultCount = translatedResult.size();
+        synchronized (mLock) {
+            for (int i = 0; i < resultCount; i++) {
+                final TranslationRequest request = translatedResult.get(i);
+                final AutofillId autofillId = request.getAutofillId();
+                if (autofillId == null) {
+                    continue;
+                }
+                final View view = mViews.get(autofillId).get();
+                if (view == null) {
+                    Log.w(TAG, "onTranslationCompleted: the Veiew for autofill id " + autofillId
+                            + " may be gone.");
+                    continue;
+                }
+                mActivity.runOnUiThread(() -> view.onTranslationComplete(request));
+            }
+        }
+    }
+
+    /**
+     * Called when there is an ui translation request comes to request view translation.
+     */
+    @WorkerThread
+    private void createTranslatorAndStart(TranslationSpec sourceSpec, TranslationSpec destSpec,
+            List<AutofillId> views) {
+        // Create Translator
+        final Translator translator = createTranslatorIfNeeded(sourceSpec, destSpec);
+        if (translator == null) {
+            Log.w(TAG, "Can not create Translator for sourceSpec:" + sourceSpec + " destSpec:"
+                    + destSpec);
+            return;
+        }
+        onUiTranslationStarted(translator, views);
+    }
+
+    @WorkerThread
+    private void sendTranslationRequest(Translator translator,
+            ArrayList<TranslationRequest> requests) {
+        translator.requestUiTranslate(requests, this::onTranslationCompleted);
+    }
+
+    /**
+     * Called when there is an ui translation request comes to request view translation.
+     */
+    private void onUiTranslationStarted(Translator translator, List<AutofillId> views) {
+        synchronized (mLock) {
+            if (views == null || views.size() == 0) {
+                throw new IllegalArgumentException("Invalid empty views: " + views);
+            }
+            // Find Views collect the translation data
+            // TODO(b/178084101): try to optimize, e.g. to this in a single traversal
+            final int viewCounts = views.size();
+            final ArrayList<TranslationRequest> requests = new ArrayList<>();
+            for (int i = 0; i < viewCounts; i++) {
+                final AutofillId viewAutofillId = views.get(i);
+                final View view = mActivity.findViewByAutofillIdTraversal(viewAutofillId);
+                if (view == null) {
+                    Log.w(TAG, "Can not find the View for autofill id= " + viewAutofillId);
+                    continue;
+                }
+                mViews.put(viewAutofillId, new WeakReference<>(view));
+                mActivity.runOnUiThread(() -> {
+                    final TranslationRequest translationRequest = view.onCreateTranslationRequest();
+                    if (translationRequest != null
+                            && translationRequest.getTranslationText().length() > 0) {
+                        requests.add(translationRequest);
+                    }
+                    if (requests.size() == viewCounts) {
+                        Log.v(TAG, "onUiTranslationStarted: send " + requests.size() + " request.");
+                        mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
+                                UiTranslationController::sendTranslationRequest,
+                                UiTranslationController.this, translator, requests));
+                    }
+                });
+            }
+        }
+    }
+
+    private void runForEachView(Consumer<View> action, @UiTranslationState int state) {
+        synchronized (mLock) {
+            mActivity.runOnUiThread(() -> {
+                final int viewCounts = mViews.size();
+                for (int i = 0; i < viewCounts; i++) {
+                    final View view = mViews.valueAt(i).get();
+                    if (view == null) {
+                        Log.w(TAG, "The View for autofill id " + mViews.keyAt(i)
+                                + " may be gone for state " + stateToString(state));
+                        continue;
+                    }
+                    action.accept(view);
+                }
+                if (state == STATE_UI_TRANSLATION_FINISHED) {
+                    mViews.clear();
+                }
+            });
+        }
+    }
+
+    private Translator createTranslatorIfNeeded(
+            TranslationSpec sourceSpec, TranslationSpec destSpec) {
+        final TranslationManager tm = mContext.getSystemService(TranslationManager.class);
+        if (tm == null) {
+            Log.e(TAG, "Can not find TranslationManager when trying to create translator.");
+            return null;
+        }
+        final Translator translator = tm.createTranslator(sourceSpec, destSpec);
+        if (translator != null) {
+            final Pair<TranslationSpec, TranslationSpec> specs = new Pair<>(sourceSpec, destSpec);
+            mTranslators.put(specs, translator);
+        }
+        return translator;
+    }
+
+    private void destroyTranslators() {
+        synchronized (mLock) {
+            final int count = mTranslators.size();
+            for (int i = 0; i < count; i++) {
+                Translator translator = mTranslators.valueAt(i);
+                translator.destroy();
+            }
+            mTranslators.clear();
+        }
+    }
+
+    /**
+     * Returns a string representation of the state.
+     */
+    public static String stateToString(@UiTranslationState int state) {
+        switch (state) {
+            case STATE_UI_TRANSLATION_STARTED:
+                return "UI_TRANSLATION_STARTED";
+            case STATE_UI_TRANSLATION_PAUSED:
+                return "UI_TRANSLATION_PAUSED";
+            case STATE_UI_TRANSLATION_RESUMED:
+                return "UI_TRANSLATION_RESUMED";
+            case STATE_UI_TRANSLATION_FINISHED:
+                return "UI_TRANSLATION_FINISHED";
+            default:
+                return "Unknown state (" + state + ")";
+        }
     }
 }
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 7a30ee2..55f8c40 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -134,7 +134,7 @@
     void noteWifiBatchedScanStartedFromSource(in WorkSource ws, int csph);
     void noteWifiBatchedScanStoppedFromSource(in WorkSource ws);
     void noteWifiRadioPowerState(int powerState, long timestampNs, int uid);
-    void noteNetworkInterfaceType(String iface, int type);
+    void noteNetworkInterfaceForTransports(String iface, in int[] transportTypes);
     void noteNetworkStatsEnabled();
     void noteDeviceIdleMode(int mode, String activeReason, int activeUid);
     void setBatteryState(int status, int health, int plugType, int level, int temp, int volt,
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index bbfd07b..c74c39a 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -27,9 +27,10 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageLite;
-import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.SELinux;
@@ -86,17 +87,19 @@
         final boolean debuggable;
 
         public static Handle create(File packageFile) throws IOException {
-            try {
-                final PackageLite lite = PackageParser.parsePackageLite(packageFile, 0);
-                return create(lite);
-            } catch (PackageParserException e) {
-                throw new IOException("Failed to parse package: " + packageFile, e);
+            final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+            final ParseResult<PackageLite> ret = ApkLiteParseUtils.parsePackageLite(input.reset(),
+                    packageFile, /* flags */ 0);
+            if (ret.isError()) {
+                throw new IOException("Failed to parse package: " + packageFile,
+                        ret.getException());
             }
+            return create(ret.getResult());
         }
 
         public static Handle create(PackageLite lite) throws IOException {
-            return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs,
-                    lite.debuggable);
+            return create(lite.getAllApkPaths(), lite.isMultiArch(), lite.isExtractNativeLibs(),
+                    lite.isDebuggable());
         }
 
         public static Handle create(List<String> codePaths, boolean multiArch,
@@ -122,14 +125,14 @@
 
         public static Handle createFd(PackageLite lite, FileDescriptor fd) throws IOException {
             final long[] apkHandles = new long[1];
-            final String path = lite.baseCodePath;
+            final String path = lite.getBaseApkPath();
             apkHandles[0] = nativeOpenApkFd(fd, path);
             if (apkHandles[0] == 0) {
                 throw new IOException("Unable to open APK " + path + " from fd " + fd);
             }
 
-            return new Handle(new String[]{path}, apkHandles, lite.multiArch,
-                    lite.extractNativeLibs, lite.debuggable);
+            return new Handle(new String[]{path}, apkHandles, lite.isMultiArch(),
+                    lite.isExtractNativeLibs(), lite.isDebuggable());
         }
 
         Handle(String[] apkPaths, long[] apkHandles, boolean multiArch,
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 2b78be3..c2f2052 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -24,8 +24,8 @@
 import android.content.pm.PackageInstaller.SessionParams;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.parsing.PackageLite;
 import android.os.Environment;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -84,8 +84,8 @@
 
     /**
      * A group of external dependencies used in
-     * {@link #resolveInstallVolume(Context, String, int, long)}. It can be backed by real values
-     * from the system or mocked ones for testing purposes.
+     * {@link #resolveInstallVolume(Context, String, int, long, TestableInterface)}.
+     * It can be backed by real values from the system or mocked ones for testing purposes.
      */
     public static abstract class TestableInterface {
         abstract public StorageManager getStorageManager(Context context);
@@ -447,7 +447,7 @@
         long sizeBytes = 0;
 
         // Include raw APKs, and possibly unpacked resources
-        for (String codePath : pkg.getAllCodePaths()) {
+        for (String codePath : pkg.getAllApkPaths()) {
             final File codeFile = new File(codePath);
             sizeBytes += codeFile.length();
         }
diff --git a/core/java/com/android/internal/content/om/OverlayScanner.java b/core/java/com/android/internal/content/om/OverlayScanner.java
index a85cf56..6b5cb8d 100644
--- a/core/java/com/android/internal/content/om/OverlayScanner.java
+++ b/core/java/com/android/internal/content/om/OverlayScanner.java
@@ -20,7 +20,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.ApkLite;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
 import android.util.ArrayMap;
 import android.util.Log;
 
@@ -124,15 +127,17 @@
     /** Extracts information about the overlay from its manifest. */
     @VisibleForTesting
     public ParsedOverlayInfo parseOverlayManifest(File overlayApk) {
-        try {
-            final PackageParser.ApkLite apkLite = PackageParser.parseApkLite(overlayApk, 0);
-            return apkLite.targetPackageName == null ? null :
-                    new ParsedOverlayInfo(apkLite.packageName, apkLite.targetPackageName,
-                            apkLite.targetSdkVersion, apkLite.overlayIsStatic,
-                            apkLite.overlayPriority, new File(apkLite.codePath));
-        } catch (PackageParser.PackageParserException e) {
-            Log.w(TAG, "Got exception loading overlay.", e);
+        final ParseTypeImpl input = ParseTypeImpl.forParsingWithoutPlatformCompat();
+        final ParseResult<ApkLite> ret = ApkLiteParseUtils.parseApkLite(input.reset(),
+                overlayApk, /* flags */ 0);
+        if (ret.isError()) {
+            Log.w(TAG, "Got exception loading overlay.", ret.getException());
             return null;
         }
+        final ApkLite apkLite = ret.getResult();
+        return apkLite.getTargetPackageName() == null ? null :
+                new ParsedOverlayInfo(apkLite.getPackageName(), apkLite.getTargetPackageName(),
+                        apkLite.getTargetSdkVersion(), apkLite.isOverlayIsStatic(),
+                        apkLite.getOverlayPriority(), new File(apkLite.getPath()));
     }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 4a24358..a21c68b 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -23,7 +23,6 @@
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.hardware.SensorManager;
-import android.net.ConnectivityManager;
 import android.os.BatteryStats;
 import android.os.BatteryStats.Uid;
 import android.os.Build;
@@ -37,6 +36,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -120,12 +120,11 @@
     private double mMaxDrainedPower;
 
     public static boolean checkWifiOnly(Context context) {
-        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
-                Context.CONNECTIVITY_SERVICE);
-        if (cm == null) {
+        final TelephonyManager tm = context.getSystemService(TelephonyManager.class);
+        if (tm == null) {
             return false;
         }
-        return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+        return !tm.isDataCapable();
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 37b621d..2b034b0 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.os;
 
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
 import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
 
@@ -36,7 +38,6 @@
 import android.database.ContentObserver;
 import android.hardware.usb.UsbManager;
 import android.location.GnssSignalQuality;
-import android.net.ConnectivityManager;
 import android.net.INetworkStatsService;
 import android.net.NetworkStats;
 import android.net.Uri;
@@ -111,6 +112,7 @@
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.XmlUtils;
+import com.android.net.module.util.NetworkCapabilitiesUtils;
 
 import libcore.util.EmptyArray;
 
@@ -1097,16 +1099,6 @@
     private long[] mCpuFreqs;
 
     /**
-     * Times spent by the system server process grouped by cluster and CPU speed.
-     */
-    private LongSamplingCounterArray mSystemServerCpuTimesUs;
-
-    /**
-     * Times spent by the system server threads grouped by cluster and CPU speed.
-     */
-    private LongSamplingCounterArray mSystemServerThreadCpuTimesUs;
-
-    /**
      * Times spent by the system server threads handling incoming binder requests.
      */
     private LongSamplingCounterArray mBinderThreadCpuTimesUs;
@@ -6711,11 +6703,12 @@
     }
 
     /** @hide */
-    public void noteNetworkInterfaceType(String iface, int networkType) {
+    public void noteNetworkInterfaceForTransports(String iface, int[] transportTypes) {
         if (TextUtils.isEmpty(iface)) return;
+        final int displayTransport = NetworkCapabilitiesUtils.getDisplayTransport(transportTypes);
 
         synchronized (mModemNetworkLock) {
-            if (ConnectivityManager.isNetworkTypeMobile(networkType)) {
+            if (displayTransport == TRANSPORT_CELLULAR) {
                 mModemIfaces = includeInStringArray(mModemIfaces, iface);
                 if (DEBUG) Slog.d(TAG, "Note mobile iface " + iface + ": " + mModemIfaces);
             } else {
@@ -6725,7 +6718,7 @@
         }
 
         synchronized (mWifiNetworkLock) {
-            if (ConnectivityManager.isNetworkTypeWifi(networkType)) {
+            if (displayTransport == TRANSPORT_WIFI) {
                 mWifiIfaces = includeInStringArray(mWifiIfaces, iface);
                 if (DEBUG) Slog.d(TAG, "Note wifi iface " + iface + ": " + mWifiIfaces);
             } else {
@@ -10853,6 +10846,14 @@
         }
     }
 
+    /**
+     * Starts tracking CPU time-in-state for threads of the system server process,
+     * keeping a separate account of threads receiving incoming binder calls.
+     */
+    public void startTrackingSystemServerCpuTime() {
+        mSystemServerCpuThreadReader.startTrackingThreadCpuTime();
+    }
+
     public void setCallback(BatteryCallback cb) {
         mCallback = cb;
     }
@@ -11505,8 +11506,6 @@
 
         MeasuredEnergyStats.resetIfNotNull(mGlobalMeasuredEnergyStats);
 
-        resetIfNotNull(mSystemServerCpuTimesUs, false, elapsedRealtimeUs);
-        resetIfNotNull(mSystemServerThreadCpuTimesUs, false, elapsedRealtimeUs);
         resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs);
 
         mLastHistoryStepDetails = null;
@@ -12692,27 +12691,17 @@
             return;
         }
 
-        if (mSystemServerCpuTimesUs == null) {
-            mSystemServerCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
-            mSystemServerThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
+        if (mBinderThreadCpuTimesUs == null) {
             mBinderThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
         }
-        mSystemServerCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.processCpuTimesUs);
-        mSystemServerThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.threadCpuTimesUs);
         mBinderThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
 
         if (DEBUG_BINDER_STATS) {
-            Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)");
-            long totalCpuTimeMs = 0;
-            long totalThreadTimeMs = 0;
+            Slog.d(TAG, "System server threads per CPU cluster (incoming binder threads)");
             long binderThreadTimeMs = 0;
             int cpuIndex = 0;
-            final long[] systemServerCpuTimesUs =
-                    mSystemServerCpuTimesUs.getCountsLocked(0);
-            final long[] systemServerThreadCpuTimesUs =
-                    mSystemServerThreadCpuTimesUs.getCountsLocked(0);
-            final long[] binderThreadCpuTimesUs =
-                    mBinderThreadCpuTimesUs.getCountsLocked(0);
+            final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked(
+                    BatteryStats.STATS_SINCE_CHARGED);
             int index = 0;
             int numCpuClusters = mPowerProfile.getNumCpuClusters();
             for (int cluster = 0; cluster < numCpuClusters; cluster++) {
@@ -12723,28 +12712,15 @@
                     if (speed != 0) {
                         sb.append(", ");
                     }
-                    long totalCountMs = systemServerThreadCpuTimesUs[index] / 1000;
                     long binderCountMs = binderThreadCpuTimesUs[index] / 1000;
-                    sb.append(String.format("%d/%d(%.1f%%)",
-                            binderCountMs,
-                            totalCountMs,
-                            totalCountMs != 0 ? (double) binderCountMs * 100 / totalCountMs : 0));
+                    sb.append(TextUtils.formatSimple("%10d", binderCountMs));
 
-                    totalCpuTimeMs += systemServerCpuTimesUs[index] / 1000;
-                    totalThreadTimeMs += totalCountMs;
                     binderThreadTimeMs += binderCountMs;
                     index++;
                 }
                 cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster);
                 Slog.d(TAG, sb.toString());
             }
-
-            Slog.d(TAG, "Total system server CPU time (ms): " + totalCpuTimeMs);
-            Slog.d(TAG, "Total system server thread time (ms): " + totalThreadTimeMs);
-            Slog.d(TAG, String.format("Total Binder thread time (ms): %d (%.1f%%)",
-                    binderThreadTimeMs,
-                    binderThreadTimeMs != 0
-                            ? (double) binderThreadTimeMs * 100 / totalThreadTimeMs : 0));
         }
     }
 
@@ -14019,60 +13995,16 @@
     }
 
 
+    /**
+     * Estimates the time spent by the system server handling incoming binder requests.
+     */
     @Override
     public long[] getSystemServiceTimeAtCpuSpeeds() {
-        // Estimates the time spent by the system server handling incoming binder requests.
-        //
-        // The data that we can get from the kernel is this:
-        //   - CPU duration for a (thread - cluster - CPU speed) combination
-        //   - CPU duration for a (UID - cluster - CPU speed) combination
-        //
-        // The configuration we have in the Power Profile is this:
-        //   - Average CPU power for a (cluster - CPU speed) combination.
-        //
-        // The model used by BatteryStats can be illustrated with this example:
-        //
-        // - Let's say the system server has 10 threads.
-        // - These 10 threads spent 1000 ms of CPU time in aggregate
-        // - Of the 10 threads 4 were execute exclusively incoming binder calls.
-        // - These 4 "binder" threads consumed 600 ms of CPU time in aggregate
-        // - The real time spent by the system server process doing all of this is, say, 200 ms.
-        //
-        // We will assume that power consumption is proportional to the time spent by the CPU
-        // across all threads.  This is a crude assumption, but we don't have more detailed data.
-        // Thus,
-        //   binderRealTime = realTime * aggregateBinderThreadTime / aggregateAllThreadTime
-        //
-        // In our example,
-        //   binderRealTime = 200 * 600 / 1000 = 120ms
-        //
-        // We can then multiply this estimated time by the average power to obtain an estimate
-        // of the total power consumed by incoming binder calls for the given cluster/speed
-        // combination.
-
-        if (mSystemServerCpuTimesUs == null) {
+        if (mBinderThreadCpuTimesUs == null) {
             return null;
         }
 
-        final long[] systemServerCpuTimesUs = mSystemServerCpuTimesUs.getCountsLocked(
-                BatteryStats.STATS_SINCE_CHARGED);
-        final long [] systemServerThreadCpuTimesUs = mSystemServerThreadCpuTimesUs.getCountsLocked(
-                BatteryStats.STATS_SINCE_CHARGED);
-        final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked(
-                BatteryStats.STATS_SINCE_CHARGED);
-
-        final int size = systemServerCpuTimesUs.length;
-        final long[] results = new long[size];
-
-        for (int i = 0; i < size; i++) {
-            if (systemServerThreadCpuTimesUs[i] == 0) {
-                continue;
-            }
-
-            results[i] = systemServerCpuTimesUs[i] * binderThreadCpuTimesUs[i]
-                    / systemServerThreadCpuTimesUs[i];
-        }
-        return results;
+        return mBinderThreadCpuTimesUs.getCountsLocked(BatteryStats.STATS_SINCE_CHARGED);
     }
 
     /**
@@ -14503,7 +14435,7 @@
         }
 
         updateSystemServiceCallStats();
-        if (mSystemServerThreadCpuTimesUs != null) {
+        if (mBinderThreadCpuTimesUs != null) {
             pw.println("Per UID System server binder time in ms:");
             long[] systemServiceTimeAtCpuSpeeds = getSystemServiceTimeAtCpuSpeeds();
             for (int i = 0; i < size; i++) {
@@ -16094,9 +16026,6 @@
             mUidStats.append(uid, u);
         }
 
-        mSystemServerCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase);
-        mSystemServerThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in,
-                mOnBatteryTimeBase);
         mBinderThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase);
     }
 
@@ -16305,8 +16234,6 @@
         } else {
             out.writeInt(0);
         }
-        LongSamplingCounterArray.writeToParcel(out, mSystemServerCpuTimesUs);
-        LongSamplingCounterArray.writeToParcel(out, mSystemServerThreadCpuTimesUs);
         LongSamplingCounterArray.writeToParcel(out, mBinderThreadCpuTimesUs);
     }
 
diff --git a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
index e6a9623..4d2a08a 100644
--- a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
@@ -16,23 +16,12 @@
 
 package com.android.internal.os;
 
-import static android.os.Process.PROC_OUT_LONG;
-import static android.os.Process.PROC_SPACE_TERM;
-
 import android.annotation.Nullable;
-import android.os.Process;
-import android.system.Os;
-import android.system.OsConstants;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.IOException;
-import java.nio.file.DirectoryIteratorException;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.util.Arrays;
 
 /**
@@ -45,93 +34,65 @@
     private static final String TAG = "KernelSingleProcCpuThreadRdr";
 
     private static final boolean DEBUG = false;
-    private static final boolean NATIVE_ENABLED = true;
-
-    /**
-     * The name of the file to read CPU statistics from, must be found in {@code
-     * /proc/$PID/task/$TID}
-     */
-    private static final String CPU_STATISTICS_FILENAME = "time_in_state";
-
-    private static final String PROC_STAT_FILENAME = "stat";
-
-    /** Directory under /proc/$PID containing CPU stats files for threads */
-    public static final String THREAD_CPU_STATS_DIRECTORY = "task";
-
-    /** Default mount location of the {@code proc} filesystem */
-    private static final Path DEFAULT_PROC_PATH = Paths.get("/proc");
-
-    /** The initial {@code time_in_state} file for {@link ProcTimeInStateReader} */
-    private static final Path INITIAL_TIME_IN_STATE_PATH = Paths.get("self/time_in_state");
-
-    /** See https://man7.org/linux/man-pages/man5/proc.5.html */
-    private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] {
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM,
-            PROC_SPACE_TERM | PROC_OUT_LONG,                  // 14: utime
-            PROC_SPACE_TERM | PROC_OUT_LONG,                  // 15: stime
-            // Ignore remaining fields
-    };
-
-    private final long[] mProcessFullStatsData = new long[2];
-
-    private static final int PROCESS_FULL_STAT_UTIME = 0;
-    private static final int PROCESS_FULL_STAT_STIME = 1;
-
-    /** Used to read and parse {@code time_in_state} files */
-    private final ProcTimeInStateReader mProcTimeInStateReader;
 
     private final int mPid;
 
-    /** Where the proc filesystem is mounted */
-    private final Path mProcPath;
+    private final CpuTimeInStateReader mCpuTimeInStateReader;
 
-    // How long a CPU jiffy is in milliseconds.
-    private final long mJiffyMillis;
-
-    // Path: /proc/<pid>/stat
-    private final String mProcessStatFilePath;
-
-    // Path: /proc/<pid>/task
-    private final Path mThreadsDirectoryPath;
+    private int[] mSelectedThreadNativeTids = new int[0];  // Sorted
 
     /**
-     * Count of frequencies read from the {@code time_in_state} file. Read from {@link
-     * #mProcTimeInStateReader#getCpuFrequenciesKhz()}.
+     * Count of frequencies read from the {@code time_in_state} file.
      */
     private int mFrequencyCount;
 
+    private boolean mIsTracking;
+
+    /**
+     * A CPU time-in-state provider for testing.  Imitates the behavior of the corresponding
+     * methods in frameworks/native/libs/cputimeinstate/cputimeinstate.c
+     */
+    @VisibleForTesting
+    public interface CpuTimeInStateReader {
+        /**
+         * Returns the overall number of cluster-frequency combinations.
+         */
+        int getCpuFrequencyCount();
+
+        /**
+         * Returns true to indicate success.
+         *
+         * Called from native.
+         */
+        boolean startTrackingProcessCpuTimes(int tgid);
+
+        /**
+         * Returns true to indicate success.
+         *
+         * Called from native.
+         */
+        boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey);
+
+        /**
+         * Must return an array of strings formatted like this:
+         * "aggKey:t0_0 t0_1...:t1_0 t1_1..."
+         * Times should be provided in nanoseconds.
+         *
+         * Called from native.
+         */
+        String[] getAggregatedTaskCpuFreqTimes(int pid);
+    }
+
     /**
      * Create with a path where `proc` is mounted. Used primarily for testing
      *
      * @param pid      PID of the process whose threads are to be read.
-     * @param procPath where `proc` is mounted (to find, see {@code mount | grep ^proc})
      */
     @VisibleForTesting
-    public KernelSingleProcessCpuThreadReader(
-            int pid,
-            Path procPath) throws IOException {
+    public KernelSingleProcessCpuThreadReader(int pid,
+            @Nullable CpuTimeInStateReader cpuTimeInStateReader) throws IOException {
         mPid = pid;
-        mProcPath = procPath;
-        mProcTimeInStateReader = new ProcTimeInStateReader(
-                mProcPath.resolve(INITIAL_TIME_IN_STATE_PATH));
-        long jiffyHz = Os.sysconf(OsConstants._SC_CLK_TCK);
-        mJiffyMillis = 1000 / jiffyHz;
-        mProcessStatFilePath =
-                mProcPath.resolve(String.valueOf(mPid)).resolve(PROC_STAT_FILENAME).toString();
-        mThreadsDirectoryPath =
-                mProcPath.resolve(String.valueOf(mPid)).resolve(THREAD_CPU_STATS_DIRECTORY);
+        mCpuTimeInStateReader = cpuTimeInStateReader;
     }
 
     /**
@@ -142,7 +103,7 @@
     @Nullable
     public static KernelSingleProcessCpuThreadReader create(int pid) {
         try {
-            return new KernelSingleProcessCpuThreadReader(pid, DEFAULT_PROC_PATH);
+            return new KernelSingleProcessCpuThreadReader(pid, null);
         } catch (IOException e) {
             Slog.e(TAG, "Failed to initialize KernelSingleProcessCpuThreadReader", e);
             return null;
@@ -150,146 +111,98 @@
     }
 
     /**
-     * Get the CPU frequencies that correspond to the times reported in {@link
-     * ProcessCpuUsage#processCpuTimesMillis} etc.
+     * Starts tracking aggregated CPU time-in-state of all threads of the process with the PID
+     * supplied in the constructor.
+     */
+    public void startTrackingThreadCpuTimes() {
+        if (!mIsTracking) {
+            if (!startTrackingProcessCpuTimes(mPid, mCpuTimeInStateReader)) {
+                Slog.e(TAG, "Failed to start tracking process CPU times for " + mPid);
+            }
+            if (mSelectedThreadNativeTids.length > 0) {
+                if (!startAggregatingThreadCpuTimes(mSelectedThreadNativeTids,
+                        mCpuTimeInStateReader)) {
+                    Slog.e(TAG, "Failed to start tracking aggregated thread CPU times for "
+                            + Arrays.toString(mSelectedThreadNativeTids));
+                }
+            }
+            mIsTracking = true;
+        }
+    }
+
+    /**
+     * @param nativeTids an array of native Thread IDs whose CPU times should
+     *                   be aggregated as a group.  This is expected to be a subset
+     *                   of all thread IDs owned by the process.
+     */
+    public void setSelectedThreadIds(int[] nativeTids) {
+        mSelectedThreadNativeTids = nativeTids.clone();
+        if (mIsTracking) {
+            startAggregatingThreadCpuTimes(mSelectedThreadNativeTids, mCpuTimeInStateReader);
+        }
+    }
+
+    /**
+     * Get the CPU frequencies that correspond to the times reported in {@link ProcessCpuUsage}.
      */
     public int getCpuFrequencyCount() {
         if (mFrequencyCount == 0) {
-            mFrequencyCount = mProcTimeInStateReader.getFrequenciesKhz().length;
+            mFrequencyCount = getCpuFrequencyCount(mCpuTimeInStateReader);
         }
         return mFrequencyCount;
     }
 
     /**
-     * Get the total and per-thread CPU usage of the process with the PID specified in the
-     * constructor.
-     *
-     * @param selectedThreadIds a SORTED array of native Thread IDs whose CPU times should
-     *                          be aggregated as a group.  This is expected to be a subset
-     *                          of all thread IDs owned by the process.
+     * Get the total CPU usage of the process with the PID specified in the
+     * constructor. The CPU usage time is aggregated across all threads and may
+     * exceed the time the entire process has been running.
      */
     @Nullable
-    public ProcessCpuUsage getProcessCpuUsage(int[] selectedThreadIds) {
+    public ProcessCpuUsage getProcessCpuUsage() {
         if (DEBUG) {
-            Slog.d(TAG, "Reading CPU thread usages with directory " + mProcPath + " process ID "
-                    + mPid);
+            Slog.d(TAG, "Reading CPU thread usages for PID " + mPid);
         }
 
-        int cpuFrequencyCount = getCpuFrequencyCount();
-        ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(cpuFrequencyCount);
+        ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(getCpuFrequencyCount());
 
-        if (NATIVE_ENABLED) {
-            boolean result = readProcessCpuUsage(mProcPath.toString(), mPid,
-                    selectedThreadIds, processCpuUsage.processCpuTimesMillis,
-                    processCpuUsage.threadCpuTimesMillis,
-                    processCpuUsage.selectedThreadCpuTimesMillis);
-            if (!result) {
-                return null;
-            }
-            return processCpuUsage;
-        }
-
-        if (!isSorted(selectedThreadIds)) {
-            throw new IllegalArgumentException("selectedThreadIds is not sorted: "
-                    + Arrays.toString(selectedThreadIds));
-        }
-
-        if (!Process.readProcFile(mProcessStatFilePath, PROCESS_FULL_STATS_FORMAT, null,
-                mProcessFullStatsData, null)) {
-            Slog.e(TAG, "Failed to read process stat file " + mProcessStatFilePath);
+        boolean result = readProcessCpuUsage(mPid,
+                processCpuUsage.threadCpuTimesMillis,
+                processCpuUsage.selectedThreadCpuTimesMillis,
+                mCpuTimeInStateReader);
+        if (!result) {
             return null;
         }
 
-        long utime = mProcessFullStatsData[PROCESS_FULL_STAT_UTIME];
-        long stime = mProcessFullStatsData[PROCESS_FULL_STAT_STIME];
-
-        long processCpuTimeMillis = (utime + stime) * mJiffyMillis;
-
-        try (DirectoryStream<Path> threadPaths = Files.newDirectoryStream(mThreadsDirectoryPath)) {
-            for (Path threadDirectory : threadPaths) {
-                readThreadCpuUsage(processCpuUsage, selectedThreadIds, threadDirectory);
-            }
-        } catch (IOException | DirectoryIteratorException e) {
-            // Expected when a process finishes
-            return null;
-        }
-
-        // Estimate per cluster per frequency CPU time for the entire process
-        // by distributing the total process CPU time proportionately to how much
-        // CPU time its threads took on those clusters/frequencies.  This algorithm
-        // works more accurately when when we have equally distributed concurrency.
-        // TODO(b/169279846): obtain actual process CPU times from the kernel
-        long totalCpuTimeAllThreads = 0;
-        for (int i = cpuFrequencyCount - 1; i >= 0; i--) {
-            totalCpuTimeAllThreads += processCpuUsage.threadCpuTimesMillis[i];
-        }
-
-        for (int i = cpuFrequencyCount - 1; i >= 0; i--) {
-            processCpuUsage.processCpuTimesMillis[i] =
-                    processCpuTimeMillis * processCpuUsage.threadCpuTimesMillis[i]
-                            / totalCpuTimeAllThreads;
+        if (DEBUG) {
+            Slog.d(TAG, "threadCpuTimesMillis = "
+                    + Arrays.toString(processCpuUsage.threadCpuTimesMillis));
+            Slog.d(TAG, "selectedThreadCpuTimesMillis = "
+                    + Arrays.toString(processCpuUsage.selectedThreadCpuTimesMillis));
         }
 
         return processCpuUsage;
     }
 
-    /**
-     * Reads a thread's CPU usage and aggregates the per-cluster per-frequency CPU times.
-     *
-     * @param threadDirectory the {@code /proc} directory of the thread
-     */
-    private void readThreadCpuUsage(ProcessCpuUsage processCpuUsage, int[] selectedThreadIds,
-            Path threadDirectory) {
-        // Get the thread ID from the directory name
-        final int threadId;
-        try {
-            final String directoryName = threadDirectory.getFileName().toString();
-            threadId = Integer.parseInt(directoryName);
-        } catch (NumberFormatException e) {
-            Slog.w(TAG, "Failed to parse thread ID when iterating over /proc/*/task", e);
-            return;
-        }
-
-        // Get the CPU statistics from the directory
-        final Path threadCpuStatPath = threadDirectory.resolve(CPU_STATISTICS_FILENAME);
-        final long[] cpuUsages = mProcTimeInStateReader.getUsageTimesMillis(threadCpuStatPath);
-        if (cpuUsages == null) {
-            return;
-        }
-
-        final int cpuFrequencyCount = getCpuFrequencyCount();
-        final boolean isSelectedThread = Arrays.binarySearch(selectedThreadIds, threadId) >= 0;
-        for (int i = cpuFrequencyCount - 1; i >= 0; i--) {
-            processCpuUsage.threadCpuTimesMillis[i] += cpuUsages[i];
-            if (isSelectedThread) {
-                processCpuUsage.selectedThreadCpuTimesMillis[i] += cpuUsages[i];
-            }
-        }
-    }
-
     /** CPU usage of a process, all of its threads and a selected subset of its threads */
     public static class ProcessCpuUsage {
-        public long[] processCpuTimesMillis;
         public long[] threadCpuTimesMillis;
         public long[] selectedThreadCpuTimesMillis;
 
         public ProcessCpuUsage(int cpuFrequencyCount) {
-            processCpuTimesMillis = new long[cpuFrequencyCount];
             threadCpuTimesMillis = new long[cpuFrequencyCount];
             selectedThreadCpuTimesMillis = new long[cpuFrequencyCount];
         }
     }
 
-    private static boolean isSorted(int[] array) {
-        for (int i = 0; i < array.length - 1; i++) {
-            if (array[i] > array[i + 1]) {
-                return false;
-            }
-        }
-        return true;
-    }
+    private native int getCpuFrequencyCount(CpuTimeInStateReader reader);
 
-    private native boolean readProcessCpuUsage(String procPath, int pid, int[] selectedThreadIds,
-            long[] processCpuTimesMillis, long[] threadCpuTimesMillis,
-            long[] selectedThreadCpuTimesMillis);
+    private native boolean startTrackingProcessCpuTimes(int pid, CpuTimeInStateReader reader);
+
+    private native boolean startAggregatingThreadCpuTimes(int[] selectedThreadIds,
+            CpuTimeInStateReader reader);
+
+    private native boolean readProcessCpuUsage(int pid,
+            long[] threadCpuTimesMillis,
+            long[] selectedThreadCpuTimesMillis,
+            CpuTimeInStateReader reader);
 }
diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
index fbbee94..fbad75e 100644
--- a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
+++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
@@ -22,8 +22,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Arrays;
 
 /**
  * Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage
@@ -31,9 +29,7 @@
  */
 public class SystemServerCpuThreadReader {
     private final KernelSingleProcessCpuThreadReader mKernelCpuThreadReader;
-    private int[] mBinderThreadNativeTids = new int[0];  // Sorted
 
-    private long[] mLastProcessCpuTimeUs;
     private long[] mLastThreadCpuTimesUs;
     private long[] mLastBinderThreadCpuTimesUs;
 
@@ -41,8 +37,6 @@
      * Times (in microseconds) spent by the system server UID.
      */
     public static class SystemServiceCpuThreadTimes {
-        // The entire process
-        public long[] processCpuTimesUs;
         // All threads
         public long[] threadCpuTimesUs;
         // Just the threads handling incoming binder calls
@@ -61,8 +55,10 @@
     }
 
     @VisibleForTesting
-    public SystemServerCpuThreadReader(Path procPath, int pid) throws IOException {
-        this(new KernelSingleProcessCpuThreadReader(pid, procPath));
+    public SystemServerCpuThreadReader(int pid,
+            KernelSingleProcessCpuThreadReader.CpuTimeInStateReader cpuTimeInStateReader)
+            throws IOException {
+        this(new KernelSingleProcessCpuThreadReader(pid, cpuTimeInStateReader));
     }
 
     @VisibleForTesting
@@ -70,9 +66,15 @@
         mKernelCpuThreadReader = kernelCpuThreadReader;
     }
 
+    /**
+     * Start tracking CPU time-in-state for the process specified in the constructor.
+     */
+    public void startTrackingThreadCpuTime() {
+        mKernelCpuThreadReader.startTrackingThreadCpuTimes();
+    }
+
     public void setBinderThreadNativeTids(int[] nativeTids) {
-        mBinderThreadNativeTids = nativeTids.clone();
-        Arrays.sort(mBinderThreadNativeTids);
+        mKernelCpuThreadReader.setSelectedThreadIds(nativeTids);
     }
 
     /**
@@ -81,33 +83,27 @@
     @Nullable
     public SystemServiceCpuThreadTimes readDelta() {
         final int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount();
-        if (mLastProcessCpuTimeUs == null) {
-            mLastProcessCpuTimeUs = new long[numCpuFrequencies];
+        if (mLastThreadCpuTimesUs == null) {
             mLastThreadCpuTimesUs = new long[numCpuFrequencies];
             mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies];
 
-            mDeltaCpuThreadTimes.processCpuTimesUs = new long[numCpuFrequencies];
             mDeltaCpuThreadTimes.threadCpuTimesUs = new long[numCpuFrequencies];
             mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies];
         }
 
         final KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
-                mKernelCpuThreadReader.getProcessCpuUsage(mBinderThreadNativeTids);
+                mKernelCpuThreadReader.getProcessCpuUsage();
         if (processCpuUsage == null) {
             return null;
         }
 
         for (int i = numCpuFrequencies - 1; i >= 0; i--) {
-            long processCpuTimesUs = processCpuUsage.processCpuTimesMillis[i] * 1000;
             long threadCpuTimesUs = processCpuUsage.threadCpuTimesMillis[i] * 1000;
             long binderThreadCpuTimesUs = processCpuUsage.selectedThreadCpuTimesMillis[i] * 1000;
-            mDeltaCpuThreadTimes.processCpuTimesUs[i] =
-                    Math.max(0, processCpuTimesUs - mLastProcessCpuTimeUs[i]);
             mDeltaCpuThreadTimes.threadCpuTimesUs[i] =
                     Math.max(0, threadCpuTimesUs - mLastThreadCpuTimesUs[i]);
             mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] =
                     Math.max(0, binderThreadCpuTimesUs - mLastBinderThreadCpuTimesUs[i]);
-            mLastProcessCpuTimeUs[i] = processCpuTimesUs;
             mLastThreadCpuTimesUs[i] = threadCpuTimesUs;
             mLastBinderThreadCpuTimesUs[i] = binderThreadCpuTimesUs;
         }
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 1e9801f..30cd94c 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -106,7 +106,6 @@
     private static LatencyTracker sLatencyTracker;
 
     private final SparseLongArray mStartRtc = new SparseLongArray();
-    private final Context mContext;
     private volatile int mSamplingInterval;
     private volatile boolean mEnabled;
 
@@ -114,15 +113,14 @@
         if (sLatencyTracker == null) {
             synchronized (LatencyTracker.class) {
                 if (sLatencyTracker == null) {
-                    sLatencyTracker = new LatencyTracker(context);
+                    sLatencyTracker = new LatencyTracker();
                 }
             }
         }
         return sLatencyTracker;
     }
 
-    public LatencyTracker(Context context) {
-        mContext = context;
+    private LatencyTracker() {
         mEnabled = DEFAULT_ENABLED;
         mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
 
@@ -173,8 +171,8 @@
         }
     }
 
-    private String getTraceNameOfAcion(int action) {
-        return "L<" + getNameOfAction(action) + ">";
+    private static String getTraceNameOfAction(int action) {
+        return "L<" + getNameOfAction(STATSD_ACTION[action]) + ">";
     }
 
     public static boolean isEnabled(Context ctx) {
@@ -194,7 +192,7 @@
         if (!isEnabled()) {
             return;
         }
-        Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, getTraceNameOfAcion(action), 0);
+        Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, getTraceNameOfAction(action), 0);
         mStartRtc.put(action, SystemClock.elapsedRealtime());
     }
 
@@ -213,7 +211,7 @@
             return;
         }
         mStartRtc.delete(action);
-        Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, getTraceNameOfAcion(action), 0);
+        Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, getTraceNameOfAction(action), 0);
         logAction(action, (int) (endRtc - startRtc));
     }
 
@@ -236,7 +234,7 @@
      * @param writeToStatsLog Whether to write the measured latency to FrameworkStatsLog.
      */
     public static void logActionDeprecated(int action, int duration, boolean writeToStatsLog) {
-        Log.i(TAG, "action=" + action + " latency=" + duration);
+        Log.i(TAG, getNameOfAction(STATSD_ACTION[action]) + " latency=" + duration);
         EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration);
 
         if (writeToStatsLog) {
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index b42404f..892c5a5 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -41,22 +41,24 @@
             int untrustedDisplayId);
 
     // TODO: Use ParceledListSlice instead
-    void getInputMethodList(int userId, in IInputMethodInfoListResultCallback resultCallback);
-    // TODO: Use ParceledListSlice instead
-    void getEnabledInputMethodList(int userId,
+    oneway void getInputMethodList(int userId,
             in IInputMethodInfoListResultCallback resultCallback);
-    void getEnabledInputMethodSubtypeList(in String imiId, boolean allowsImplicitlySelectedSubtypes,
+    // TODO: Use ParceledListSlice instead
+    oneway void getEnabledInputMethodList(int userId,
+            in IInputMethodInfoListResultCallback resultCallback);
+    oneway void getEnabledInputMethodSubtypeList(in String imiId,
+            boolean allowsImplicitlySelectedSubtypes,
             in IInputMethodSubtypeListResultCallback resultCallback);
-    void getLastInputMethodSubtype(in IInputMethodSubtypeResultCallback resultCallback);
+    oneway void getLastInputMethodSubtype(in IInputMethodSubtypeResultCallback resultCallback);
 
-    void showSoftInput(in IInputMethodClient client, IBinder windowToken, int flags,
+    oneway void showSoftInput(in IInputMethodClient client, IBinder windowToken, int flags,
             in ResultReceiver resultReceiver, in IBooleanResultCallback resultCallback);
-    void hideSoftInput(in IInputMethodClient client, IBinder windowToken, int flags,
+    oneway void hideSoftInput(in IInputMethodClient client, IBinder windowToken, int flags,
             in ResultReceiver resultReceiver, in IBooleanResultCallback resultCallback);
     // If windowToken is null, this just does startInput().  Otherwise this reports that a window
     // has gained focus, and if 'attribute' is non-null then also does startInput.
     // @NonNull
-    void startInputOrWindowGainedFocus(
+    oneway void startInputOrWindowGainedFocus(
             /* @StartInputReason */ int startInputReason,
             in IInputMethodClient client, in IBinder windowToken,
             /* @StartInputFlags */ int startInputFlags,
@@ -66,29 +68,31 @@
             int unverifiedTargetSdkVersion,
             in IInputBindResultResultCallback inputBindResult);
 
-    void showInputMethodPickerFromClient(in IInputMethodClient client,
+    oneway void showInputMethodPickerFromClient(in IInputMethodClient client,
             int auxiliarySubtypeMode, in IVoidResultCallback resultCallback);
-    void showInputMethodPickerFromSystem(in IInputMethodClient client, int auxiliarySubtypeMode,
-            int displayId, in IVoidResultCallback resultCallback);
-    void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId,
+    oneway void showInputMethodPickerFromSystem(in IInputMethodClient client,
+            int auxiliarySubtypeMode, int displayId, in IVoidResultCallback resultCallback);
+    oneway void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client,
+            String topId, in IVoidResultCallback resultCallback);
+    oneway void isInputMethodPickerShownForTest(in IBooleanResultCallback resultCallback);
+    oneway void getCurrentInputMethodSubtype(in IInputMethodSubtypeResultCallback resultCallback);
+    oneway void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes,
             in IVoidResultCallback resultCallback);
-    void isInputMethodPickerShownForTest(in IBooleanResultCallback resultCallback);
-    void getCurrentInputMethodSubtype(in IInputMethodSubtypeResultCallback resultCallback);
-    void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
     // This is kept due to @UnsupportedAppUsage.
     // TODO(Bug 113914148): Consider removing this.
-    void getInputMethodWindowVisibleHeight(IIntResultCallback resultCallback);
+    oneway void getInputMethodWindowVisibleHeight(IIntResultCallback resultCallback);
 
-    void reportActivityView(in IInputMethodClient parentClient, int childDisplayId,
+    oneway void reportActivityView(in IInputMethodClient parentClient, int childDisplayId,
             in float[] matrixValues, in IVoidResultCallback resultCallback);
 
     oneway void reportPerceptible(in IBinder windowToken, boolean perceptible);
     /** Remove the IME surface. Requires INTERNAL_SYSTEM_WINDOW permission. */
-    void removeImeSurface();
+    oneway void removeImeSurface(in IVoidResultCallback resultCallback);
     /** Remove the IME surface. Requires passing the currently focused window. */
-    void removeImeSurfaceFromWindow(in IBinder windowToken);
+    oneway void removeImeSurfaceFromWindow(in IBinder windowToken,
+            in IVoidResultCallback resultCallback);
     void startProtoDump(in byte[] protoDump, int source, String where);
-    void isImeTraceEnabled(in IBooleanResultCallback resultCallback);
+    oneway void isImeTraceEnabled(in IBooleanResultCallback resultCallback);
 
     // Starts an ime trace.
     void startImeTrace();
diff --git a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
index 52bed6b..dfae684 100644
--- a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
+++ b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
@@ -26,239 +26,230 @@
 #include <android_runtime/Log.h>
 
 #include <nativehelper/ScopedPrimitiveArray.h>
-#include <nativehelper/ScopedUtfChars.h>
 
 namespace android {
 
+static constexpr uint16_t DEFAULT_THREAD_AGGREGATION_KEY = 0;
+static constexpr uint16_t SELECTED_THREAD_AGGREGATION_KEY = 1;
+
+static constexpr uint64_t NSEC_PER_MSEC = 1000000;
+
 // Number of milliseconds in a jiffy - the unit of time measurement for processes and threads
 static const uint32_t gJiffyMillis = (uint32_t)(1000 / sysconf(_SC_CLK_TCK));
 
-// Given a PID, returns a vector of all TIDs for the process' tasks. Thread IDs are
-// file names in the /proc/<pid>/task directory.
-static bool getThreadIds(const std::string &procPath, const pid_t pid,
-                         std::vector<pid_t> &outThreadIds) {
-    std::string taskPath = android::base::StringPrintf("%s/%u/task", procPath.c_str(), pid);
+// Abstract class for readers of CPU time-in-state. There are two implementations of
+// this class: BpfCpuTimeInStateReader and MockCpuTimeInStateReader.  The former is used
+// by the production code. The latter is used by unit tests to provide mock
+// CPU time-in-state data via a Java implementation.
+class ICpuTimeInStateReader {
+public:
+    virtual ~ICpuTimeInStateReader() {}
 
-    struct dirent **dirlist;
-    int threadCount = scandir(taskPath.c_str(), &dirlist, NULL, NULL);
-    if (threadCount == -1) {
-        ALOGE("Cannot read directory %s", taskPath.c_str());
-        return false;
-    }
+    // Returns the overall number of cluser-frequency combinations
+    virtual size_t getCpuFrequencyCount();
 
-    outThreadIds.reserve(threadCount);
+    // Marks the CPU time-in-state tracking for threads of the specified TGID
+    virtual bool startTrackingProcessCpuTimes(pid_t) = 0;
 
-    for (int i = 0; i < threadCount; i++) {
-        pid_t tid;
-        if (android::base::ParseInt<pid_t>(dirlist[i]->d_name, &tid)) {
-            outThreadIds.push_back(tid);
+    // Marks the thread specified by its PID for CPU time-in-state tracking.
+    virtual bool startAggregatingTaskCpuTimes(pid_t, uint16_t) = 0;
+
+    // Retrieves the accumulated time-in-state data, which is organized as a map
+    // from aggregation keys to vectors of vectors using the format:
+    // { aggKey0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...],
+    //   aggKey1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... }
+    // where ti_j_k is the ns tid i spent running on the jth cluster at the cluster's kth lowest
+    // freq.
+    virtual std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
+    getAggregatedTaskCpuFreqTimes(pid_t, const std::vector<uint16_t> &);
+};
+
+// ICpuTimeInStateReader that uses eBPF to provide a map of aggregated CPU time-in-state values.
+// See cputtimeinstate.h/.cpp
+class BpfCpuTimeInStateReader : public ICpuTimeInStateReader {
+public:
+    size_t getCpuFrequencyCount() {
+        std::optional<std::vector<std::vector<uint32_t>>> cpuFreqs = android::bpf::getCpuFreqs();
+        if (!cpuFreqs) {
+            ALOGE("Cannot obtain CPU frequency count");
+            return 0;
         }
-        free(dirlist[i]);
-    }
-    free(dirlist);
 
-    return true;
+        size_t freqCount = 0;
+        for (auto cluster : *cpuFreqs) {
+            freqCount += cluster.size();
+        }
+
+        return freqCount;
+    }
+
+    bool startTrackingProcessCpuTimes(pid_t tgid) {
+        return android::bpf::startTrackingProcessCpuTimes(tgid);
+    }
+
+    bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) {
+        return android::bpf::startAggregatingTaskCpuTimes(pid, aggregationKey);
+    }
+
+    std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
+    getAggregatedTaskCpuFreqTimes(pid_t pid, const std::vector<uint16_t> &aggregationKeys) {
+        return android::bpf::getAggregatedTaskCpuFreqTimes(pid, aggregationKeys);
+    }
+};
+
+// ICpuTimeInStateReader that uses JNI to provide a map of aggregated CPU time-in-state
+// values.
+// This version of CpuTimeInStateReader is used exclusively for providing mock data in tests.
+class MockCpuTimeInStateReader : public ICpuTimeInStateReader {
+private:
+    JNIEnv *mEnv;
+    jobject mCpuTimeInStateReader;
+
+public:
+    MockCpuTimeInStateReader(JNIEnv *env, jobject cpuTimeInStateReader)
+          : mEnv(env), mCpuTimeInStateReader(cpuTimeInStateReader) {}
+
+    size_t getCpuFrequencyCount();
+
+    bool startTrackingProcessCpuTimes(pid_t tgid);
+
+    bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey);
+
+    std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
+    getAggregatedTaskCpuFreqTimes(pid_t tgid, const std::vector<uint16_t> &aggregationKeys);
+};
+
+static ICpuTimeInStateReader *getCpuTimeInStateReader(JNIEnv *env,
+                                                      jobject cpuTimeInStateReaderObject) {
+    if (cpuTimeInStateReaderObject) {
+        return new MockCpuTimeInStateReader(env, cpuTimeInStateReaderObject);
+    } else {
+        return new BpfCpuTimeInStateReader();
+    }
 }
 
-// Reads contents of a time_in_state file and returns times as a vector of times per frequency
-// A time_in_state file contains pairs of frequency - time (in jiffies):
-//
-//    cpu0
-//    300000 30
-//    403200 0
-//    cpu4
-//    710400 10
-//    825600 20
-//    940800 30
-//
-static bool getThreadTimeInState(const std::string &procPath, const pid_t pid, const pid_t tid,
-                                 const size_t frequencyCount,
-                                 std::vector<uint64_t> &outThreadTimeInState) {
-    std::string timeInStateFilePath =
-            android::base::StringPrintf("%s/%u/task/%u/time_in_state", procPath.c_str(), pid, tid);
-    std::string data;
+static jint getCpuFrequencyCount(JNIEnv *env, jclass, jobject cpuTimeInStateReaderObject) {
+    std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
+            getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
+    return cpuTimeInStateReader->getCpuFrequencyCount();
+}
 
-    if (!android::base::ReadFileToString(timeInStateFilePath, &data)) {
-        ALOGE("Cannot read file: %s", timeInStateFilePath.c_str());
-        return false;
-    }
+static jboolean startTrackingProcessCpuTimes(JNIEnv *env, jclass, jint tgid,
+                                             jobject cpuTimeInStateReaderObject) {
+    std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
+            getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
+    return cpuTimeInStateReader->startTrackingProcessCpuTimes(tgid);
+}
 
-    auto lines = android::base::Split(data, "\n");
-    size_t index = 0;
-    for (const auto &line : lines) {
-        if (line.empty()) {
-            continue;
-        }
+static jboolean startAggregatingThreadCpuTimes(JNIEnv *env, jclass, jintArray selectedThreadIdArray,
+                                               jobject cpuTimeInStateReaderObject) {
+    ScopedIntArrayRO selectedThreadIds(env, selectedThreadIdArray);
+    std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
+            getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
 
-        auto numbers = android::base::Split(line, " ");
-        if (numbers.size() != 2) {
-            continue;
-        }
-        uint64_t timeInState;
-        if (!android::base::ParseUint<uint64_t>(numbers[1], &timeInState)) {
-            ALOGE("Invalid time_in_state file format: %s", timeInStateFilePath.c_str());
+    for (int i = 0; i < selectedThreadIds.size(); i++) {
+        if (!cpuTimeInStateReader->startAggregatingTaskCpuTimes(selectedThreadIds[i],
+                                                                SELECTED_THREAD_AGGREGATION_KEY)) {
             return false;
         }
-        if (index < frequencyCount) {
-            outThreadTimeInState[index] = timeInState;
-        }
-        index++;
     }
+    return true;
+}
 
+// Converts time-in-state data from a vector of vectors to a flat array.
+// Also converts from nanoseconds to milliseconds.
+static bool flattenTimeInStateData(ScopedLongArrayRW &cpuTimesMillis,
+                                   const std::vector<std::vector<uint64_t>> &data) {
+    size_t frequencyCount = cpuTimesMillis.size();
+    size_t index = 0;
+    for (const auto &cluster : data) {
+        for (const uint64_t &timeNanos : cluster) {
+            if (index < frequencyCount) {
+                cpuTimesMillis[index] = timeNanos / NSEC_PER_MSEC;
+            }
+            index++;
+        }
+    }
     if (index != frequencyCount) {
-        ALOGE("Incorrect number of frequencies %u in %s. Expected %u",
-              (uint32_t)outThreadTimeInState.size(), timeInStateFilePath.c_str(),
-              (uint32_t)frequencyCount);
+        ALOGE("CPU time-in-state reader returned data for %zu frequencies; expected: %zu", index,
+              frequencyCount);
         return false;
     }
 
     return true;
 }
 
-static int pidCompare(const void *a, const void *b) {
-    return (*(pid_t *)a - *(pid_t *)b);
-}
-
-static inline bool isSelectedThread(const pid_t tid, const pid_t *selectedThreadIds,
-                                    const size_t selectedThreadCount) {
-    return bsearch(&tid, selectedThreadIds, selectedThreadCount, sizeof(pid_t), pidCompare) != NULL;
-}
-
-// Reads all /proc/<pid>/task/*/time_in_state files and aggregates per-frequency
+// Reads all CPU time-in-state data accumulated by BPF and aggregates per-frequency
 // time in state data for all threads.  Also, separately aggregates time in state for
 // selected threads whose TIDs are passes as selectedThreadIds.
-static void aggregateThreadCpuTimes(const std::string &procPath, const pid_t pid,
-                                    const std::vector<pid_t> &threadIds,
-                                    const size_t frequencyCount, const pid_t *selectedThreadIds,
-                                    const size_t selectedThreadCount,
-                                    uint64_t *threadCpuTimesMillis,
-                                    uint64_t *selectedThreadCpuTimesMillis) {
-    for (size_t j = 0; j < frequencyCount; j++) {
-        threadCpuTimesMillis[j] = 0;
-        selectedThreadCpuTimesMillis[j] = 0;
-    }
-
-    for (size_t i = 0; i < threadIds.size(); i++) {
-        pid_t tid = threadIds[i];
-        std::vector<uint64_t> timeInState(frequencyCount);
-        if (!getThreadTimeInState(procPath, pid, tid, frequencyCount, timeInState)) {
-            continue;
-        }
-
-        bool selectedThread = isSelectedThread(tid, selectedThreadIds, selectedThreadCount);
-        for (size_t j = 0; j < frequencyCount; j++) {
-            threadCpuTimesMillis[j] += timeInState[j];
-            if (selectedThread) {
-                selectedThreadCpuTimesMillis[j] += timeInState[j];
-            }
-        }
-    }
-    for (size_t i = 0; i < frequencyCount; i++) {
-        threadCpuTimesMillis[i] *= gJiffyMillis;
-        selectedThreadCpuTimesMillis[i] *= gJiffyMillis;
-    }
-}
-
-// Reads process utime and stime from the /proc/<pid>/stat file.
-// Format of this file is described in https://man7.org/linux/man-pages/man5/proc.5.html.
-static bool getProcessCpuTime(const std::string &procPath, const pid_t pid,
-                              uint64_t &outTimeMillis) {
-    std::string statFilePath = android::base::StringPrintf("%s/%u/stat", procPath.c_str(), pid);
-    std::string data;
-    if (!android::base::ReadFileToString(statFilePath, &data)) {
-        return false;
-    }
-
-    auto fields = android::base::Split(data, " ");
-    uint64_t utime, stime;
-
-    // Field 14 (counting from 1) is utime - process time in user space, in jiffies
-    // Field 15 (counting from 1) is stime - process time in system space, in jiffies
-    if (fields.size() < 15 || !android::base::ParseUint(fields[13], &utime) ||
-        !android::base::ParseUint(fields[14], &stime)) {
-        ALOGE("Invalid file format %s", statFilePath.c_str());
-        return false;
-    }
-
-    outTimeMillis = (utime + stime) * gJiffyMillis;
-    return true;
-}
-
-// Estimates per cluster per frequency CPU time for the entire process
-// by distributing the total process CPU time proportionately to how much
-// CPU time its threads took on those clusters/frequencies.  This algorithm
-// works more accurately when when we have equally distributed concurrency.
-// TODO(b/169279846): obtain actual process CPU times from the kernel
-static void estimateProcessTimeInState(const uint64_t processCpuTimeMillis,
-                                       const uint64_t *threadCpuTimesMillis,
-                                       const size_t frequencyCount,
-                                       uint64_t *processCpuTimesMillis) {
-    uint64_t totalCpuTimeAllThreads = 0;
-    for (size_t i = 0; i < frequencyCount; i++) {
-        totalCpuTimeAllThreads += threadCpuTimesMillis[i];
-    }
-
-    if (totalCpuTimeAllThreads != 0) {
-        for (size_t i = 0; i < frequencyCount; i++) {
-            processCpuTimesMillis[i] =
-                    processCpuTimeMillis * threadCpuTimesMillis[i] / totalCpuTimeAllThreads;
-        }
-    } else {
-        for (size_t i = 0; i < frequencyCount; i++) {
-            processCpuTimesMillis[i] = 0;
-        }
-    }
-}
-
-static jboolean readProcessCpuUsage(JNIEnv *env, jclass, jstring procPath, jint pid,
-                                    jintArray selectedThreadIdArray,
-                                    jlongArray processCpuTimesMillisArray,
+static jboolean readProcessCpuUsage(JNIEnv *env, jclass, jint pid,
                                     jlongArray threadCpuTimesMillisArray,
-                                    jlongArray selectedThreadCpuTimesMillisArray) {
-    ScopedUtfChars procPathChars(env, procPath);
-    ScopedIntArrayRO selectedThreadIds(env, selectedThreadIdArray);
-    ScopedLongArrayRW processCpuTimesMillis(env, processCpuTimesMillisArray);
+                                    jlongArray selectedThreadCpuTimesMillisArray,
+                                    jobject cpuTimeInStateReaderObject) {
     ScopedLongArrayRW threadCpuTimesMillis(env, threadCpuTimesMillisArray);
     ScopedLongArrayRW selectedThreadCpuTimesMillis(env, selectedThreadCpuTimesMillisArray);
+    std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
+            getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
 
-    std::string procPathStr(procPathChars.c_str());
-
-    // Get all thread IDs for the process.
-    std::vector<pid_t> threadIds;
-    if (!getThreadIds(procPathStr, pid, threadIds)) {
-        ALOGE("Could not obtain thread IDs from: %s", procPathStr.c_str());
-        return false;
-    }
-
-    size_t frequencyCount = processCpuTimesMillis.size();
+    const size_t frequencyCount = cpuTimeInStateReader->getCpuFrequencyCount();
 
     if (threadCpuTimesMillis.size() != frequencyCount) {
-        ALOGE("Invalid array length: threadCpuTimesMillis");
+        ALOGE("Invalid threadCpuTimesMillis array length: %zu frequencies; expected: %zu",
+              threadCpuTimesMillis.size(), frequencyCount);
         return false;
     }
+
     if (selectedThreadCpuTimesMillis.size() != frequencyCount) {
-        ALOGE("Invalid array length: selectedThreadCpuTimesMillisArray");
+        ALOGE("Invalid selectedThreadCpuTimesMillis array length: %zu frequencies; expected: %zu",
+              selectedThreadCpuTimesMillis.size(), frequencyCount);
         return false;
     }
 
-    aggregateThreadCpuTimes(procPathStr, pid, threadIds, frequencyCount, selectedThreadIds.get(),
-                            selectedThreadIds.size(),
-                            reinterpret_cast<uint64_t *>(threadCpuTimesMillis.get()),
-                            reinterpret_cast<uint64_t *>(selectedThreadCpuTimesMillis.get()));
-
-    uint64_t processCpuTime;
-    bool ret = getProcessCpuTime(procPathStr, pid, processCpuTime);
-    if (ret) {
-        estimateProcessTimeInState(processCpuTime,
-                                   reinterpret_cast<uint64_t *>(threadCpuTimesMillis.get()),
-                                   frequencyCount,
-                                   reinterpret_cast<uint64_t *>(processCpuTimesMillis.get()));
+    for (size_t i = 0; i < frequencyCount; i++) {
+        threadCpuTimesMillis[i] = 0;
+        selectedThreadCpuTimesMillis[i] = 0;
     }
-    return ret;
+
+    std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> data =
+            cpuTimeInStateReader->getAggregatedTaskCpuFreqTimes(pid,
+                                                                {DEFAULT_THREAD_AGGREGATION_KEY,
+                                                                 SELECTED_THREAD_AGGREGATION_KEY});
+    if (!data) {
+        ALOGE("Cannot read thread CPU times for PID %d", pid);
+        return false;
+    }
+
+    if (!flattenTimeInStateData(threadCpuTimesMillis, (*data)[DEFAULT_THREAD_AGGREGATION_KEY])) {
+        return false;
+    }
+
+    if (!flattenTimeInStateData(selectedThreadCpuTimesMillis,
+                                (*data)[SELECTED_THREAD_AGGREGATION_KEY])) {
+        return false;
+    }
+
+    // threadCpuTimesMillis returns CPU times for _all_ threads, including the selected ones
+    for (size_t i = 0; i < frequencyCount; i++) {
+        threadCpuTimesMillis[i] += selectedThreadCpuTimesMillis[i];
+    }
+
+    return true;
 }
 
 static const JNINativeMethod g_single_methods[] = {
-        {"readProcessCpuUsage", "(Ljava/lang/String;I[I[J[J[J)Z", (void *)readProcessCpuUsage},
+        {"getCpuFrequencyCount",
+         "(Lcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)I",
+         (void *)getCpuFrequencyCount},
+        {"startTrackingProcessCpuTimes",
+         "(ILcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z",
+         (void *)startTrackingProcessCpuTimes},
+        {"startAggregatingThreadCpuTimes",
+         "([ILcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z",
+         (void *)startAggregatingThreadCpuTimes},
+        {"readProcessCpuUsage",
+         "(I[J[J"
+         "Lcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z",
+         (void *)readProcessCpuUsage},
 };
 
 int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv *env) {
@@ -266,4 +257,77 @@
                                 g_single_methods, NELEM(g_single_methods));
 }
 
+size_t MockCpuTimeInStateReader::getCpuFrequencyCount() {
+    jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader);
+    jmethodID mid = mEnv->GetMethodID(cls, "getCpuFrequencyCount", "()I");
+    if (mid == 0) {
+        ALOGE("Couldn't find the method getCpuFrequencyCount");
+        return false;
+    }
+    return (size_t)mEnv->CallIntMethod(mCpuTimeInStateReader, mid);
+}
+
+bool MockCpuTimeInStateReader::startTrackingProcessCpuTimes(pid_t tgid) {
+    jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader);
+    jmethodID mid = mEnv->GetMethodID(cls, "startTrackingProcessCpuTimes", "(I)Z");
+    if (mid == 0) {
+        ALOGE("Couldn't find the method startTrackingProcessCpuTimes");
+        return false;
+    }
+    return mEnv->CallBooleanMethod(mCpuTimeInStateReader, mid, tgid);
+}
+
+bool MockCpuTimeInStateReader::startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) {
+    jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader);
+    jmethodID mid = mEnv->GetMethodID(cls, "startAggregatingTaskCpuTimes", "(II)Z");
+    if (mid == 0) {
+        ALOGE("Couldn't find the method startAggregatingTaskCpuTimes");
+        return false;
+    }
+    return mEnv->CallBooleanMethod(mCpuTimeInStateReader, mid, pid, aggregationKey);
+}
+
+std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
+MockCpuTimeInStateReader::getAggregatedTaskCpuFreqTimes(
+        pid_t pid, const std::vector<uint16_t> &aggregationKeys) {
+    jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader);
+    jmethodID mid =
+            mEnv->GetMethodID(cls, "getAggregatedTaskCpuFreqTimes", "(I)[Ljava/lang/String;");
+    if (mid == 0) {
+        ALOGE("Couldn't find the method getAggregatedTaskCpuFreqTimes");
+        return {};
+    }
+
+    std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>> map;
+
+    jobjectArray stringArray =
+            (jobjectArray)mEnv->CallObjectMethod(mCpuTimeInStateReader, mid, pid);
+    int size = mEnv->GetArrayLength(stringArray);
+    for (int i = 0; i < size; i++) {
+        ScopedUtfChars line(mEnv, (jstring)mEnv->GetObjectArrayElement(stringArray, i));
+        uint16_t aggregationKey;
+        std::vector<std::vector<uint64_t>> times;
+
+        // Each string is formatted like this: "aggKey:t0_0 t0_1...:t1_0 t1_1..."
+        auto fields = android::base::Split(line.c_str(), ":");
+        android::base::ParseUint(fields[0], &aggregationKey);
+
+        for (int j = 1; j < fields.size(); j++) {
+            auto numbers = android::base::Split(fields[j], " ");
+
+            std::vector<uint64_t> chunk;
+            for (int k = 0; k < numbers.size(); k++) {
+                uint64_t time;
+                android::base::ParseUint(numbers[k], &time);
+                chunk.emplace_back(time);
+            }
+            times.emplace_back(chunk);
+        }
+
+        map.emplace(aggregationKey, times);
+    }
+
+    return map;
+}
+
 } // namespace android
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c359a7d..c882431 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -307,6 +307,7 @@
     optional bool animating_bounds = 26 [deprecated = true];
     optional float minimize_amount = 27;
     optional bool created_by_organizer = 28;
+    optional string affinity = 29;
 }
 
 /* represents ActivityRecordProto */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3975529..396f954 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1744,6 +1744,12 @@
     <permission android:name="android.permission.REQUEST_NETWORK_SCORES"
         android:protectionLevel="signature|setup" />
 
+    <!-- Allows applications to restart the Wi-Fi subsystem.
+     @SystemApi
+     <p>Not for use by third-party applications. @hide -->
+    <permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM"
+                android:protectionLevel="signature|setup" />
+
     <!-- @SystemApi @hide Allows applications to toggle airplane mode.
          <p>Not for use by third-party or privileged applications.
     -->
@@ -5906,7 +5912,7 @@
 
         <!-- AOSP configures a default secondary LocationTimeZoneProvider that uses an on-device
              data set from the com.android.geotz APEX. -->
-        <service android:name="com.android.timezone.geotz.provider.OfflineLocationTimeZoneProviderService"
+        <service android:name="com.android.timezone.location.provider.OfflineLocationTimeZoneProviderService"
                  android:enabled="@bool/config_enableSecondaryLocationTimeZoneProvider"
                  android:permission="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE"
                  android:exported="false">
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 243c244..415a0a2 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8288,6 +8288,23 @@
     </declare-styleable>
 
     <!-- =============================== -->
+    <!-- System Speech Recognition attributes -->
+    <!-- =============================== -->
+    <eat-comment />
+
+    <!-- Use <code>on-device-recognition-service</code> as the root tag of the XML resource that
+         describes a {@link android.service.speech.RecognitionService}, which is referenced
+         from its {@link android.service.speech.RecognitionService#SERVICE_META_DATA} meta-data
+         entry.
+         @hide @SystemApi
+    -->
+    <declare-styleable name="OnDeviceRecognitionService">
+        <!-- Fully qualified class name of an activity that allows the user to modify
+             the settings for this service. -->
+        <attr name="settingsActivity" />
+    </declare-styleable>
+
+    <!-- =============================== -->
     <!-- Content Capture attributes -->
     <!-- =============================== -->
     <eat-comment />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4e272f1..a928408 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3800,6 +3800,12 @@
 -->
     <string name="config_defaultSearchUiService" translatable="false"></string>
 
+    <!-- The package name for the system's speech recognition service.
+         This service must be trusted, as it can be activated without explicit consent of the user.
+         Example: "com.android.speech/.RecognitionService"
+    -->
+    <string name="config_defaultOnDeviceSpeechRecognitionService" translatable="false"></string>
+
     <string name="config_defaultMusicRecognitionService" translatable="false"></string>
 
     <!-- The package name for the default retail demo app.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e92eb97..bcef680 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3489,6 +3489,7 @@
   <java-symbol type="string" name="notification_channel_do_not_disturb" />
   <java-symbol type="string" name="notification_channel_accessibility_magnification" />
   <java-symbol type="string" name="config_defaultAutofillService" />
+  <java-symbol type="string" name="config_defaultOnDeviceSpeechRecognitionService" />
   <java-symbol type="string" name="config_defaultTextClassifierPackage" />
   <java-symbol type="string" name="config_defaultWellbeingPackage" />
   <java-symbol type="string" name="config_defaultContentCaptureService" />
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index 65ea2a8..3df2e90 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -171,7 +171,8 @@
         FontConfig fontConfig;
         try {
             fontConfig = FontListParser.parse(
-                    TEST_FONTS_XML, TEST_FONT_DIR, oemXmlPath, TEST_OEM_DIR, updatableFontMap);
+                    TEST_FONTS_XML, TEST_FONT_DIR, oemXmlPath, TEST_OEM_DIR, updatableFontMap, 0,
+                    0);
         } catch (IOException | XmlPullParserException e) {
             throw new RuntimeException(e);
         }
@@ -199,7 +200,7 @@
         FontConfig fontConfig;
         try {
             fontConfig = FontListParser.parse(
-                    SYSTEM_FONTS_XML, SYSTEM_FONT_DIR, null, TEST_OEM_DIR, null);
+                    SYSTEM_FONTS_XML, SYSTEM_FONT_DIR, null, TEST_OEM_DIR, null, 0, 0);
         } catch (IOException | XmlPullParserException e) {
             throw new RuntimeException(e);
         }
diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java
index e301037..90a6ca3 100644
--- a/core/tests/coretests/src/android/text/FontFallbackSetup.java
+++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java
@@ -79,7 +79,7 @@
 
         FontConfig fontConfig;
         try {
-            fontConfig = FontListParser.parse(testFontsXml, mTestFontsDir, null, null, null);
+            fontConfig = FontListParser.parse(testFontsXml, mTestFontsDir, null, null, null, 0, 0);
         } catch (IOException | XmlPullParserException e) {
             throw new RuntimeException(e);
         }
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
index b5720a2..2de800b 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
@@ -19,122 +19,87 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.os.FileUtils;
-
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.File;
 import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class KernelSingleProcessCpuThreadReaderTest {
 
-    private File mProcDirectory;
-
-    @Before
-    public void setUp() {
-        Context context = InstrumentationRegistry.getContext();
-        mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        FileUtils.deleteContents(mProcDirectory);
-    }
-
     @Test
     public void getProcessCpuUsage() throws IOException {
-        setupDirectory(42,
-                new int[] {42, 1, 2, 3},
-                new int[] {1000, 2000},
-                // Units are 10ms aka 10000Us
-                new int[][] {{100, 200}, {0, 200}, {100, 300}, {0, 600}},
-                new int[] {4500, 500});
+        // Units are nanoseconds
+        MockCpuTimeInStateReader mockReader = new MockCpuTimeInStateReader(4, new String[] {
+                "0:1000000000 2000000000 3000000000:4000000000",
+                "1:100000000 200000000 300000000:400000000",
+        });
 
         KernelSingleProcessCpuThreadReader reader = new KernelSingleProcessCpuThreadReader(42,
-                mProcDirectory.toPath());
+                mockReader);
+        reader.setSelectedThreadIds(new int[] {2, 3});
+        reader.startTrackingThreadCpuTimes();
         KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
-                reader.getProcessCpuUsage(new int[] {2, 3});
-        assertThat(processCpuUsage.threadCpuTimesMillis).isEqualTo(new long[] {2000, 13000});
-        assertThat(processCpuUsage.selectedThreadCpuTimesMillis).isEqualTo(new long[] {1000, 9000});
-        assertThat(processCpuUsage.processCpuTimesMillis).isEqualTo(new long[] {6666, 43333});
+                reader.getProcessCpuUsage();
+        assertThat(mockReader.mTrackedTgid).isEqualTo(42);
+        // The strings are formatted as <TID TGID AGG_KEY>, where AGG_KEY is 1 for binder
+        // threads and 0 for all other threads.
+        assertThat(mockReader.mTrackedTasks).containsExactly(
+                "2 1",
+                "3 1");
+        assertThat(processCpuUsage.threadCpuTimesMillis).isEqualTo(
+                new long[] {1100, 2200, 3300, 4400});
+        assertThat(processCpuUsage.selectedThreadCpuTimesMillis).isEqualTo(
+                new long[] {100, 200, 300, 400});
     }
 
     @Test
     public void getCpuFrequencyCount() throws IOException {
-        setupDirectory(13,
-                new int[] {13},
-                new int[] {1000, 2000, 3000},
-                new int[][] {{100, 200, 300}},
-                new int[] {14, 15});
+        MockCpuTimeInStateReader mockReader = new MockCpuTimeInStateReader(3, new String[0]);
 
         KernelSingleProcessCpuThreadReader reader = new KernelSingleProcessCpuThreadReader(13,
-                mProcDirectory.toPath());
+                mockReader);
         int cpuFrequencyCount = reader.getCpuFrequencyCount();
         assertThat(cpuFrequencyCount).isEqualTo(3);
     }
 
-    private void setupDirectory(int pid, int[] threadIds, int[] cpuFrequencies,
-            int[][] threadCpuTimes, int[] processCpuTimes)
-            throws IOException {
+    public static class MockCpuTimeInStateReader implements
+            KernelSingleProcessCpuThreadReader.CpuTimeInStateReader {
+        private final int mCpuFrequencyCount;
+        private final String[] mAggregatedTaskCpuFreqTimes;
+        public int mTrackedTgid;
+        public List<String> mTrackedTasks = new ArrayList<>();
 
-        assertTrue(mProcDirectory.toPath().resolve("self").toFile().mkdirs());
-
-        try (OutputStream timeInStateStream =
-                     Files.newOutputStream(
-                             mProcDirectory.toPath().resolve("self").resolve("time_in_state"))) {
-            for (int i = 0; i < cpuFrequencies.length; i++) {
-                final String line = cpuFrequencies[i] + " 0\n";
-                timeInStateStream.write(line.getBytes());
-            }
+        public MockCpuTimeInStateReader(int cpuFrequencyCount,
+                String[] aggregatedTaskCpuFreqTimes) {
+            mCpuFrequencyCount = cpuFrequencyCount;
+            mAggregatedTaskCpuFreqTimes = aggregatedTaskCpuFreqTimes;
         }
 
-        Path processPath = mProcDirectory.toPath().resolve(String.valueOf(pid));
-
-        // Make /proc/$PID
-        assertTrue(processPath.toFile().mkdirs());
-
-        // Write /proc/$PID/stat. Only the fields 14-17 matter.
-        try (OutputStream timeInStateStream = Files.newOutputStream(processPath.resolve("stat"))) {
-            timeInStateStream.write(
-                    (pid + " (test) S 4 5 6 7 8 9 10 11 12 13 "
-                            + processCpuTimes[0] + " "
-                            + processCpuTimes[1] + " "
-                            + "16 17 18 19 20 ...").getBytes());
+        @Override
+        public int getCpuFrequencyCount() {
+            return mCpuFrequencyCount;
         }
 
-        // Make /proc/$PID/task
-        final Path selfThreadsPath = processPath.resolve("task");
-        assertTrue(selfThreadsPath.toFile().mkdirs());
+        @Override
+        public boolean startTrackingProcessCpuTimes(int tgid) {
+            mTrackedTgid = tgid;
+            return true;
+        }
 
-        // Make thread directories
-        for (int i = 0; i < threadIds.length; i++) {
-            // Make /proc/$PID/task/$TID
-            final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
-            assertTrue(threadPath.toFile().mkdirs());
+        public boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey) {
+            mTrackedTasks.add(pid + " " + aggregationKey);
+            return true;
+        }
 
-            // Make /proc/$PID/task/$TID/time_in_state
-            try (OutputStream timeInStateStream =
-                         Files.newOutputStream(threadPath.resolve("time_in_state"))) {
-                for (int j = 0; j < cpuFrequencies.length; j++) {
-                    final String line = cpuFrequencies[j] + " " + threadCpuTimes[i][j] + "\n";
-                    timeInStateStream.write(line.getBytes());
-                }
-            }
+        public String[] getAggregatedTaskCpuFreqTimes(int pid) {
+            return mAggregatedTaskCpuFreqTimes;
         }
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
index 4230066..66a8379 100644
--- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
@@ -21,7 +21,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
 import android.net.NetworkStats;
 import android.os.BatteryConsumer;
 import android.os.Process;
@@ -78,7 +78,8 @@
                 8_000_000_000L, APP_UID, 8000, 8000);
 
         // Note established network
-        stats.noteNetworkInterfaceType("cellular", ConnectivityManager.TYPE_MOBILE);
+        stats.noteNetworkInterfaceForTransports("cellular",
+                new int[] { NetworkCapabilities.TRANSPORT_CELLULAR });
 
         // Note application network activity
         NetworkStats networkStats = new NetworkStats(10000, 1)
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
index 121c637..d116d4d 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
@@ -16,146 +16,86 @@
 
 package com.android.internal.os;
 
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
 
-import android.content.Context;
-import android.os.FileUtils;
-
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.After;
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.File;
 import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class SystemServerCpuThreadReaderTest {
-    private File mProcDirectory;
-
-    @Before
-    public void setUp() {
-        Context context = InstrumentationRegistry.getContext();
-        mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        FileUtils.deleteContents(mProcDirectory);
-    }
 
     @Test
-    public void testReaderDelta_firstTime() throws IOException {
+    public void testReadDelta() throws IOException {
         int pid = 42;
-        setupDirectory(
-                pid,
-                new int[] {42, 1, 2, 3},
-                new int[] {1000, 2000},
-                // Units are 10ms aka 10000Us
-                new int[][] {{100, 200}, {0, 200}, {0, 300}, {0, 400}},
-                new int[] {1400, 1500});
 
-        SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(
-                mProcDirectory.toPath(), pid);
-        reader.setBinderThreadNativeTids(new int[] {1, 3});
-        SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
-                reader.readDelta();
-        assertArrayEquals(new long[] {100 * 10000, 1100 * 10000},
-                systemServiceCpuThreadTimes.threadCpuTimesUs);
-        assertArrayEquals(new long[] {0, 600 * 10000},
-                systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
-    }
+        MockCpuTimeInStateReader mockReader = new MockCpuTimeInStateReader(4);
+        // Units are nanoseconds
+        mockReader.setAggregatedTaskCpuFreqTimes(new String[] {
+                "0:1000000000 2000000000 3000000000:4000000000",
+                "1:100000000 200000000 300000000:400000000",
+        });
 
-    @Test
-    public void testReaderDelta_nextTime() throws IOException {
-        int pid = 42;
-        setupDirectory(
-                pid,
-                new int[] {42, 1, 2, 3},
-                new int[] {1000, 2000},
-                new int[][] {{100, 200}, {0, 200}, {0, 300}, {0, 400}},
-                new int[] {1400, 1500});
-
-        SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(
-                mProcDirectory.toPath(), pid);
+        SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(pid, mockReader);
         reader.setBinderThreadNativeTids(new int[] {1, 3});
 
-        // First time, populate "last" snapshot
-        reader.readDelta();
-
-        FileUtils.deleteContents(mProcDirectory);
-        setupDirectory(
-                pid,
-                new int[] {42, 1, 2, 3},
-                new int[] {1000, 2000},
-                new int[][] {{500, 600}, {700, 800}, {900, 1000}, {1100, 1200}},
-                new int[] {2400, 2500});
-
-        // Second time, get the actual delta
+        // The first invocation of readDelta populates the "last" snapshot
         SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
                 reader.readDelta();
 
-        assertArrayEquals(new long[] {3100 * 10000, 2500 * 10000},
-                systemServiceCpuThreadTimes.threadCpuTimesUs);
-        assertArrayEquals(new long[] {1800 * 10000, 1400 * 10000},
-                systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
+        assertThat(systemServiceCpuThreadTimes.threadCpuTimesUs)
+                .isEqualTo(new long[] {1100000, 2200000, 3300000, 4400000});
+        assertThat(systemServiceCpuThreadTimes.binderThreadCpuTimesUs)
+                .isEqualTo(new long[] {100000, 200000, 300000, 400000});
+
+        mockReader.setAggregatedTaskCpuFreqTimes(new String[] {
+                "0:1010000000 2020000000 3030000000:4040000000",
+                "1:101000000 202000000 303000000:404000000",
+        });
+
+        // The second invocation gets the actual delta
+        systemServiceCpuThreadTimes = reader.readDelta();
+
+        assertThat(systemServiceCpuThreadTimes.threadCpuTimesUs)
+                .isEqualTo(new long[] {11000, 22000, 33000, 44000});
+        assertThat(systemServiceCpuThreadTimes.binderThreadCpuTimesUs)
+                .isEqualTo(new long[] {1000, 2000, 3000, 4000});
     }
 
-    private void setupDirectory(int pid, int[] threadIds, int[] cpuFrequencies, int[][] cpuTimes,
-            int[] processCpuTimes)
-            throws IOException {
+    public static class MockCpuTimeInStateReader implements
+            KernelSingleProcessCpuThreadReader.CpuTimeInStateReader {
+        private final int mCpuFrequencyCount;
+        private String[] mAggregatedTaskCpuFreqTimes;
 
-        assertTrue(mProcDirectory.toPath().resolve("self").toFile().mkdirs());
-
-        try (OutputStream timeInStateStream =
-                     Files.newOutputStream(
-                             mProcDirectory.toPath().resolve("self").resolve("time_in_state"))) {
-            for (int i = 0; i < cpuFrequencies.length; i++) {
-                final String line = cpuFrequencies[i] + " 0\n";
-                timeInStateStream.write(line.getBytes());
-            }
+        MockCpuTimeInStateReader(int frequencyCount) {
+            mCpuFrequencyCount = frequencyCount;
         }
 
-        Path processPath = mProcDirectory.toPath().resolve(String.valueOf(pid));
-        // Make /proc/$PID
-        assertTrue(processPath.toFile().mkdirs());
-
-        // Write /proc/$PID/stat. Only the fields 14-17 matter.
-        try (OutputStream timeInStateStream = Files.newOutputStream(processPath.resolve("stat"))) {
-            timeInStateStream.write(
-                    (pid + " (test) S 4 5 6 7 8 9 10 11 12 13 "
-                            + processCpuTimes[0] + " "
-                            + processCpuTimes[1] + " "
-                            + "16 17 18 19 20 ...").getBytes());
+        @Override
+        public int getCpuFrequencyCount() {
+            return mCpuFrequencyCount;
         }
 
-        // Make /proc/$PID/task
-        final Path selfThreadsPath = processPath.resolve("task");
-        assertTrue(selfThreadsPath.toFile().mkdirs());
+        @Override
+        public boolean startTrackingProcessCpuTimes(int tgid) {
+            return true;
+        }
 
-        // Make thread directories
-        for (int i = 0; i < threadIds.length; i++) {
-            // Make /proc/$PID/task/$TID
-            final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
-            assertTrue(threadPath.toFile().mkdirs());
+        public boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey) {
+            return true;
+        }
 
-            // Make /proc/$PID/task/$TID/time_in_state
-            try (OutputStream timeInStateStream =
-                         Files.newOutputStream(threadPath.resolve("time_in_state"))) {
-                for (int j = 0; j < cpuFrequencies.length; j++) {
-                    final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n";
-                    timeInStateStream.write(line.getBytes());
-                }
-            }
+        public void setAggregatedTaskCpuFreqTimes(String[] mAggregatedTaskCpuFreqTimes) {
+            this.mAggregatedTaskCpuFreqTimes = mAggregatedTaskCpuFreqTimes;
+        }
+
+        public String[] getAggregatedTaskCpuFreqTimes(int pid) {
+            return mAggregatedTaskCpuFreqTimes;
         }
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
index a5cafb9..24741fe 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -62,7 +62,6 @@
     public void testCalculateApp() {
         // Test Power Profile has two CPU clusters with 3 and 4 speeds, thus 7 freq times total
         mMockSystemServerCpuThreadReader.setCpuTimes(
-                new long[] {10000, 15000, 20000, 25000, 30000, 35000, 40000},
                 new long[] {30000, 40000, 50000, 60000, 70000, 80000, 90000},
                 new long[] {20000, 30000, 40000, 50000, 60000, 70000, 80000});
 
@@ -144,9 +143,7 @@
             super(null);
         }
 
-        public void setCpuTimes(long[] processCpuTimesUs, long[] threadCpuTimesUs,
-                long[] binderThreadCpuTimesUs) {
-            mThreadTimes.processCpuTimesUs = processCpuTimesUs;
+        public void setCpuTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) {
             mThreadTimes.threadCpuTimesUs = threadCpuTimesUs;
             mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs;
         }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 03f8918..f84d947 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -354,6 +354,7 @@
         <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
         <permission name="android.permission.MOVE_PACKAGE"/>
         <!-- Needed for test only -->
+        <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/>
         <permission name="android.permission.NETWORK_AIRPLANE_MODE"/>
         <permission name="android.permission.OBSERVE_APP_USAGE"/>
         <permission name="android.permission.NETWORK_SCAN"/>
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index c8ff95d..bb795cd 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -51,7 +51,8 @@
         XmlPullParser parser = Xml.newPullParser();
         parser.setInput(in, null);
         parser.nextTag();
-        return readFamilies(parser, "/system/fonts/", new FontCustomizationParser.Result(), null);
+        return readFamilies(parser, "/system/fonts/", new FontCustomizationParser.Result(), null,
+                0, 0);
     }
 
     /**
@@ -71,7 +72,9 @@
             @NonNull String systemFontDir,
             @Nullable String oemCustomizationXmlPath,
             @Nullable String productFontDir,
-            @Nullable Map<String, File> updatableFontMap
+            @Nullable Map<String, File> updatableFontMap,
+            long lastModifiedDate,
+            int configVersion
     ) throws IOException, XmlPullParserException {
         FontCustomizationParser.Result oemCustomization;
         if (oemCustomizationXmlPath != null) {
@@ -90,7 +93,8 @@
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(is, null);
             parser.nextTag();
-            return readFamilies(parser, systemFontDir, oemCustomization, updatableFontMap);
+            return readFamilies(parser, systemFontDir, oemCustomization, updatableFontMap,
+                    lastModifiedDate, configVersion);
         }
     }
 
@@ -98,7 +102,9 @@
             @NonNull XmlPullParser parser,
             @NonNull String fontDir,
             @NonNull FontCustomizationParser.Result customization,
-            @Nullable Map<String, File> updatableFontMap)
+            @Nullable Map<String, File> updatableFontMap,
+            long lastModifiedDate,
+            int configVersion)
             throws XmlPullParserException, IOException {
         List<FontConfig.FontFamily> families = new ArrayList<>();
         List<FontConfig.Alias> aliases = new ArrayList<>(customization.getAdditionalAliases());
@@ -126,7 +132,7 @@
         }
 
         families.addAll(oemNamedFamilies.values());
-        return new FontConfig(families, aliases);
+        return new FontConfig(families, aliases, lastModifiedDate, configVersion);
     }
 
     /**
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index ebf7cea..dd64327 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -570,6 +570,13 @@
         }
     }
 
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        if (mState.mNativePtr != 0) {
+            nSetBounds(mState.mNativePtr, bounds);
+        }
+    }
+
 
     private static native long nCreate(long nativeImageDecoder,
             @Nullable ImageDecoder decoder, int width, int height, long colorSpaceHandle,
@@ -601,4 +608,6 @@
     private static native long nNativeByteSize(long nativePtr);
     @FastNative
     private static native void nSetMirrored(long nativePtr, boolean mirror);
+    @FastNative
+    private static native void nSetBounds(long nativePtr, Rect rect);
 }
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index a41215f..c166e12 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -240,10 +240,12 @@
      * @hide
      */
     public static @NonNull FontConfig getSystemFontConfig(
-            @Nullable Map<String, File> updatableFontMap
+            @Nullable Map<String, File> updatableFontMap,
+            long lastModifiedDate,
+            int configVersion
     ) {
         return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR,
-                updatableFontMap);
+                updatableFontMap, lastModifiedDate, configVersion);
     }
 
     /**
@@ -251,7 +253,8 @@
      * @hide
      */
     public static @NonNull FontConfig getSystemPreinstalledFontConfig() {
-        return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, null);
+        return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, null,
+                0, 0);
     }
 
     /* package */ static @NonNull FontConfig getSystemFontConfigInternal(
@@ -259,17 +262,19 @@
             @NonNull String systemFontDir,
             @Nullable String oemXml,
             @Nullable String productFontDir,
-            @Nullable Map<String, File> updatableFontMap
+            @Nullable Map<String, File> updatableFontMap,
+            long lastModifiedDate,
+            int configVersion
     ) {
         try {
             return FontListParser.parse(fontsXml, systemFontDir, oemXml, productFontDir,
-                                                updatableFontMap);
+                                                updatableFontMap, lastModifiedDate, configVersion);
         } catch (IOException e) {
             Log.e(TAG, "Failed to open/read system font configurations.", e);
-            return new FontConfig(Collections.emptyList(), Collections.emptyList());
+            return new FontConfig(Collections.emptyList(), Collections.emptyList(), 0, 0);
         } catch (XmlPullParserException e) {
             Log.e(TAG, "Failed to parse the system font configuration.", e);
-            return new FontConfig(Collections.emptyList(), Collections.emptyList());
+            return new FontConfig(Collections.emptyList(), Collections.emptyList(), 0, 0);
         }
     }
 
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index 5928540..014d688 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -67,6 +67,7 @@
             PURPOSE_SIGN,
             PURPOSE_VERIFY,
             PURPOSE_WRAP_KEY,
+            PURPOSE_AGREE_KEY,
     })
     public @interface PurposeEnum {}
 
@@ -96,6 +97,11 @@
     public static final int PURPOSE_WRAP_KEY = 1 << 5;
 
     /**
+     * Purpose of key: creating a shared ECDH secret through key agreement.
+     */
+    public static final int PURPOSE_AGREE_KEY = 1 << 6;
+
+    /**
      * @hide
      */
     public static abstract class Purpose {
@@ -113,6 +119,8 @@
                     return KeymasterDefs.KM_PURPOSE_VERIFY;
                 case PURPOSE_WRAP_KEY:
                     return KeymasterDefs.KM_PURPOSE_WRAP;
+                case PURPOSE_AGREE_KEY:
+                    return KeymasterDefs.KM_PURPOSE_AGREE_KEY;
                 default:
                     throw new IllegalArgumentException("Unknown purpose: " + purpose);
             }
@@ -130,6 +138,8 @@
                     return PURPOSE_VERIFY;
                 case KeymasterDefs.KM_PURPOSE_WRAP:
                     return PURPOSE_WRAP_KEY;
+                case KeymasterDefs.KM_PURPOSE_AGREE_KEY:
+                    return PURPOSE_AGREE_KEY;
                 default:
                     throw new IllegalArgumentException("Unknown purpose: " + purpose);
             }
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
new file mode 100644
index 0000000..fc963a8
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore2;
+
+import android.hardware.security.keymint.Algorithm;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyPurpose;
+import android.hardware.security.keymint.Tag;
+import android.security.KeyStoreException;
+import android.security.KeyStoreOperation;
+import android.security.keystore.KeyStoreCryptoOperation;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.crypto.KeyAgreementSpi;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * {@link KeyAgreementSpi} which provides an ECDH implementation backed by Android KeyStore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreKeyAgreementSpi extends KeyAgreementSpi
+        implements KeyStoreCryptoOperation {
+
+    private static final String TAG = "AndroidKeyStoreKeyAgreementSpi";
+
+    /**
+     * ECDH implementation.
+     *
+     * @hide
+     */
+    public static class ECDH extends AndroidKeyStoreKeyAgreementSpi {
+        public ECDH() {
+            super(Algorithm.EC);
+        }
+    }
+
+    private final int mKeymintAlgorithm;
+
+    // Fields below are populated by engineInit and should be preserved after engineDoFinal.
+    private AndroidKeyStorePrivateKey mKey;
+    private PublicKey mOtherPartyKey;
+
+    // Fields below are reset when engineDoFinal succeeds.
+    private KeyStoreOperation mOperation;
+    private long mOperationHandle;
+
+    protected AndroidKeyStoreKeyAgreementSpi(int keymintAlgorithm) {
+        resetAll();
+
+        mKeymintAlgorithm = keymintAlgorithm;
+    }
+
+    @Override
+    protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException {
+        resetAll();
+
+        if (key == null) {
+            throw new InvalidKeyException("key == null");
+        } else if (!(key instanceof AndroidKeyStorePrivateKey)) {
+            throw new InvalidKeyException(
+                    "Only Android KeyStore private keys supported. Key: " + key);
+        }
+        // Checking the correct KEY_PURPOSE and algorithm is done by the Keymint implementation in
+        // ensureKeystoreOperationInitialized() below.
+        mKey = (AndroidKeyStorePrivateKey) key;
+
+        boolean success = false;
+        try {
+            ensureKeystoreOperationInitialized();
+            success = true;
+        } finally {
+            if (!success) {
+                resetAll();
+            }
+        }
+    }
+
+    @Override
+    protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        if (params != null) {
+            throw new InvalidAlgorithmParameterException(
+                    "Unsupported algorithm parameters: " + params);
+        }
+        engineInit(key, random);
+    }
+
+    @Override
+    protected Key engineDoPhase(Key key, boolean lastPhase)
+            throws InvalidKeyException, IllegalStateException {
+        ensureKeystoreOperationInitialized();
+
+        if (key == null) {
+            throw new InvalidKeyException("key == null");
+        } else if (!(key instanceof PublicKey)) {
+            throw new InvalidKeyException("Only public keys supported. Key: " + key);
+        } else if (!lastPhase) {
+            throw new IllegalStateException(
+                    "Only one other party supported. lastPhase must be set to true.");
+        } else if (mOtherPartyKey != null) {
+            throw new IllegalStateException(
+                    "Only one other party supported. doPhase() must only be called exactly once.");
+        }
+        // The other party key will be passed as part of the doFinal() call, to prevent an
+        // additional IPC.
+        mOtherPartyKey = (PublicKey) key;
+
+        return null; // No intermediate key
+    }
+
+    @Override
+    protected byte[] engineGenerateSecret() throws IllegalStateException {
+        try {
+            ensureKeystoreOperationInitialized();
+        } catch (InvalidKeyException e) {
+            throw new IllegalStateException("Not initialized", e);
+        }
+
+        if (mOtherPartyKey == null) {
+            throw new IllegalStateException("Other party key not provided. Call doPhase() first.");
+        }
+        byte[] otherPartyKeyEncoded = mOtherPartyKey.getEncoded();
+
+        try {
+            return mOperation.finish(otherPartyKeyEncoded, null);
+        } catch (KeyStoreException e) {
+            throw new ProviderException("Keystore operation failed", e);
+        } finally {
+            resetWhilePreservingInitState();
+        }
+    }
+
+    @Override
+    protected SecretKey engineGenerateSecret(String algorithm)
+            throws IllegalStateException, NoSuchAlgorithmException, InvalidKeyException {
+        byte[] generatedSecret = engineGenerateSecret();
+
+        return new SecretKeySpec(generatedSecret, algorithm);
+    }
+
+    @Override
+    protected int engineGenerateSecret(byte[] sharedSecret, int offset)
+            throws IllegalStateException, ShortBufferException {
+        byte[] generatedSecret = engineGenerateSecret();
+
+        if (generatedSecret.length > sharedSecret.length - offset) {
+            throw new ShortBufferException("Needed: " + generatedSecret.length);
+        }
+        System.arraycopy(generatedSecret, 0, sharedSecret, offset, generatedSecret.length);
+        return generatedSecret.length;
+    }
+
+    @Override
+    public long getOperationHandle() {
+        return mOperationHandle;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            resetAll();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private void resetWhilePreservingInitState() {
+        KeyStoreCryptoOperationUtils.abortOperation(mOperation);
+        mOperationHandle = 0;
+        mOperation = null;
+        mOtherPartyKey = null;
+    }
+
+    private void resetAll() {
+        resetWhilePreservingInitState();
+        mKey = null;
+    }
+
+    private void ensureKeystoreOperationInitialized()
+            throws InvalidKeyException, IllegalStateException {
+        if (mKey == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+        if (mOperation != null) {
+            return;
+        }
+
+        // We don't need to explicitly pass in any other parameters here, as they're part of the
+        // private key that is available to Keymint.
+        List<KeyParameter> parameters = new ArrayList<>();
+        parameters.add(KeyStore2ParameterUtils.makeEnum(
+                Tag.PURPOSE, KeyPurpose.AGREE_KEY
+        ));
+
+        try {
+            mOperation =
+                    mKey.getSecurityLevel().createOperation(mKey.getKeyIdDescriptor(), parameters);
+        } catch (KeyStoreException keyStoreException) {
+            // If necessary, throw an exception due to KeyStore operation having failed.
+            InvalidKeyException e =
+                    KeyStoreCryptoOperationUtils.getInvalidKeyException(mKey, keyStoreException);
+            if (e != null) {
+                throw e;
+            }
+        }
+
+        // Set the operation handle. This will be a random number, or the operation challenge if
+        // user authentication is required. If we got a challenge we check if the authorization can
+        // possibly succeed.
+        mOperationHandle =
+                KeyStoreCryptoOperationUtils.getOrMakeOperationChallenge(mOperation, mKey);
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 403da18..164bc86 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -94,6 +94,9 @@
             put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede");
         }
 
+        // javax.crypto.KeyAgreement
+        put("KeyAgreement.ECDH", PACKAGE_NAME + ".AndroidKeyStoreKeyAgreementSpi$ECDH");
+
         // java.security.SecretKeyFactory
         putSecretKeyFactoryImpl("AES");
         if (supports3DES) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 3944128..a2cd683 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -42,10 +42,11 @@
 import androidx.annotation.BinderThread;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.internal.inputmethod.Completable;
+import com.android.internal.inputmethod.ResultCallbacks;
 import com.android.internal.view.IInputMethodManager;
 
 import java.util.ArrayList;
-import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -207,23 +208,21 @@
         }
 
         protected void insetsChanged(InsetsState insetsState) {
-            mMainExecutor.execute(() -> {
-                if (mInsetsState.equals(insetsState)) {
-                    return;
-                }
+            if (mInsetsState.equals(insetsState)) {
+                return;
+            }
 
-                mImeShowing = insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME);
+            mImeShowing = insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME);
 
-                final InsetsSource newSource = insetsState.getSource(InsetsState.ITYPE_IME);
-                final Rect newFrame = newSource.getFrame();
-                final Rect oldFrame = mInsetsState.getSource(InsetsState.ITYPE_IME).getFrame();
+            final InsetsSource newSource = insetsState.getSource(InsetsState.ITYPE_IME);
+            final Rect newFrame = newSource.getFrame();
+            final Rect oldFrame = mInsetsState.getSource(InsetsState.ITYPE_IME).getFrame();
 
-                mInsetsState.set(insetsState, true /* copySources */);
-                if (mImeShowing && !newFrame.equals(oldFrame) && newSource.isVisible()) {
-                    if (DEBUG) Slog.d(TAG, "insetsChanged when IME showing, restart animation");
-                    startAnimation(mImeShowing, true /* forceRestart */);
-                }
-            });
+            mInsetsState.set(insetsState, true /* copySources */);
+            if (mImeShowing && !newFrame.equals(oldFrame) && newSource.isVisible()) {
+                if (DEBUG) Slog.d(TAG, "insetsChanged when IME showing, restart animation");
+                startAnimation(mImeShowing, true /* forceRestart */);
+            }
         }
 
         @VisibleForTesting
@@ -236,27 +235,25 @@
                         continue;
                     }
                     if (activeControl.getType() == InsetsState.ITYPE_IME) {
-                        mMainExecutor.execute(() -> {
-                            final Point lastSurfacePosition = mImeSourceControl != null
-                                    ? mImeSourceControl.getSurfacePosition() : null;
-                            final boolean positionChanged =
-                                    !activeControl.getSurfacePosition().equals(lastSurfacePosition);
-                            final boolean leashChanged =
-                                    !haveSameLeash(mImeSourceControl, activeControl);
-                            mImeSourceControl = activeControl;
-                            if (mAnimation != null) {
-                                if (positionChanged) {
-                                    startAnimation(mImeShowing, true /* forceRestart */);
-                                }
-                            } else {
-                                if (leashChanged) {
-                                    applyVisibilityToLeash();
-                                }
-                                if (!mImeShowing) {
-                                    removeImeSurface();
-                                }
+                        final Point lastSurfacePosition = mImeSourceControl != null
+                                ? mImeSourceControl.getSurfacePosition() : null;
+                        final boolean positionChanged =
+                                !activeControl.getSurfacePosition().equals(lastSurfacePosition);
+                        final boolean leashChanged =
+                                !haveSameLeash(mImeSourceControl, activeControl);
+                        mImeSourceControl = activeControl;
+                        if (mAnimation != null) {
+                            if (positionChanged) {
+                                startAnimation(mImeShowing, true /* forceRestart */);
                             }
-                        });
+                        } else {
+                            if (leashChanged) {
+                                applyVisibilityToLeash();
+                            }
+                            if (!mImeShowing) {
+                                removeImeSurface();
+                            }
+                        }
                     }
                 }
             }
@@ -281,7 +278,7 @@
                 return;
             }
             if (DEBUG) Slog.d(TAG, "Got showInsets for ime");
-            mMainExecutor.execute(() -> startAnimation(true /* show */, false /* forceRestart */));
+            startAnimation(true /* show */, false /* forceRestart */);
         }
 
 
@@ -290,7 +287,7 @@
                 return;
             }
             if (DEBUG) Slog.d(TAG, "Got hideInsets for ime");
-            mMainExecutor.execute(() -> startAnimation(false /* show */, false /* forceRestart */));
+            startAnimation(false /* show */, false /* forceRestart */);
         }
 
         public void topFocusedWindowChanged(String packageName) {
@@ -506,7 +503,9 @@
             try {
                 // Remove the IME surface to make the insets invisible for
                 // non-client controlled insets.
-                imms.removeImeSurface();
+                final Completable.Void value = Completable.createVoid();
+                imms.removeImeSurface(ResultCallbacks.of(value));
+                Completable.getResult(value);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to remove IME surface.", e);
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index f839727..a8961ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -30,6 +30,8 @@
 import android.view.DisplayInfo;
 import android.view.Gravity;
 
+import com.android.wm.shell.common.DisplayLayout;
+
 import java.io.PrintWriter;
 
 /**
@@ -190,9 +192,9 @@
                 size = adjustSizeToAspectRatio(overrideMinSize, aspectRatio);
             } else {
                 // Calculate the default size using the display size and default min edge size.
-                final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
+                final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout();
                 size = getSizeForAspectRatio(aspectRatio, mDefaultMinSize,
-                        displayInfo.logicalWidth, displayInfo.logicalHeight);
+                        displayLayout.width(), displayLayout.height());
             }
         }
 
@@ -232,7 +234,7 @@
         final Size defaultSize;
         final Rect insetBounds = new Rect();
         getInsetBounds(insetBounds);
-        final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
+        final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout();
         final Size overrideMinSize = mPipBoundsState.getOverrideMinSize();
         if (overrideMinSize != null) {
             // The override minimal size is set, use that as the default size making sure it's
@@ -241,7 +243,7 @@
         } else {
             // Calculate the default size using the display size and default min edge size.
             defaultSize = getSizeForAspectRatio(mDefaultAspectRatio,
-                    mDefaultMinSize, displayInfo.logicalWidth, displayInfo.logicalHeight);
+                    mDefaultMinSize, displayLayout.width(), displayLayout.height());
         }
 
         // Now that we have the default size, apply the snap fraction if valid or position the
@@ -264,12 +266,12 @@
      * Populates the bounds on the screen that the PIP can be visible in.
      */
     public void getInsetBounds(Rect outRect) {
-        final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
+        final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout();
         Rect insets = mPipBoundsState.getDisplayLayout().stableInsets();
         outRect.set(insets.left + mScreenEdgeInsets.x,
                 insets.top + mScreenEdgeInsets.y,
-                displayInfo.logicalWidth - insets.right - mScreenEdgeInsets.x,
-                displayInfo.logicalHeight - insets.bottom - mScreenEdgeInsets.y);
+                displayLayout.width() - insets.right - mScreenEdgeInsets.x,
+                displayLayout.height() - insets.bottom - mScreenEdgeInsets.y);
     }
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 9595b5a..b112c51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -24,6 +24,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.Size;
+import android.view.Display;
 import android.view.DisplayInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -68,7 +69,7 @@
     private int mStashOffset;
     private @Nullable PipReentryState mPipReentryState;
     private @Nullable ComponentName mLastPipComponentName;
-    private final @NonNull DisplayInfo mDisplayInfo = new DisplayInfo();
+    private int mDisplayId = Display.DEFAULT_DISPLAY;
     private final @NonNull DisplayLayout mDisplayLayout = new DisplayLayout();
     /** The current minimum edge size of PIP. */
     private int mMinEdgeSize;
@@ -238,26 +239,20 @@
         return mLastPipComponentName;
     }
 
-    /** Get the current display info. */
-    @NonNull
-    public DisplayInfo getDisplayInfo() {
-        return mDisplayInfo;
+    /** Get the current display id. */
+    public int getDisplayId() {
+        return mDisplayId;
     }
 
-    /** Update the display info. */
-    public void setDisplayInfo(@NonNull DisplayInfo displayInfo) {
-        mDisplayInfo.copyFrom(displayInfo);
-    }
-
-    /** Set the rotation of the display. */
-    public void setDisplayRotation(int rotation) {
-        mDisplayInfo.rotation = rotation;
+    /** Set the current display id for the associated display layout. */
+    public void setDisplayId(int displayId) {
+        mDisplayId = displayId;
     }
 
     /** Returns the display's bounds. */
     @NonNull
     public Rect getDisplayBounds() {
-        return new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+        return new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
     }
 
     /** Update the display layout. */
@@ -474,7 +469,7 @@
         pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
         pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
         pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
-        pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
+        pw.println(innerPrefix + "mDisplayId=" + mDisplayId);
         pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout);
         pw.println(innerPrefix + "mStashedState=" + mStashedState);
         pw.println(innerPrefix + "mStashOffset=" + mStashOffset);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index b80f285..b7958b7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -55,6 +55,7 @@
 import android.util.Log;
 import android.util.Rational;
 import android.util.Size;
+import android.view.Display;
 import android.view.SurfaceControl;
 import android.window.TaskOrganizer;
 import android.window.WindowContainerToken;
@@ -324,7 +325,7 @@
         mPipUiEventLoggerLogger.log(
                 PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
         final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation()
-                != mPipBoundsState.getDisplayInfo().rotation;
+                != mPipBoundsState.getDisplayLayout().rotation();
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         final Rect destinationBounds = initialConfig.windowConfiguration.getBounds();
         final int direction = syncWithSplitScreenBounds(destinationBounds)
@@ -437,7 +438,7 @@
 
         // If the displayId of the task is different than what PipBoundsHandler has, then update
         // it. This is possible if we entered PiP on an external display.
-        if (info.displayId != mPipBoundsState.getDisplayInfo().displayId
+        if (info.displayId != mPipBoundsState.getDisplayId()
                 && mOnDisplayIdChangeCallback != null) {
             mOnDisplayIdChangeCallback.accept(info.displayId);
         }
@@ -605,6 +606,10 @@
         mState = State.UNDEFINED;
         mPipUiEventLoggerLogger.setTaskInfo(null);
         mPipMenuController.detach();
+
+        if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) {
+            mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY);
+        }
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index cefeb939..c06f9d0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -19,8 +19,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 
 import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
@@ -86,7 +84,6 @@
     private PipTouchHandler mTouchHandler;
     protected final PipImpl mImpl = new PipImpl();
 
-    private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
     private final Rect mTmpInsetBounds = new Rect();
 
     private boolean mIsInFixedRotation;
@@ -145,7 +142,7 @@
         }
     };
 
-    private final DisplayController.OnDisplaysChangedListener mFixedRotationListener =
+    private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener =
             new DisplayController.OnDisplaysChangedListener() {
                 @Override
                 public void onFixedRotationStarted(int displayId, int newRotation) {
@@ -159,8 +156,20 @@
 
                 @Override
                 public void onDisplayAdded(int displayId) {
-                    mPipBoundsState.setDisplayLayout(
-                            mDisplayController.getDisplayLayout(displayId));
+                    if (displayId != mPipBoundsState.getDisplayId()) {
+                        return;
+                    }
+                    onDisplayChanged(mDisplayController.getDisplayLayout(displayId),
+                            false /* saveRestoreSnapFraction */);
+                }
+
+                @Override
+                public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+                    if (displayId != mPipBoundsState.getDisplayId()) {
+                        return;
+                    }
+                    onDisplayChanged(mDisplayController.getDisplayLayout(displayId),
+                            true /* saveRestoreSnapFraction */);
                 }
             };
 
@@ -261,12 +270,9 @@
                 INPUT_CONSUMER_PIP, mainExecutor);
         mPipTaskOrganizer.registerPipTransitionCallback(this);
         mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> {
-            final DisplayInfo newDisplayInfo = new DisplayInfo();
-            displayController.getDisplay(displayId).getDisplayInfo(newDisplayInfo);
-            mPipBoundsState.setDisplayInfo(newDisplayInfo);
-            updateMovementBounds(null /* toBounds */, false /* fromRotation */,
-                    false /* fromImeAdjustment */, false /* fromShelfAdjustment */,
-                    null /* wct */);
+            mPipBoundsState.setDisplayId(displayId);
+            onDisplayChanged(displayController.getDisplayLayout(displayId),
+                    false /* saveRestoreSnapFraction */);
         });
         mPipBoundsState.setOnMinimalSizeChangeCallback(
                 () -> {
@@ -291,13 +297,12 @@
             mPipInputConsumer.setRegistrationListener(mTouchHandler::onRegistrationChanged);
         }
         displayController.addDisplayChangingController(mRotationController);
-        displayController.addDisplayWindowListener(mFixedRotationListener);
+        displayController.addDisplayWindowListener(mDisplaysChangedListener);
 
         // Ensure that we have the display info in case we get calls to update the bounds before the
         // listener calls back
-        final DisplayInfo displayInfo = new DisplayInfo();
-        context.getDisplay().getDisplayInfo(displayInfo);
-        mPipBoundsState.setDisplayInfo(displayInfo);
+        mPipBoundsState.setDisplayId(context.getDisplayId());
+        mPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay()));
 
         try {
             mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener);
@@ -363,11 +368,42 @@
     }
 
     private void onOverlayChanged() {
-        mPipBoundsState.setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay()));
-        updateMovementBounds(null /* toBounds */,
-                false /* fromRotation */, false /* fromImeAdjustment */,
-                false /* fromShelfAdjustment */,
-                null /* windowContainerTransaction */);
+        onDisplayChanged(new DisplayLayout(mContext, mContext.getDisplay()),
+                false /* saveRestoreSnapFraction */);
+    }
+
+    private void onDisplayChanged(DisplayLayout layout, boolean saveRestoreSnapFraction) {
+        Runnable updateDisplayLayout = () -> {
+            mPipBoundsState.setDisplayLayout(layout);
+            updateMovementBounds(null /* toBounds */,
+                    false /* fromRotation */, false /* fromImeAdjustment */,
+                    false /* fromShelfAdjustment */,
+                    null /* windowContainerTransaction */);
+        };
+
+        if (saveRestoreSnapFraction) {
+            // Calculate the snap fraction of the current stack along the old movement bounds
+            final PipSnapAlgorithm pipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm();
+            final Rect postChangeStackBounds = new Rect(mPipBoundsState.getBounds());
+            final float snapFraction = pipSnapAlgorithm.getSnapFraction(postChangeStackBounds,
+                    mPipBoundsAlgorithm.getMovementBounds(postChangeStackBounds),
+                    mPipBoundsState.getStashedState());
+
+            updateDisplayLayout.run();
+
+            // Calculate the stack bounds in the new orientation based on same fraction along the
+            // rotated movement bounds.
+            final Rect postChangeMovementBounds = mPipBoundsAlgorithm.getMovementBounds(
+                    postChangeStackBounds, false /* adjustForIme */);
+            pipSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
+                    snapFraction, mPipBoundsState.getStashedState(),
+                    mPipBoundsState.getStashOffset(),
+                    mPipBoundsState.getDisplayBounds());
+
+            mTouchHandler.getMotionHelper().movePip(postChangeStackBounds);
+        } else {
+            updateDisplayLayout.run();
+        }
     }
 
     private void registerSessionListenerForCurrentUser() {
@@ -500,7 +536,7 @@
         // Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler before
         // passing to mTouchHandler/mPipTaskOrganizer
         final Rect outBounds = new Rect(toBounds);
-        mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo());
+        final int rotation = mPipBoundsState.getDisplayLayout().rotation();
 
         mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds);
         mPipBoundsState.setNormalBounds(mPipBoundsAlgorithm.getNormalBounds());
@@ -512,8 +548,7 @@
         mPipTaskOrganizer.onMovementBoundsChanged(outBounds, fromRotation, fromImeAdjustment,
                 fromShelfAdjustment, wct);
         mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mPipBoundsState.getNormalBounds(),
-                outBounds, fromImeAdjustment, fromShelfAdjustment,
-                mTmpDisplayInfo.rotation);
+                outBounds, fromImeAdjustment, fromShelfAdjustment, rotation);
     }
 
     /**
@@ -525,13 +560,6 @@
         // Update the display layout, note that we have to do this on every rotation even if we
         // aren't in PIP since we need to update the display layout to get the right resources
         mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation);
-
-        // Populate the new {@link #mDisplayInfo}.
-        // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
-        // therefore, the width/height may require a swap first.
-        // Moving forward, we should get the new dimensions after rotation from DisplayLayout.
-        mPipBoundsState.setDisplayRotation(toRotation);
-        updateDisplayInfoIfNeeded();
     }
 
     /**
@@ -543,9 +571,8 @@
     private boolean onDisplayRotationChanged(Context context, Rect outBounds, Rect oldBounds,
             Rect outInsetBounds,
             int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) {
-        // Bail early if the event is not sent to current {@link #mDisplayInfo}
-        if ((displayId != mPipBoundsState.getDisplayInfo().displayId)
-                || (fromRotation == toRotation)) {
+        // Bail early if the event is not sent to current display
+        if ((displayId != mPipBoundsState.getDisplayId()) || (fromRotation == toRotation)) {
             return false;
         }
 
@@ -570,13 +597,6 @@
         // Update the display layout
         mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation);
 
-        // Populate the new {@link #mDisplayInfo}.
-        // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
-        // therefore, the width/height may require a swap first.
-        // Moving forward, we should get the new dimensions after rotation from DisplayLayout.
-        mPipBoundsState.getDisplayInfo().rotation = toRotation;
-        updateDisplayInfoIfNeeded();
-
         // Calculate the stack bounds in the new orientation based on same fraction along the
         // rotated movement bounds.
         final Rect postChangeMovementBounds = mPipBoundsAlgorithm.getMovementBounds(
@@ -591,21 +611,6 @@
         return true;
     }
 
-    private void updateDisplayInfoIfNeeded() {
-        final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
-        final boolean updateNeeded;
-        if ((displayInfo.rotation == ROTATION_0) || (displayInfo.rotation == ROTATION_180)) {
-            updateNeeded = (displayInfo.logicalWidth > displayInfo.logicalHeight);
-        } else {
-            updateNeeded = (displayInfo.logicalWidth < displayInfo.logicalHeight);
-        }
-        if (updateNeeded) {
-            final int newLogicalHeight = displayInfo.logicalWidth;
-            displayInfo.logicalWidth = displayInfo.logicalHeight;
-            displayInfo.logicalHeight = newLogicalHeight;
-        }
-    }
-
     private void dump(PrintWriter pw) {
         final String innerPrefix = "  ";
         pw.println(TAG);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 61cf22b..75fc9f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -36,6 +36,7 @@
 
 import com.android.wm.shell.R;
 import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.TaskStackListenerCallback;
 import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -138,7 +139,8 @@
         mMainExecutor = mainExecutor;
 
         mPipBoundsState = pipBoundsState;
-        mPipBoundsState.setDisplayInfo(getDisplayInfo());
+        mPipBoundsState.setDisplayId(context.getDisplayId());
+        mPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay()));
         mPipBoundsAlgorithm = pipBoundsAlgorithm;
 
         mPipMediaController = pipMediaController;
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt
index ed305a2..e79820f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt
@@ -67,7 +67,8 @@
                     visibleLayersShownMoreThanOneConsecutiveEntry(
                             listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
                                     nonResizeableApp.defaultWindowName, LETTER_BOX_NAME,
-                                    TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME)
+                                    TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME),
+                            bugId = 178447631
                     )
                 }
                 windowManagerTrace {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt
index 88dab51..280af5d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt
@@ -68,7 +68,7 @@
                             listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
                                     nonResizeableApp.defaultWindowName, LETTER_BOX_NAME,
                                     TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME),
-                        enabled = false
+                        bugId = 178447631
                     )
                 }
                 windowManagerTrace {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt
index 292d0ef..5e0760c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt
@@ -127,7 +127,7 @@
                         hasVisibleRegion(pipApp.defaultWindowName, startingBounds)
                         isInvisible(testApp.defaultWindowName)
                     }
-                    end("testApp layer covers fullscreen") {
+                    end("testApp layer covers fullscreen", enabled = false) {
                         hasVisibleRegion(testApp.defaultWindowName, endingBounds)
                     }
                     navBarLayerIsAlwaysVisible(bugId = 140855415)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index 5e0d518..4cedc48 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -19,11 +19,13 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.InsetsState.ITYPE_IME;
 import static android.view.Surface.ROTATION_0;
+import static android.view.WindowInsets.Type.ime;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 
@@ -40,18 +42,22 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.concurrent.Executor;
+
 @SmallTest
 public class DisplayImeControllerTest {
 
     private SurfaceControl.Transaction mT;
     private DisplayImeController.PerDisplay mPerDisplay;
     private IInputMethodManager mMock;
+    private Executor mExecutor;
 
     @Before
     public void setUp() throws Exception {
         mT = mock(SurfaceControl.Transaction.class);
         mMock = mock(IInputMethodManager.class);
-        mPerDisplay = new DisplayImeController(null, null, Runnable::run, new TransactionPool() {
+        mExecutor = spy(Runnable::run);
+        mPerDisplay = new DisplayImeController(null, null, mExecutor, new TransactionPool() {
             @Override
             public SurfaceControl.Transaction acquire() {
                 return mT;
@@ -65,10 +71,36 @@
             public IInputMethodManager getImms() {
                 return mMock;
             }
+            @Override
+            void removeImeSurface() { }
         }.new PerDisplay(DEFAULT_DISPLAY, ROTATION_0);
     }
 
     @Test
+    public void insetsControlChanged_schedulesNoWorkOnExecutor() {
+        mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
+        verifyZeroInteractions(mExecutor);
+    }
+
+    @Test
+    public void insetsChanged_schedulesNoWorkOnExecutor() {
+        mPerDisplay.insetsChanged(insetsStateWithIme(false));
+        verifyZeroInteractions(mExecutor);
+    }
+
+    @Test
+    public void showInsets_schedulesNoWorkOnExecutor() {
+        mPerDisplay.showInsets(ime(), true);
+        verifyZeroInteractions(mExecutor);
+    }
+
+    @Test
+    public void hideInsets_schedulesNoWorkOnExecutor() {
+        mPerDisplay.hideInsets(ime(), true);
+        verifyZeroInteractions(mExecutor);
+    }
+
+    @Test
     public void reappliesVisibilityToChangedLeash() {
         verifyZeroInteractions(mT);
         mPerDisplay.mImeShowing = true;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
index ef99235..babfc5c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
@@ -24,12 +24,14 @@
 import android.testing.TestableLooper;
 import android.testing.TestableResources;
 import android.util.Size;
+import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -62,7 +64,8 @@
         mPipBoundsState = new PipBoundsState(mContext);
         mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState);
 
-        mPipBoundsState.setDisplayInfo(mDefaultDisplayInfo);
+        mPipBoundsState.setDisplayLayout(
+                new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true));
     }
 
     private void initializeMockResources() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 245858d..7a810a1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -38,6 +38,7 @@
 import android.testing.TestableLooper;
 import android.util.Rational;
 import android.util.Size;
+import android.view.Display;
 import android.view.DisplayInfo;
 import android.window.WindowContainerToken;
 
@@ -45,6 +46,7 @@
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestShellExecutor;
 import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.pip.phone.PhonePipMenuController;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
@@ -196,11 +198,11 @@
 
     private void preparePipTaskOrg() {
         final DisplayInfo info = new DisplayInfo();
-        mPipBoundsState.setDisplayInfo(info);
+        mPipBoundsState.setDisplayLayout(new DisplayLayout(info,
+                mContext.getResources(), true, true));
         when(mMockPipBoundsAlgorithm.getEntryDestinationBounds()).thenReturn(new Rect());
         when(mMockPipBoundsAlgorithm.getAdjustedDestinationBounds(any(), anyFloat()))
                 .thenReturn(new Rect());
-        mPipBoundsState.setDisplayInfo(info);
         mSpiedPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
         doNothing().when(mSpiedPipTaskOrganizer).enterPipWithAlphaAnimation(any(), anyLong());
         doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any());
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 1ebc489..1a8d9eb 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -700,11 +700,14 @@
         NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get());
     }
 
+    SkFilterMode filter = paint && paint->isFilterBitmap() ? SkFilterMode::kLinear
+                                                           : SkFilterMode::kNearest;
+
     lattice.fBounds = nullptr;
     SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
     auto image = bitmap.makeImage();
     apply_looper(paint, [&](const SkPaint& p) {
-        mCanvas->drawImageLattice(image.get(), lattice, dst, &p);
+        mCanvas->drawImageLattice(image.get(), lattice, dst, filter, &p);
     });
 }
 
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
index fa0c45b..cceba59 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -408,21 +408,24 @@
         const sk_sp<Bitmap>& bitmap,
         SkRect dst,
         SkCanvas::Lattice lattice,
+        SkFilterMode filter,
         SkPaint  paint
     ):  dst(dst),
         lattice(lattice),
+        filter(filter),
         bitmap(bitmap),
         image(bitmap->makeImage()),
         paint(std::move(paint)) {}
 
     SkRect dst;
     SkCanvas::Lattice lattice;
+    SkFilterMode filter;
     const sk_sp<Bitmap> bitmap;
     const sk_sp<SkImage> image;
 
     SkPaint paint;
     void draw(SkCanvas* canvas) const {
-        canvas->drawImageLattice(image.get(), lattice, dst, &paint);
+        canvas->drawImageLattice(image.get(), lattice, dst, filter, &paint);
     }
     ASSERT_DRAWABLE()
 };
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 638de85..0d3d3e3 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -20,6 +20,7 @@
 #endif
 
 #include "utils/TraceUtils.h"
+#include "pipeline/skia/SkiaUtils.h"
 
 #include <SkPicture.h>
 #include <SkRefCnt.h>
@@ -31,6 +32,7 @@
 AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed)
         : mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed) {
     mTimeToShowNextSnapshot = ms2ns(mSkAnimatedImage->currentFrameDuration());
+    setStagingBounds(mSkAnimatedImage->getBounds());
 }
 
 void AnimatedImageDrawable::syncProperties() {
@@ -127,21 +129,38 @@
     return snap;
 }
 
+// Update the matrix to map from the intrinsic bounds of the SkAnimatedImage to
+// the bounds specified by Drawable#setBounds.
+static void handleBounds(SkMatrix* matrix, const SkRect& intrinsicBounds, const SkRect& bounds) {
+    matrix->preTranslate(bounds.left(), bounds.top());
+    matrix->preScale(bounds.width()  / intrinsicBounds.width(),
+                     bounds.height() / intrinsicBounds.height());
+}
+
 // Only called on the RenderThread.
 void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
+    // Store the matrix used to handle bounds and mirroring separate from the
+    // canvas. We may need to invert the matrix to determine the proper bounds
+    // to pass to saveLayer, and this matrix (as opposed to, potentially, the
+    // canvas' matrix) only uses scale and translate, so it must be invertible.
+    SkMatrix matrix;
+    SkAutoCanvasRestore acr(canvas, true);
+    handleBounds(&matrix, mSkAnimatedImage->getBounds(), mProperties.mBounds);
+
+    if (mProperties.mMirrored) {
+        matrix.preTranslate(mSkAnimatedImage->getBounds().width(), 0);
+        matrix.preScale(-1, 1);
+    }
+
     std::optional<SkPaint> lazyPaint;
-    SkAutoCanvasRestore acr(canvas, false);
     if (mProperties.mAlpha != SK_AlphaOPAQUE || mProperties.mColorFilter.get()) {
         lazyPaint.emplace();
         lazyPaint->setAlpha(mProperties.mAlpha);
         lazyPaint->setColorFilter(mProperties.mColorFilter);
         lazyPaint->setFilterQuality(kLow_SkFilterQuality);
     }
-    if (mProperties.mMirrored) {
-        canvas->save();
-        canvas->translate(mSkAnimatedImage->getBounds().width(), 0);
-        canvas->scale(-1, 1);
-    }
+
+    canvas->concat(matrix);
 
     const bool starting = mStarting;
     mStarting = false;
@@ -151,7 +170,11 @@
         // The image is not animating, and never was. Draw directly from
         // mSkAnimatedImage.
         if (lazyPaint) {
-            canvas->saveLayer(mSkAnimatedImage->getBounds(), &*lazyPaint);
+            SkMatrix inverse;
+            (void) matrix.invert(&inverse);
+            SkRect r = mProperties.mBounds;
+            inverse.mapRect(&r);
+            canvas->saveLayer(r, &*lazyPaint);
         }
 
         std::unique_lock lock{mImageLock};
@@ -211,17 +234,31 @@
 }
 
 int AnimatedImageDrawable::drawStaging(SkCanvas* canvas) {
-    SkAutoCanvasRestore acr(canvas, false);
+    // Store the matrix used to handle bounds and mirroring separate from the
+    // canvas. We may need to invert the matrix to determine the proper bounds
+    // to pass to saveLayer, and this matrix (as opposed to, potentially, the
+    // canvas' matrix) only uses scale and translate, so it must be invertible.
+    SkMatrix matrix;
+    SkAutoCanvasRestore acr(canvas, true);
+    handleBounds(&matrix, mSkAnimatedImage->getBounds(), mStagingProperties.mBounds);
+
+    if (mStagingProperties.mMirrored) {
+        matrix.preTranslate(mSkAnimatedImage->getBounds().width(), 0);
+        matrix.preScale(-1, 1);
+    }
+
+    canvas->concat(matrix);
+
     if (mStagingProperties.mAlpha != SK_AlphaOPAQUE || mStagingProperties.mColorFilter.get()) {
         SkPaint paint;
         paint.setAlpha(mStagingProperties.mAlpha);
         paint.setColorFilter(mStagingProperties.mColorFilter);
-        canvas->saveLayer(mSkAnimatedImage->getBounds(), &paint);
-    }
-    if (mStagingProperties.mMirrored) {
-        canvas->save();
-        canvas->translate(mSkAnimatedImage->getBounds().width(), 0);
-        canvas->scale(-1, 1);
+
+        SkMatrix inverse;
+        (void) matrix.invert(&inverse);
+        SkRect r = mStagingProperties.mBounds;
+        inverse.mapRect(&r);
+        canvas->saveLayer(r, &paint);
     }
 
     if (!mRunning) {
@@ -294,4 +331,10 @@
     return ns2ms(mTimeToShowNextSnapshot - mCurrentTime);
 }
 
+SkRect AnimatedImageDrawable::onGetBounds() {
+    // This must return a bounds that is valid for all possible states,
+    // including if e.g. the client calls setBounds.
+    return SkRectMakeLargest();
+}
+
 }  // namespace android
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index f81a5a4..8ca3c7e 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -67,9 +67,10 @@
         mStagingProperties.mColorFilter = filter;
     }
     void setStagingMirrored(bool mirrored) { mStagingProperties.mMirrored = mirrored; }
+    void setStagingBounds(const SkRect& bounds) { mStagingProperties.mBounds = bounds; }
     void syncProperties();
 
-    virtual SkRect onGetBounds() override { return mSkAnimatedImage->getBounds(); }
+    SkRect onGetBounds() override;
 
     // Draw to software canvas, and return time to next draw.
     // 0 means the animation is not running.
@@ -109,7 +110,7 @@
     size_t byteSize() const { return sizeof(*this) + mBytesUsed; }
 
 protected:
-    virtual void onDraw(SkCanvas* canvas) override;
+    void onDraw(SkCanvas* canvas) override;
 
 private:
     sk_sp<SkAnimatedImage> mSkAnimatedImage;
@@ -145,6 +146,7 @@
         int mAlpha = SK_AlphaOPAQUE;
         sk_sp<SkColorFilter> mColorFilter;
         bool mMirrored = false;
+        SkRect mBounds;
 
         Properties() = default;
         Properties(Properties&) = default;
diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp
index 1ff1565..c9433ec8 100644
--- a/libs/hwui/jni/AnimatedImageDrawable.cpp
+++ b/libs/hwui/jni/AnimatedImageDrawable.cpp
@@ -244,6 +244,14 @@
     drawable->setStagingMirrored(mirrored);
 }
 
+static void AnimatedImageDrawable_nSetBounds(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
+                                             jobject jrect) {
+    auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+    SkRect rect;
+    GraphicsJNI::jrect_to_rect(env, jrect, &rect);
+    drawable->setStagingBounds(rect);
+}
+
 static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
     { "nCreate",             "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",(void*) AnimatedImageDrawable_nCreate },
     { "nGetNativeFinalizer", "()J",                                                          (void*) AnimatedImageDrawable_nGetNativeFinalizer },
@@ -259,6 +267,7 @@
     { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
     { "nNativeByteSize",     "(J)J",                                                         (void*) AnimatedImageDrawable_nNativeByteSize },
     { "nSetMirrored",        "(JZ)V",                                                        (void*) AnimatedImageDrawable_nSetMirrored },
+    { "nSetBounds",          "(JLandroid/graphics/Rect;)V",                                  (void*) AnimatedImageDrawable_nSetBounds },
 };
 
 int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index a09e742..c9e8d80 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -528,6 +528,7 @@
             bitmap,
             SkRect::MakeWH(5, 1),
             lattice,
+            SkFilterMode::kNearest,
             SkPaint{}
         }
     );
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 77f7b54..31fb8d0 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -16,6 +16,7 @@
 package android.media.session;
 
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.pm.ParceledListSlice;
 import android.media.AudioAttributes;
 import android.media.MediaMetadata;
@@ -35,6 +36,7 @@
     void setFlags(int flags);
     void setActive(boolean active);
     void setMediaButtonReceiver(in PendingIntent mbr);
+    void setMediaButtonBroadcastReceiver(in ComponentName broadcastReceiver);
     void setLaunchPendingIntent(in PendingIntent pi);
     void destroySession();
 
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 14b2368..24118b0 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -23,6 +23,7 @@
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.media.AudioAttributes;
@@ -131,6 +132,7 @@
     public @interface SessionFlags { }
 
     private final Object mLock = new Object();
+    private Context mContext;
     private final int mMaxBitmapSize;
 
     private final Token mSessionToken;
@@ -194,6 +196,7 @@
                     + "parcelables");
         }
 
+        mContext = context;
         mMaxBitmapSize = context.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize);
         mCbStub = new CallbackStub(this);
@@ -277,7 +280,10 @@
      *
      * @param mbr The {@link PendingIntent} to send the media button event to.
      * @see PendingIntent#getActivity
+     *
+     * @deprecated Use {@link #setMediaButtonBroadcastReceiver(ComponentName)} instead.
      */
+    @Deprecated
     public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
         try {
             mBinder.setMediaButtonReceiver(mbr);
@@ -287,6 +293,32 @@
     }
 
     /**
+     * Set the component name of the manifest-declared {@link android.content.BroadcastReceiver}
+     * class that should receive media buttons. This allows restarting playback after the session
+     * has been stopped. If your app is started in this way an {@link Intent#ACTION_MEDIA_BUTTON}
+     * intent will be sent to the broadcast receiver.
+     * <p>
+     * Note: The given {@link android.content.BroadcastReceiver} should belong to the same package
+     * as the context that was given when creating {@link MediaSession}.
+     *
+     * @param broadcastReceiver the component name of the BroadcastReceiver class
+     */
+    public void setMediaButtonBroadcastReceiver(@Nullable ComponentName broadcastReceiver) {
+        try {
+            if (broadcastReceiver != null) {
+                if (!TextUtils.equals(broadcastReceiver.getPackageName(),
+                        mContext.getPackageName())) {
+                    throw new IllegalArgumentException("broadcastReceiver should belong to the same"
+                            + " package as the context given when creating MediaSession.");
+                }
+            }
+            mBinder.setMediaButtonBroadcastReceiver(broadcastReceiver);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Failure in setMediaButtonBroadcastReceiver.", e);
+        }
+    }
+
+    /**
      * Set any flags for the session.
      *
      * @param flags The flags to set for this session.
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index c6c7142..935cb37 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -25,7 +25,10 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -143,16 +146,21 @@
 
                 File file = new File(mPackageURI.getPath());
                 try {
-                    PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);
-                    params.setAppPackageName(pkg.packageName);
-                    params.setInstallLocation(pkg.installLocation);
-                    params.setSize(
-                            PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));
-                } catch (PackageParser.PackageParserException e) {
-                    Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");
-                    Log.e(LOG_TAG,
-                            "Cannot calculate installed size " + file + ". Try only apk size.");
-                    params.setSize(file.length());
+                    final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+                    final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
+                            input.reset(), file, /* flags */ 0);
+                    if (result.isError()) {
+                        Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");
+                        Log.e(LOG_TAG,
+                                "Cannot calculate installed size " + file + ". Try only apk size.");
+                        params.setSize(file.length());
+                    } else {
+                        final PackageLite pkg = result.getResult();
+                        params.setAppPackageName(pkg.getPackageName());
+                        params.setInstallLocation(pkg.getInstallLocation());
+                        params.setSize(
+                                PackageHelper.calculateInstalledSize(pkg, params.abiOverride));
+                    }
                 } catch (IOException e) {
                     Log.e(LOG_TAG,
                             "Cannot calculate installed size " + file + ". Try only apk size.");
diff --git a/packages/SettingsLib/SettingsTheme/res/layout/preference_category_settings.xml b/packages/SettingsLib/SettingsTheme/res/layout/preference_category_settings.xml
index 4b1b255..4a1b089 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout/preference_category_settings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout/preference_category_settings.xml
@@ -19,14 +19,13 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:paddingLeft="24dp"
-    android:paddingStart="24dp"
     android:paddingRight="?android:attr/listPreferredItemPaddingRight"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
     android:background="?android:attr/selectableItemBackground"
     android:baselineAligned="false"
     android:layout_marginTop="16dp"
-    android:gravity="center_vertical">
+    android:gravity="center_vertical"
+    style="@style/PreferenceCategoryStartMargin">
 
     <RelativeLayout
         android:layout_width="0dp"
@@ -57,6 +56,5 @@
             android:textColor="?android:attr/textColorSecondary"
             android:maxLines="10"
             style="@style/PreferenceSummaryTextStyle"/>
-
     </RelativeLayout>
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-sw360dp/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-sw360dp/styles.xml
new file mode 100644
index 0000000..4f40256
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-sw360dp/styles.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <style name="PreferenceCategoryStartMargin">
+        <item name="android:paddingLeft">24dp</item>
+        <item name="android:paddingStart">24dp</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles.xml b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
index 6b285d5..a6623b0 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
@@ -22,4 +22,9 @@
         <!-- 0.8 Spacing, 0.8/11 = 0.072727273 -->
         <item name="android:letterSpacing">0.072727273</item>
     </style>
+
+    <style name="PreferenceCategoryStartMargin">
+        <item name="android:paddingLeft">?android:attr/listPreferredItemPaddingLeft</item>
+        <item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item>
+    </style>
 </resources>
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 5d8fc0b..7eab559 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -300,6 +300,9 @@
     <!-- Permission needed to test mainline permission module rollback -->
     <uses-permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS" />
 
+    <!-- Permission needed to restart WiFi Subsystem -->
+    <uses-permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM" />
+
     <!-- Permission needed to read wifi network credentials for CtsNetTestCases -->
     <uses-permission android:name="android.permission.NETWORK_AIRPLANE_MODE" />
 
diff --git a/packages/SystemUI/res/drawable/stat_sys_roaming_large.xml b/packages/SystemUI/res/drawable/stat_sys_roaming_large.xml
new file mode 100644
index 0000000..1511659
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_roaming_large.xml
@@ -0,0 +1,24 @@
+<!--
+     Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/signal_icon_size"
+        android:height="@dimen/signal_icon_size"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M14.21,12.81c0.36,-0.16 0.69,-0.36 0.97,-0.61c0.41,-0.38 0.72,-0.83 0.94,-1.37c0.21,-0.54 0.32,-1.14 0.32,-1.79c0,-0.92 -0.16,-1.7 -0.49,-2.33c-0.32,-0.64 -0.79,-1.12 -1.43,-1.45c-0.62,-0.33 -1.4,-0.49 -2.32,-0.49H8.23V19h1.8v-5.76h2.5L15.06,19h1.92v-0.12L14.21,12.81zM10.03,11.71V6.32h2.18c0.59,0 1.06,0.11 1.42,0.34c0.36,0.22 0.62,0.54 0.78,0.95c0.16,0.41 0.24,0.89 0.24,1.44c0,0.49 -0.09,0.93 -0.27,1.34c-0.18,0.4 -0.46,0.73 -0.82,0.97c-0.36,0.23 -0.82,0.35 -1.37,0.35H10.03z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/global_screenshot_preview.xml b/packages/SystemUI/res/layout/global_screenshot_preview.xml
index 5262407..100213b 100644
--- a/packages/SystemUI/res/layout/global_screenshot_preview.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_preview.xml
@@ -25,7 +25,7 @@
     android:layout_marginBottom="@dimen/screenshot_offset_y"
     android:scaleType="fitEnd"
     android:elevation="@dimen/screenshot_preview_elevation"
-    android:visibility="gone"
+    android:visibility="invisible"
     android:background="@drawable/screenshot_rounded_corners"
     android:adjustViewBounds="true"
     android:contentDescription="@string/screenshot_edit_label"
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
index bfd079b..5552020 100644
--- a/packages/SystemUI/res/layout/mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/mobile_signal_group.xml
@@ -77,4 +77,11 @@
             android:contentDescription="@string/data_connection_roaming"
             android:visibility="gone" />
     </FrameLayout>
+    <ImageView
+        android:id="@+id/mobile_roaming_large"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/stat_sys_roaming_large"
+        android:contentDescription="@string/data_connection_roaming"
+        android:visibility="gone" />
 </com.android.keyguard.AlphaOptimizedLinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
index 42d541e3..10d49b3 100644
--- a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
@@ -85,6 +85,13 @@
                 android:contentDescription="@string/data_connection_roaming"
                 android:visibility="gone" />
         </FrameLayout>
+        <ImageView
+            android:id="@+id/mobile_roaming_large"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/stat_sys_roaming_large"
+            android:contentDescription="@string/data_connection_roaming"
+            android:visibility="gone" />
     </com.android.keyguard.AlphaOptimizedLinearLayout>
 </com.android.systemui.statusbar.StatusBarMobileView>
 
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 0a17828..09710d7 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -351,7 +351,7 @@
     <bool name="config_showNotificationGear">true</bool>
 
     <!-- Whether or not a background should be drawn behind a notification. -->
-    <bool name="config_drawNotificationBackground">false</bool>
+    <bool name="config_drawNotificationBackground">true</bool>
 
     <!-- Whether or the notifications can be shown and dismissed with a drag. -->
     <bool name="config_enableNotificationShadeDrag">true</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 79cb236..e510930 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -155,7 +155,7 @@
     <dimen name="notification_max_heads_up_height_increased">188dp</dimen>
 
     <!-- Side padding on the lockscreen on the side of notifications -->
-    <dimen name="notification_side_paddings">16dp</dimen>
+    <dimen name="notification_side_paddings">4dp</dimen>
 
     <!-- padding between the heads up and the statusbar -->
     <dimen name="heads_up_status_bar_padding">8dp</dimen>
@@ -177,7 +177,10 @@
     <dimen name="notification_min_interaction_height">40dp</dimen>
 
     <!-- Radius for notifications corners without adjacent notifications -->
-    <dimen name="notification_corner_radius">28dp</dimen>
+    <dimen name="notification_corner_radius">8dp</dimen>
+
+    <!-- Radius for notifications corners with adjacent notifications -->
+    <dimen name="notification_corner_radius_small">0dp</dimen>
 
     <!-- the padding of the shelf icon container -->
     <dimen name="shelf_icon_container_padding">13dp</dimen>
@@ -619,7 +622,7 @@
     <dimen name="z_distance_between_notifications">0.5dp</dimen>
 
     <!-- The height of the divider between the individual notifications. -->
-    <dimen name="notification_divider_height">2dp</dimen>
+    <dimen name="notification_divider_height">1dp</dimen>
 
     <!-- The corner radius of the shadow behind the notification. -->
     <dimen name="notification_shadow_radius">0dp</dimen>
@@ -632,7 +635,7 @@
     <dimen name="notification_children_container_divider_height">0.5dp</dimen>
 
     <!-- The horizontal margin of the content in the notification shade -->
-    <dimen name="notification_shade_content_margin_horizontal">16dp</dimen>
+    <dimen name="notification_shade_content_margin_horizontal">4dp</dimen>
 
     <!-- The top margin for the notification children container in its non-expanded form. -->
     <dimen name="notification_children_container_margin_top">
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 905a575..49f9109 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -16,10 +16,12 @@
   -->
 
 <resources>
-    <bool name="are_flags_overrideable">true</bool>
+    <bool name="are_flags_overrideable">false</bool>
 
     <bool name="flag_notification_pipeline2">false</bool>
     <bool name="flag_notification_pipeline2_rendering">false</bool>
+    <bool name="flag_notif_updates">false</bool>
+
     <bool name="flag_shade_is_opaque">false</bool>
 
     <!-- b/171917882 -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index e5c4bf3..9164137 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -24,6 +24,7 @@
 
 import android.annotation.NonNull;
 import android.app.Activity;
+import android.app.ActivityClient;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RecentTaskInfo;
 import android.app.ActivityManager.RunningTaskInfo;
@@ -140,8 +141,9 @@
      */
     public void invalidateHomeTaskSnapshot(final Activity homeActivity) {
         try {
-            getService().invalidateHomeTaskSnapshot(homeActivity.getActivityToken());
-        } catch (RemoteException e) {
+            ActivityClient.getInstance().invalidateHomeTaskSnapshot(
+                    homeActivity.getActivityToken());
+        } catch (Throwable e) {
             Log.w(TAG, "Failed to invalidate home snapshot", e);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
index 02f34ac..5384ddf 100644
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui;
 
-import android.app.ActivityTaskManager;
+import android.app.ActivityClient;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Color;
@@ -27,7 +27,6 @@
 import android.hardware.display.DisplayManager;
 import android.inputmethodservice.InputMethodService;
 import android.os.IBinder;
-import android.os.RemoteException;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Display;
@@ -237,12 +236,7 @@
 
         @Override
         public void onClick(View v) {
-            try {
-                ActivityTaskManager.getService().restartActivityProcessIfVisible(
-                        mLastActivityToken);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Unable to restart activity", e);
-            }
+            ActivityClient.getInstance().restartActivityProcessIfVisible(mLastActivityToken);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index dc09fa7..b373cff 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -47,6 +47,7 @@
 import com.android.internal.BrightnessSynchronizer;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -71,7 +72,8 @@
  * {@code sensorId} parameters.
  */
 @SuppressWarnings("deprecation")
-class UdfpsController implements DozeReceiver {
+@SysUISingleton
+public class UdfpsController implements DozeReceiver {
     private static final String TAG = "UdfpsController";
     // Gamma approximation for the sRGB color space.
     private static final float DISPLAY_GAMMA = 2.2f;
@@ -177,7 +179,7 @@
     };
 
     @Inject
-    UdfpsController(@NonNull Context context,
+    public UdfpsController(@NonNull Context context,
             @Main Resources resources,
             LayoutInflater inflater,
             @Nullable FingerprintManager fingerprintManager,
@@ -464,7 +466,7 @@
         onFingerUp();
     }
 
-    private void onFingerDown(int x, int y, float minor, float major) {
+    protected void onFingerDown(int x, int y, float minor, float major) {
         if (mHbmSupported) {
             try {
                 FileWriter fw = new FileWriter(mHbmPath);
@@ -482,7 +484,7 @@
         mView.showScrimAndDot();
     }
 
-    private void onFingerUp() {
+    protected void onFingerUp() {
         mFingerprintManager.onPointerUp(mSensorProps.sensorId);
         // Hiding the scrim before disabling HBM results in less noticeable flicker.
         mView.hideScrimAndDot();
@@ -521,4 +523,8 @@
         }
         return normalizedBacklight;
     }
+
+    protected UdfpsView getView() {
+        return mView;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 265703e..96ecc7b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -27,6 +27,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -34,20 +35,20 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Surface;
-import android.view.View;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
 import android.view.ViewTreeObserver;
 
 import com.android.systemui.R;
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.ScrimController;
 
 /**
  * A full screen view with a configurable illumination dot and scrim.
  */
-public class UdfpsView extends View implements DozeReceiver,
-        StatusBarStateController.StateListener,  ScrimController.ScrimChangedListener{
+public class UdfpsView extends SurfaceView implements DozeReceiver,
+        StatusBarStateController.StateListener,  ScrimController.ScrimChangedListener {
     private static final String TAG = "UdfpsView";
 
     // Values in pixels.
@@ -86,6 +87,29 @@
     // The runnable is reset to null after it's executed once.
     @Nullable private Runnable mRunAfterShowingScrimAndDot;
 
+    @NonNull private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
+        @Override
+        public void surfaceCreated(@NonNull SurfaceHolder holder) {
+            Log.d(TAG, "Surface created");
+            // SurfaceView sets this to true by default. We must set it to false to allow
+            // onDraw to be called
+            setWillNotDraw(false);
+        }
+
+        @Override
+        public void surfaceChanged(@NonNull SurfaceHolder holder, int format,
+                int width, int height) {
+
+        }
+
+        @Override
+        public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
+            Log.d(TAG, "Surface destroyed");
+            // Must not draw when the surface is destroyed
+            setWillNotDraw(true);
+        }
+    };
+
     public UdfpsView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -102,6 +126,8 @@
             a.recycle();
         }
 
+        getHolder().addCallback(mSurfaceCallback);
+        getHolder().setFormat(PixelFormat.TRANSLUCENT);
 
         mScrimRect = new Rect();
         mScrimPaint = new Paint(0 /* flags */);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index 39b92dc..7d8d86f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -20,6 +20,7 @@
 import android.content.res.ColorStateList;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.FeatureFlagUtils;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -65,10 +66,13 @@
         super.onFinishInflate();
         mDualToneHandler = new DualToneHandler(getContext());
         mMobileGroup = findViewById(R.id.mobile_combo);
+        if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+            mMobileRoaming = findViewById(R.id.mobile_roaming_large);
+        } else {
+            mMobileRoaming = findViewById(R.id.mobile_roaming);
+        }
         mMobileSignal = findViewById(R.id.mobile_signal);
-        mMobileRoaming = findViewById(R.id.mobile_roaming);
         mCarrierText = findViewById(R.id.qs_carrier_text);
-
         mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
 
         int colorForeground = Utils.getColorAttrDefaultColor(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index ebdcc00..77200cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -71,7 +71,7 @@
                         boolean activityIn, boolean activityOut,
                         CharSequence typeContentDescription,
                         CharSequence typeContentDescriptionHtml, CharSequence description,
-                        boolean isWide, int subId, boolean roaming) {
+                        boolean isWide, int subId, boolean roaming, boolean showTriangle) {
                     int slotIndex = getSlotIndex(subId);
                     if (slotIndex >= SIM_SLOTS) {
                         Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index f742752..720c5dc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -268,7 +268,7 @@
                 int qsType, boolean activityIn, boolean activityOut,
                 CharSequence typeContentDescription,
                 CharSequence typeContentDescriptionHtml, CharSequence description,
-                boolean isWide, int subId, boolean roaming) {
+                boolean isWide, int subId, boolean roaming, boolean showTriangle) {
             if (qsIcon == null) {
                 // Not data sim, don't display.
                 return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 1523278..1b2ad4c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -34,6 +34,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.graph.SignalDrawable;
+import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.settingslib.net.DataUsageController;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -178,6 +179,7 @@
         CharSequence mDataSubscriptionName;
         CharSequence mDataContentDescription;
         int mMobileSignalIconId;
+        int mQsTypeIcon;
         boolean mActivityIn;
         boolean mActivityOut;
         boolean mNoSim;
@@ -194,6 +196,7 @@
                 .append(",mDataSubscriptionName=").append(mDataSubscriptionName)
                 .append(",mDataContentDescription=").append(mDataContentDescription)
                 .append(",mMobileSignalIconId=").append(mMobileSignalIconId)
+                .append(",mQsTypeIcon=").append(mQsTypeIcon)
                 .append(",mActivityIn=").append(mActivityIn)
                 .append(",mActivityOut=").append(mActivityOut)
                 .append(",mNoSim=").append(mNoSim)
@@ -249,7 +252,7 @@
                 int qsType, boolean activityIn, boolean activityOut,
                 CharSequence typeContentDescription,
                 CharSequence typeContentDescriptionHtml, CharSequence description,
-                boolean isWide, int subId, boolean roaming) {
+                boolean isWide, int subId, boolean roaming, boolean showTriangle) {
             if (DEBUG) {
                 Log.d(TAG, "setMobileDataIndicators: "
                         + "statusIcon = " + (statusIcon == null ? "" :  statusIcon.toString()) + ","
@@ -263,7 +266,8 @@
                         + "description = " + description + ","
                         + "isWide = " + isWide + ","
                         + "subId = " + subId + ","
-                        + "roaming = " + roaming);
+                        + "roaming = " + roaming + ","
+                        + "showTriangle = " + showTriangle);
             }
             if (qsIcon == null) {
                 // Not data sim, don't display.
@@ -274,6 +278,7 @@
             mCellularInfo.mDataContentDescription =
                     (description != null) ? typeContentDescriptionHtml : null;
             mCellularInfo.mMobileSignalIconId = qsIcon.icon;
+            mCellularInfo.mQsTypeIcon = qsType;
             mCellularInfo.mActivityIn = activityIn;
             mCellularInfo.mActivityOut = activityOut;
             mCellularInfo.mRoaming = roaming;
@@ -292,6 +297,7 @@
             if (mCellularInfo.mNoSim) {
                 // Make sure signal gets cleared out when no sims.
                 mCellularInfo.mMobileSignalIconId = 0;
+                mCellularInfo.mQsTypeIcon = 0;
             }
             refreshState(mCellularInfo);
         }
@@ -374,6 +380,7 @@
         state.label = r.getString(R.string.quick_settings_internet_label);
         if (cb.mAirplaneModeEnabled) {
             if (!state.value) {
+                state.state = Tile.STATE_INACTIVE;
                 state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_airplane);
                 state.secondaryLabel = r.getString(R.string.status_bar_airplane);
             } else if (!wifiConnected) {
@@ -443,7 +450,8 @@
         state.activityOut = mobileDataEnabled && cb.mActivityOut;
         state.expandedAccessibilityClassName = Switch.class.getName();
 
-        if (cb.mAirplaneModeEnabled && cb.mNoDefaultNetwork) {
+        if (cb.mAirplaneModeEnabled && cb.mQsTypeIcon != TelephonyIcons.ICON_CWF) {
+            state.state = Tile.STATE_INACTIVE;
             state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_airplane);
             state.secondaryLabel = r.getString(R.string.status_bar_airplane);
         } else if (cb.mNoDefaultNetwork && cb.mNoNetworksAvailable) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index b71036c..4431b69 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -49,7 +49,6 @@
 import android.hardware.display.DisplayManager;
 import android.media.MediaActionSound;
 import android.net.Uri;
-import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -67,6 +66,7 @@
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
 import android.view.View;
+import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.view.WindowInsets;
 import android.view.WindowManager;
@@ -80,13 +80,15 @@
 import com.android.internal.policy.PhoneWindow;
 import com.android.settingslib.applications.InterestingConfigChanges;
 import com.android.systemui.R;
-import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
 import com.android.systemui.util.DeviceConfigProxy;
 
 import java.util.List;
 import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 
@@ -173,7 +175,7 @@
     private final UiEventLogger mUiEventLogger;
     private final ImageExporter mImageExporter;
     private final Executor mMainExecutor;
-    private final Executor mBgExecutor;
+    private final ExecutorService mBgExecutor;
 
     private final WindowManager mWindowManager;
     private final WindowManager.LayoutParams mWindowLayoutParams;
@@ -182,17 +184,14 @@
     private final ScrollCaptureClient mScrollCaptureClient;
     private final DeviceConfigProxy mConfigProxy;
     private final PhoneWindow mWindow;
-    private final View mDecorView;
     private final DisplayManager mDisplayManager;
 
-    private final Binder mWindowToken;
     private ScreenshotView mScreenshotView;
     private Bitmap mScreenBitmap;
     private SaveImageInBackgroundTask mSaveInBgTask;
 
     private Animator mScreenshotAnimation;
-
-    private Runnable mOnCompleteRunnable;
+    private RequestCallback mCurrentRequestCallback;
 
     private final Handler mScreenshotHandler = new Handler(Looper.getMainLooper()) {
         @Override
@@ -229,15 +228,14 @@
             UiEventLogger uiEventLogger,
             DeviceConfigProxy configProxy,
             ImageExporter imageExporter,
-            @Main Executor mainExecutor,
-            @Background Executor bgExecutor) {
+            @Main Executor mainExecutor) {
         mScreenshotSmartActions = screenshotSmartActions;
         mNotificationsController = screenshotNotificationsController;
         mScrollCaptureClient = scrollCaptureClient;
         mUiEventLogger = uiEventLogger;
         mImageExporter = imageExporter;
         mMainExecutor = mainExecutor;
-        mBgExecutor = bgExecutor;
+        mBgExecutor = Executors.newSingleThreadExecutor();
 
         mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
         final Context displayContext = context.createDisplayContext(getDefaultDisplay());
@@ -247,9 +245,6 @@
         mAccessibilityManager = AccessibilityManager.getInstance(mContext);
         mConfigProxy = configProxy;
 
-        mWindowToken = new Binder("ScreenshotController");
-        mScrollCaptureClient.setHostWindowToken(mWindowToken);
-
         // Setup the window that we are going to use
         mWindowLayoutParams = new WindowManager.LayoutParams(
                 MATCH_PARENT, MATCH_PARENT, /* xpos */ 0, /* ypos */ 0, TYPE_SCREENSHOT,
@@ -263,7 +258,6 @@
         mWindowLayoutParams.setTitle("ScreenshotAnimation");
         mWindowLayoutParams.layoutInDisplayCutoutMode =
                 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        mWindowLayoutParams.token = mWindowToken;
         // This is needed to let touches pass through outside the touchable areas
         mWindowLayoutParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
 
@@ -272,8 +266,8 @@
         mWindow.requestFeature(Window.FEATURE_NO_TITLE);
         mWindow.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
         mWindow.setBackgroundDrawableResource(android.R.color.transparent);
-        mDecorView = mWindow.getDecorView();
 
+        mConfigChanges.applyNewConfig(context.getResources());
         reloadAssets();
 
         // Setup the Camera shutter sound
@@ -281,9 +275,8 @@
         mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
     }
 
-    void takeScreenshotFullscreen(Consumer<Uri> finisher, Runnable onComplete) {
-        mOnCompleteRunnable = onComplete;
-
+    void takeScreenshotFullscreen(Consumer<Uri> finisher, RequestCallback requestCallback) {
+        mCurrentRequestCallback = requestCallback;
         DisplayMetrics displayMetrics = new DisplayMetrics();
         getDefaultDisplay().getRealMetrics(displayMetrics);
         takeScreenshotInternal(
@@ -293,16 +286,14 @@
 
     void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
             Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
-            Consumer<Uri> finisher, Runnable onComplete) {
+            Consumer<Uri> finisher, RequestCallback requestCallback) {
         // TODO: use task Id, userId, topComponent for smart handler
-        mOnCompleteRunnable = onComplete;
 
         if (screenshot == null) {
             Log.e(TAG, "Got null bitmap from screenshot message");
             mNotificationsController.notifyScreenshotError(
                     R.string.screenshot_failed_to_capture_text);
-            finisher.accept(null);
-            mOnCompleteRunnable.run();
+            requestCallback.reportError();
             return;
         }
 
@@ -312,17 +303,19 @@
             visibleInsets = Insets.NONE;
             screenshotScreenBounds.set(0, 0, screenshot.getWidth(), screenshot.getHeight());
         }
+        mCurrentRequestCallback = requestCallback;
         saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, showFlash);
     }
 
     /**
      * Displays a screenshot selector
      */
-    void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
-        dismissScreenshot(true);
-        mOnCompleteRunnable = onComplete;
+    void takeScreenshotPartial(final Consumer<Uri> finisher, RequestCallback requestCallback) {
+        mScreenshotView.reset();
+        mCurrentRequestCallback = requestCallback;
 
-        mWindowManager.addView(mScreenshotView, mWindowLayoutParams);
+        attachWindow();
+        mWindow.setContentView(mScreenshotView);
 
         mScreenshotView.takePartialScreenshot(
                 rect -> takeScreenshotInternal(finisher, rect));
@@ -343,9 +336,9 @@
             }
             return;
         }
-        mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
+        cancelTimeout();
         if (immediate) {
-            resetScreenshotView();
+            finishDismiss();
         } else {
             mScreenshotView.animateDismissal();
         }
@@ -360,6 +353,8 @@
      */
     void releaseContext() {
         mContext.release();
+        mCameraSound.release();
+        mBgExecutor.shutdownNow();
     }
 
     /**
@@ -369,12 +364,8 @@
         if (DEBUG_UI) {
             Log.d(TAG, "reloadAssets()");
         }
-        boolean wasAttached = mDecorView.isAttachedToWindow();
-        if (wasAttached) {
-            if (DEBUG_WINDOW) {
-                Log.d(TAG, "Removing screenshot window");
-            }
-            mWindowManager.removeView(mDecorView);
+        if (mScreenshotView != null && mScreenshotView.isAttachedToWindow()) {
+            mWindow.clearContentView(); // Is there a simpler way to say "remove screenshotView?"
         }
 
         // respect the display cutout in landscape (since we'd otherwise overlap) but not portrait
@@ -382,12 +373,6 @@
         mWindowLayoutParams.setFitInsetsTypes(
                 orientation == ORIENTATION_PORTRAIT ? 0 : WindowInsets.Type.displayCutout());
 
-        // ignore system bar insets for the purpose of window layout
-        mDecorView.setOnApplyWindowInsetsListener((v, insets) -> v.onApplyWindowInsets(
-                new WindowInsets.Builder(insets)
-                        .setInsets(WindowInsets.Type.all(), Insets.NONE)
-                        .build()));
-
         // Inflate the screenshot layout
         mScreenshotView = (ScreenshotView)
                 LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null);
@@ -399,12 +384,18 @@
 
             @Override
             public void onDismiss() {
-                resetScreenshotView();
+                finishDismiss();
             }
         });
 
+        // ignore system bar insets for the purpose of window layout
+        mScreenshotView.setOnApplyWindowInsetsListener((v, insets) -> v.onApplyWindowInsets(
+                new WindowInsets.Builder(insets)
+                        .setInsets(WindowInsets.Type.all(), Insets.NONE)
+                        .build()));
+
         // TODO(159460485): Remove this when focus is handled properly in the system
-        mDecorView.setOnTouchListener((v, event) -> {
+        mScreenshotView.setOnTouchListener((v, event) -> {
             if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
                 if (DEBUG_INPUT) {
                     Log.d(TAG, "onTouch: ACTION_OUTSIDE");
@@ -426,8 +417,10 @@
             return false;
         });
 
-        // view is added to window manager in startAnimation
-        mWindow.setContentView(mScreenshotView, mWindowLayoutParams);
+        if (DEBUG_WINDOW) {
+            Log.d(TAG, "adding OnComputeInternalInsetsListener");
+        }
+        mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(mScreenshotView);
     }
 
     /**
@@ -464,14 +457,9 @@
             Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
             mNotificationsController.notifyScreenshotError(
                     R.string.screenshot_failed_to_capture_text);
-            if (DEBUG_CALLBACK) {
-                Log.d(TAG, "Supplying null to Consumer<Uri>");
+            if (mCurrentRequestCallback != null) {
+                mCurrentRequestCallback.reportError();
             }
-            finisher.accept(null);
-            if (DEBUG_CALLBACK) {
-                Log.d(TAG, "Calling mOnCompleteRunnable.run()");
-            }
-            mOnCompleteRunnable.run();
             return;
         }
 
@@ -488,6 +476,13 @@
             mAccessibilityManager.sendAccessibilityEvent(event);
         }
 
+        if (mConfigChanges.applyNewConfig(mContext.getResources())) {
+            if (DEBUG_UI) {
+                Log.d(TAG, "saveScreenshot: reloading assets");
+            }
+            reloadAssets();
+        }
+
         if (mScreenshotView.isAttachedToWindow()) {
             // if we didn't already dismiss for another reason
             if (!mScreenshotView.isDismissing()) {
@@ -514,33 +509,101 @@
         mScreenBitmap.setHasAlpha(false);
         mScreenBitmap.prepareToDraw();
 
-        if (mConfigChanges.applyNewConfig(mContext.getResources())) {
-            if (DEBUG_UI) {
-                Log.d(TAG, "saveScreenshot: reloading assets");
-            }
-            reloadAssets();
-        }
+        saveScreenshotInWorkerThread(finisher, this::showUiOnActionsReady);
 
         // The window is focusable by default
         setWindowFocusable(true);
 
-        // Start the post-screenshot animation
-        startAnimation(finisher, screenRect, screenInsets, showFlash);
-
         if (mConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.SCREENSHOT_SCROLLING_ENABLED, false)) {
-            mScrollCaptureClient.request(DEFAULT_DISPLAY, (connection) ->
-                    mScreenshotView.showScrollChip(() ->
-                            runScrollCapture(connection,
-                                    () -> mScreenshotHandler.post(
-                                            () -> dismissScreenshot(false)))));
+            View decorView = mWindow.getDecorView();
+
+            // Wait until this window is attached to request because it is
+            // the reference used to locate the target window (below).
+            withWindowAttached(() -> {
+                mScrollCaptureClient.setHostWindowToken(decorView.getWindowToken());
+                mScrollCaptureClient.request(DEFAULT_DISPLAY,
+                        /* onConnection */
+                        (connection) -> mScreenshotHandler.post(() ->
+                                mScreenshotView.showScrollChip(() ->
+                                        /* onClick */
+                                        runScrollCapture(connection))));
+            });
+        }
+
+
+        attachWindow();
+        if (DEBUG_WINDOW) {
+            Log.d(TAG, "setContentView: " + mScreenshotView);
+        }
+        mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        if (DEBUG_WINDOW) {
+                            Log.d(TAG, "onPreDraw: startAnimation");
+                        }
+                        mScreenshotView.getViewTreeObserver().removeOnPreDrawListener(this);
+                        startAnimation(screenRect, showFlash);
+                        return true;
+                    }
+                });
+        mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
+        setContentView(mScreenshotView);
+        cancelTimeout(); // restarted after animation
+    }
+
+    private void withWindowAttached(Runnable action) {
+        View decorView = mWindow.getDecorView();
+        if (decorView.isAttachedToWindow()) {
+            action.run();
+        } else {
+            decorView.getViewTreeObserver().addOnWindowAttachListener(
+                    new ViewTreeObserver.OnWindowAttachListener() {
+                        @Override
+                        public void onWindowAttached() {
+                            decorView.getViewTreeObserver().removeOnWindowAttachListener(this);
+                            action.run();
+                        }
+
+                        @Override
+                        public void onWindowDetached() { }
+                    });
+
         }
     }
 
-    private void runScrollCapture(ScrollCaptureClient.Connection connection, Runnable andThen) {
+    private void setContentView(View contentView) {
+        mWindow.setContentView(contentView);
+    }
+
+    private void attachWindow() {
+        View decorView = mWindow.getDecorView();
+        if (decorView.isAttachedToWindow()) {
+            return;
+        }
+        if (DEBUG_WINDOW) {
+            Log.d(TAG, "attachWindow");
+        }
+        mWindowManager.addView(decorView, mWindowLayoutParams);
+        decorView.requestApplyInsets();
+    }
+
+    void removeWindow() {
+        final View decorView = mWindow.peekDecorView();
+        if (decorView != null && decorView.isAttachedToWindow()) {
+            if (DEBUG_WINDOW) {
+                Log.d(TAG, "Removing screenshot window");
+            }
+            mWindowManager.removeViewImmediate(decorView);
+        }
+    }
+
+    private void runScrollCapture(ScrollCaptureClient.Connection connection) {
+        cancelTimeout();
         ScrollCaptureController controller = new ScrollCaptureController(mContext, connection,
                 mMainExecutor, mBgExecutor, mImageExporter);
-        controller.run(andThen);
+        controller.start(/* onDismiss */ () -> dismissScreenshot(false));
     }
 
     /**
@@ -549,11 +612,11 @@
      */
     private void saveScreenshotAndToast(Consumer<Uri> finisher) {
         // Play the shutter sound to notify that we've taken a screenshot
-        mScreenshotHandler.post(() -> {
-            mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
-        });
+        mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
 
-        saveScreenshotInWorkerThread(finisher, imageData -> {
+        saveScreenshotInWorkerThread(
+                /* onComplete */ finisher,
+                /* actionsReadyListener */ imageData -> {
             if (DEBUG_CALLBACK) {
                 Log.d(TAG, "returning URI to finisher (Consumer<URI>): " + imageData.uri);
             }
@@ -573,55 +636,35 @@
     /**
      * Starts the animation after taking the screenshot
      */
-    private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
-            boolean showFlash) {
-        mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
-        mScreenshotHandler.post(() -> {
-            if (!mScreenshotView.isAttachedToWindow()) {
-                if (DEBUG_WINDOW) {
-                    Log.d(TAG, "Adding screenshot window");
-                }
-                mWindowManager.addView(mWindow.getDecorView(), mWindowLayoutParams);
-            }
+    private void startAnimation(Rect screenRect, boolean showFlash) {
+        if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
+            mScreenshotAnimation.cancel();
+        }
 
-            mScreenshotView.prepareForAnimation(mScreenBitmap, screenInsets);
+        mScreenshotAnimation =
+                mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash);
 
-            mScreenshotHandler.post(() -> {
-                if (DEBUG_WINDOW) {
-                    Log.d(TAG, "adding OnComputeInternalInsetsListener");
-                }
-                mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(
-                        mScreenshotView);
+        // Play the shutter sound to notify that we've taken a screenshot
+        mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
 
-                mScreenshotAnimation =
-                        mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash);
-
-                saveScreenshotInWorkerThread(finisher, this::showUiOnActionsReady);
-
-                // Play the shutter sound to notify that we've taken a screenshot
-                mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
-
-                if (DEBUG_ANIM) {
-                    Log.d(TAG, "starting post-screenshot animation");
-                }
-                mScreenshotAnimation.start();
-            });
-        });
+        if (DEBUG_ANIM) {
+            Log.d(TAG, "starting post-screenshot animation");
+        }
+        mScreenshotAnimation.start();
     }
 
     /** Reset screenshot view and then call onCompleteRunnable */
-    private void resetScreenshotView() {
+    private void finishDismiss() {
         if (DEBUG_UI) {
-            Log.d(TAG, "resetScreenshotView");
+            Log.d(TAG, "finishDismiss");
         }
-        if (mScreenshotView.isAttachedToWindow()) {
-            if (DEBUG_WINDOW) {
-                Log.d(TAG, "Removing screenshot window");
-            }
-            mWindowManager.removeView(mDecorView);
-        }
+        cancelTimeout();
+        removeWindow();
         mScreenshotView.reset();
-        mOnCompleteRunnable.run();
+        if (mCurrentRequestCallback != null) {
+            mCurrentRequestCallback.onFinish();
+            mCurrentRequestCallback = null;
+        }
     }
 
     /**
@@ -645,8 +688,12 @@
         mSaveInBgTask.execute();
     }
 
-    private void resetTimeout() {
+    private void cancelTimeout() {
         mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
+    }
+
+    private void resetTimeout() {
+        cancelTimeout();
 
         AccessibilityManager accessibilityManager = (AccessibilityManager)
                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
@@ -705,7 +752,7 @@
 
                 @Override
                 public void hideSharedElements() {
-                    resetScreenshotView();
+                    finishDismiss();
                 }
 
                 @Override
@@ -753,13 +800,21 @@
         if (DEBUG_WINDOW) {
             Log.d(TAG, "setWindowFocusable: " + focusable);
         }
+        int flags = mWindowLayoutParams.flags;
         if (focusable) {
             mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
         } else {
             mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
         }
-        if (mDecorView.isAttachedToWindow()) {
-            mWindowManager.updateViewLayout(mDecorView, mWindowLayoutParams);
+        if (mWindowLayoutParams.flags == flags) {
+            if (DEBUG_WINDOW) {
+                Log.d(TAG, "setWindowFocusable: skipping, already " + focusable);
+            }
+            return;
+        }
+        final View decorView = mWindow.peekDecorView();
+        if (decorView != null && decorView.isAttachedToWindow()) {
+            mWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 211f507..bf86b68 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -316,21 +316,21 @@
         mScreenshotSelectorView.requestFocus();
     }
 
-    void prepareForAnimation(Bitmap bitmap, Insets screenInsets) {
+    void setScreenshot(Bitmap bitmap, Insets screenInsets) {
         mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, bitmap, screenInsets));
-        // make static preview invisible (from gone) so we can query its location on screen
-        mScreenshotPreview.setVisibility(View.INVISIBLE);
     }
 
     AnimatorSet createScreenshotDropInAnimation(Rect bounds, boolean showFlash) {
-        mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-        mScreenshotPreview.buildLayer();
+        if (DEBUG_ANIM) {
+            Log.d(TAG, "createAnim: bounds=" + bounds + " showFlash=" + showFlash);
+        }
 
         Rect previewBounds = new Rect();
         mScreenshotPreview.getBoundsOnScreen(previewBounds);
-        int[] previewLocation = new int[2];
-        mScreenshotPreview.getLocationInWindow(previewLocation);
+        Rect targetPosition = new Rect();
+        mScreenshotPreview.getHitRect(targetPosition);
 
+        // ratio of preview width, end vs. start size
         float cornerScale =
                 mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height());
         final float currentScale = 1 / cornerScale;
@@ -358,8 +358,13 @@
 
         // animate from the current location, to the static preview location
         final PointF startPos = new PointF(bounds.centerX(), bounds.centerY());
-        final PointF finalPos = new PointF(previewLocation[0] + previewBounds.width() / 2f,
-                previewLocation[1] + previewBounds.height() / 2f);
+        final PointF finalPos = new PointF(targetPosition.exactCenterX(),
+                targetPosition.exactCenterY());
+
+        if (DEBUG_ANIM) {
+            Log.d(TAG, "toCorner: startPos=" + startPos);
+            Log.d(TAG, "toCorner: finalPos=" + finalPos);
+        }
 
         ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1);
         toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
@@ -427,7 +432,7 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 if (DEBUG_ANIM) {
-                    Log.d(TAG, "drop-in animation completed");
+                    Log.d(TAG, "drop-in animation ended");
                 }
                 mDismissButton.setOnClickListener(view -> {
                     if (DEBUG_INPUT) {
@@ -653,13 +658,12 @@
         getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
         // Clear any references to the bitmap
         mScreenshotPreview.setImageDrawable(null);
+        mScreenshotPreview.setVisibility(View.INVISIBLE);
         mPendingSharedTransition = false;
         mActionsContainerBackground.setVisibility(View.GONE);
         mActionsContainer.setVisibility(View.GONE);
         mBackgroundProtection.setAlpha(0f);
         mDismissButton.setVisibility(View.GONE);
-        mScreenshotPreview.setVisibility(View.GONE);
-        mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
         mScreenshotStatic.setTranslationX(0);
         mScreenshotPreview.setTranslationY(0);
         mScreenshotPreview.setContentDescription(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 54b1b2c..825c857 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -72,7 +72,7 @@
      *
      * @param after action to take after the flow is complete
      */
-    public void run(final Runnable after) {
+    public void start(final Runnable after) {
         mCaptureTime = ZonedDateTime.now();
         mRequestId = UUID.randomUUID();
         mConnection.start((session) -> startCapture(session, after));
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 144ad39..daa9d09 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -25,6 +25,7 @@
 import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE;
 import static com.android.systemui.screenshot.LogConfig.logTag;
 
+import android.annotation.MainThread;
 import android.app.Service;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -59,7 +60,8 @@
 public class TakeScreenshotService extends Service {
     private static final String TAG = logTag(TakeScreenshotService.class);
 
-    private final ScreenshotController mScreenshot;
+    private ScreenshotController mScreenshot;
+
     private final UserManager mUserManager;
     private final UiEventLogger mUiEventLogger;
     private final ScreenshotNotificationsController mNotificationsController;
@@ -79,6 +81,15 @@
         }
     };
 
+    /** Informs about coarse grained state of the Controller. */
+    interface RequestCallback {
+        /** Respond to the current request indicating the screenshot request failed.*/
+        void reportError();
+
+        /** The controller has completed handling this request UI has been removed */
+        void onFinish();
+    }
+
     @Inject
     public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager,
             UiEventLogger uiEventLogger,
@@ -116,7 +127,8 @@
             Log.d(TAG, "onUnbind");
         }
         if (mScreenshot != null) {
-            mScreenshot.dismissScreenshot(true);
+            mScreenshot.removeWindow();
+            mScreenshot = null;
         }
         unregisterReceiver(mCloseSystemDialogs);
         return false;
@@ -126,18 +138,39 @@
     public void onDestroy() {
         super.onDestroy();
         if (mScreenshot != null) {
+            mScreenshot.removeWindow();
             mScreenshot.releaseContext();
+            mScreenshot = null;
         }
         if (DEBUG_SERVICE) {
             Log.d(TAG, "onDestroy");
         }
     }
 
+    static class RequestCallbackImpl implements RequestCallback {
+        private final Messenger mReplyTo;
+
+        RequestCallbackImpl(Messenger replyTo) {
+            mReplyTo = replyTo;
+        }
+
+        public void reportError() {
+            reportUri(mReplyTo, null);
+            sendComplete(mReplyTo);
+        }
+
+        @Override
+        public void onFinish() {
+            sendComplete(mReplyTo);
+        }
+    }
+
     /** Respond to incoming Message via Binder (Messenger) */
+    @MainThread
     private boolean handleMessage(Message msg) {
         final Messenger replyTo = msg.replyTo;
-        final Runnable onComplete = () -> sendComplete(replyTo);
         final Consumer<Uri> uriConsumer = (uri) -> reportUri(replyTo, uri);
+        RequestCallback requestCallback = new RequestCallbackImpl(replyTo);
 
         // If the storage for this user is locked, we have no place to store
         // the screenshot, so skip taking it instead of showing a misleading
@@ -146,14 +179,7 @@
             Log.w(TAG, "Skipping screenshot because storage is locked!");
             mNotificationsController.notifyScreenshotError(
                     R.string.screenshot_failed_to_save_user_locked_text);
-            if (DEBUG_CALLBACK) {
-                Log.d(TAG, "handleMessage: calling uriConsumer.accept(null)");
-            }
-            uriConsumer.accept(null);
-            if (DEBUG_CALLBACK) {
-                Log.d(TAG, "handleMessage: calling onComplete.run()");
-            }
-            onComplete.run();
+            requestCallback.reportError();
             return true;
         }
 
@@ -167,13 +193,13 @@
                 if (DEBUG_SERVICE) {
                     Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_FULLSCREEN");
                 }
-                mScreenshot.takeScreenshotFullscreen(uriConsumer, onComplete);
+                mScreenshot.takeScreenshotFullscreen(uriConsumer, requestCallback);
                 break;
             case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
                 if (DEBUG_SERVICE) {
                     Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_SELECTED_REGION");
                 }
-                mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);
+                mScreenshot.takeScreenshotPartial(uriConsumer, requestCallback);
                 break;
             case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
                 if (DEBUG_SERVICE) {
@@ -186,8 +212,16 @@
                 int taskId = screenshotRequest.getTaskId();
                 int userId = screenshotRequest.getUserId();
                 ComponentName topComponent = screenshotRequest.getTopComponent();
-                mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
-                        taskId, userId, topComponent, uriConsumer, onComplete);
+
+                if (screenshot == null) {
+                    Log.e(TAG, "Got null bitmap from screenshot message");
+                    mNotificationsController.notifyScreenshotError(
+                            R.string.screenshot_failed_to_capture_text);
+                    requestCallback.reportError();
+                } else {
+                    mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
+                            taskId, userId, topComponent, uriConsumer, requestCallback);
+                }
                 break;
             default:
                 Log.w(TAG, "Invalid screenshot option: " + msg.what);
@@ -196,7 +230,7 @@
         return true;
     };
 
-    private void sendComplete(Messenger target) {
+    private static void sendComplete(Messenger target) {
         try {
             if (DEBUG_CALLBACK) {
                 Log.d(TAG, "sendComplete: " + target);
@@ -207,7 +241,7 @@
         }
     }
 
-    private void reportUri(Messenger target, Uri uri) {
+    private static void reportUri(Messenger target, Uri uri) {
         try {
             if (DEBUG_CALLBACK) {
                 Log.d(TAG, "reportUri: " + target + " -> " + uri);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index fdb793e..924eb26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -131,6 +131,7 @@
      */
     public void removeRemoteInput(NotificationEntry entry, Object token) {
         Objects.requireNonNull(entry);
+        if (entry.mRemoteEditImeVisible) return;
 
         pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */, token);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index 239addd..d562726 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -27,6 +27,7 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.FeatureFlagUtils;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -64,7 +65,6 @@
         LayoutInflater inflater = LayoutInflater.from(context);
         StatusBarMobileView v = (StatusBarMobileView)
                 inflater.inflate(R.layout.status_bar_mobile_signal_group, null);
-
         v.setSlot(slot);
         v.init();
         v.setVisibleState(STATE_ICON);
@@ -104,7 +104,11 @@
         mMobileGroup = findViewById(R.id.mobile_group);
         mMobile = findViewById(R.id.mobile_signal);
         mMobileType = findViewById(R.id.mobile_type);
-        mMobileRoaming = findViewById(R.id.mobile_roaming);
+        if (FeatureFlagUtils.isEnabled(getContext(), FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+            mMobileRoaming = findViewById(R.id.mobile_roaming_large);
+        } else {
+            mMobileRoaming = findViewById(R.id.mobile_roaming);
+        }
         mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space);
         mIn = findViewById(R.id.mobile_in);
         mOut = findViewById(R.id.mobile_out);
@@ -160,7 +164,7 @@
         } else {
             mMobileType.setVisibility(View.GONE);
         }
-
+        mMobile.setVisibility(mState.showTriangle ? View.VISIBLE : View.GONE);
         mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
         mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
         mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
@@ -191,6 +195,7 @@
             }
         }
 
+        mMobile.setVisibility(state.showTriangle ? View.VISIBLE : View.GONE);
         mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
         mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
         mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
@@ -200,7 +205,8 @@
 
         needsLayout |= state.roaming != mState.roaming
                 || state.activityIn != mState.activityIn
-                || state.activityOut != mState.activityOut;
+                || state.activityOut != mState.activityOut
+                || state.showTriangle != mState.showTriangle;
 
         mState = state;
         return needsLayout;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index bafa4a25..8a22b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -179,6 +179,8 @@
     private boolean mShelfIconVisible;
     private boolean mIsAlerting;
 
+    public boolean mRemoteEditImeVisible;
+
     /**
      * @param sbn the StatusBarNotification from system server
      * @param ranking also from system server
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 596aea0..2b12119 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -302,8 +302,12 @@
         } else {
             mMenuContainer = new FrameLayout(mContext);
         }
-        final boolean newFlowHideShelf = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.SHOW_NEW_NOTIF_DISMISS, 1 /* on by default */) == 1;
+        // The setting can win (which is needed for tests) but if not set, then use the flag
+        final int showDismissSetting =  Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.SHOW_NEW_NOTIF_DISMISS, -1);
+        final boolean newFlowHideShelf = showDismissSetting == -1
+                ? mContext.getResources().getBoolean(R.bool.flag_notif_updates)
+                : showDismissSetting == 1;
         if (newFlowHideShelf) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 079cf77..45f5b31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -16,8 +16,10 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
+import android.content.res.Resources;
 import android.util.MathUtils;
 
+import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -45,11 +47,6 @@
     private ExpandableNotificationRow mTrackedHeadsUp;
     private float mAppearFraction;
 
-    // Radius for notification corners WITH adjacent notifications
-    // as percent of radius WITHOUT adjacent notifications.
-    // TODO(b/175710408) pull from dimens and hide from beta builds.
-    static final float SMALL_CORNER_RADIUS = 4f/28;
-
     @Inject
     NotificationRoundnessManager(
             KeyguardBypassController keyguardBypassController,
@@ -128,7 +125,9 @@
         if (view.showingPulsing() && !mBypassController.getBypassEnabled()) {
             return 1.0f;
         }
-        return SMALL_CORNER_RADIUS;
+        final Resources resources = view.getResources();
+        return resources.getDimension(R.dimen.notification_corner_radius_small)
+                / resources.getDimension(R.dimen.notification_corner_radius);
     }
 
     public void setExpanded(float expandedHeight, float appearFraction) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index f07d874..3f3be44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -41,8 +41,6 @@
 import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.PointF;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -503,7 +501,8 @@
         mSections = mSectionsManager.createSectionsForBuckets();
 
         mAmbientState = new AmbientState(context, mSectionsManager);
-        mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground).getDefaultColor();
+        mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackgroundFloating)
+                .getDefaultColor();
         int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
         int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
         mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback,
@@ -623,7 +622,8 @@
     }
 
     void updateBgColor() {
-        mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground).getDefaultColor();
+        mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackgroundFloating)
+                .getDefaultColor();
         updateBackgroundDimming();
         mShelf.onUiModeChanged();
     }
@@ -2282,7 +2282,8 @@
             if (child.getVisibility() != View.GONE
                     && !(child instanceof StackScrollerDecorView)
                     && child != mShelf
-                    && mSwipeHelper.getSwipedView() != child) {
+                    && (mSwipeHelper.getSwipedView() != child
+                        || !child.getResources().getBoolean(R.bool.flag_notif_updates))) {
                 children.add(child);
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index d11e864..5f90077 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -221,7 +221,7 @@
             int qsType, boolean activityIn, boolean activityOut,
             CharSequence typeContentDescription,
             CharSequence typeContentDescriptionHtml, CharSequence description,
-            boolean isWide, int subId, boolean roaming) {
+            boolean isWide, int subId, boolean roaming, boolean showTriangle) {
         if (DEBUG) {
             Log.d(TAG, "setMobileDataIndicators: "
                     + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + ","
@@ -235,7 +235,8 @@
                     + "description = " + description + ","
                     + "isWide = " + isWide + ","
                     + "subId = " + subId + ","
-                    + "roaming = " + roaming);
+                    + "roaming = " + roaming + ","
+                    + "showTriangle = " + showTriangle);
         }
         MobileIconState state = getState(subId);
         if (state == null) {
@@ -250,6 +251,7 @@
         state.typeId = statusType;
         state.contentDescription = statusIcon.contentDescription;
         state.typeContentDescription = typeContentDescription;
+        state.showTriangle = showTriangle;
         state.roaming = roaming;
         state.activityIn = activityIn && mActivityEnabled;
         state.activityOut = activityOut && mActivityEnabled;
@@ -551,6 +553,7 @@
         public int subId;
         public int strengthId;
         public int typeId;
+        public boolean showTriangle;
         public boolean roaming;
         public boolean needsLeadingPadding;
         public CharSequence typeContentDescription;
@@ -569,20 +572,21 @@
                 return false;
             }
             MobileIconState that = (MobileIconState) o;
-            return subId == that.subId &&
-                    strengthId == that.strengthId &&
-                    typeId == that.typeId &&
-                    roaming == that.roaming &&
-                    needsLeadingPadding == that.needsLeadingPadding &&
-                    Objects.equals(typeContentDescription, that.typeContentDescription);
+            return subId == that.subId
+                    && strengthId == that.strengthId
+                    && typeId == that.typeId
+                    && showTriangle == that.showTriangle
+                    && roaming == that.roaming
+                    && needsLeadingPadding == that.needsLeadingPadding
+                    && Objects.equals(typeContentDescription, that.typeContentDescription);
         }
 
         @Override
         public int hashCode() {
 
             return Objects
-                    .hash(super.hashCode(), subId, strengthId, typeId, roaming, needsLeadingPadding,
-                            typeContentDescription);
+                    .hash(super.hashCode(), subId, strengthId, typeId, showTriangle, roaming,
+                            needsLeadingPadding, typeContentDescription);
         }
 
         public MobileIconState copy() {
@@ -596,6 +600,7 @@
             other.subId = subId;
             other.strengthId = strengthId;
             other.typeId = typeId;
+            other.showTriangle = showTriangle;
             other.roaming = roaming;
             other.needsLeadingPadding = needsLeadingPadding;
             other.typeContentDescription = typeContentDescription;
@@ -613,8 +618,9 @@
         }
 
         @Override public String toString() {
-            return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + ", roaming="
-                    + roaming + ", typeId=" + typeId + ", visible=" + visible + ")";
+            return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId
+                    + ", showTriangle=" + showTriangle + ", roaming=" + roaming
+                    + ", typeId=" + typeId + ", visible=" + visible + ")";
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index 5e88cd5..08a4492 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -124,12 +124,13 @@
             final int statusType, final int qsType, final boolean activityIn,
             final boolean activityOut, final CharSequence typeContentDescription,
             CharSequence typeContentDescriptionHtml, final CharSequence description,
-            final boolean isWide, final int subId, boolean roaming) {
+            final boolean isWide, final int subId, boolean roaming, boolean showTriangle) {
         post(() -> {
             for (SignalCallback signalCluster : mSignalCallbacks) {
                 signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType,
                         activityIn, activityOut, typeContentDescription,
-                        typeContentDescriptionHtml, description, isWide, subId, roaming);
+                        typeContentDescriptionHtml, description, isWide, subId, roaming,
+                        showTriangle);
             }
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 2f66508..39472de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -284,13 +284,15 @@
                     && !mCurrentState.carrierNetworkChangeMode
                     && mCurrentState.activityOut;
             showDataIcon &= mCurrentState.dataSim && mCurrentState.isDefault;
+            boolean showTriangle = showDataIcon && !mCurrentState.airplaneMode;
+            int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
+            showDataIcon |= mCurrentState.roaming;
             IconState statusIcon = new IconState(showDataIcon && !mCurrentState.airplaneMode,
                     getCurrentIconId(), contentDescription);
-            int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
             callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
                     activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
                     description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
-                    mCurrentState.roaming);
+                    mCurrentState.roaming, showTriangle);
         } else {
             boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
             IconState statusIcon = new IconState(
@@ -316,10 +318,11 @@
                     && mCurrentState.activityOut;
             showDataIcon &= mCurrentState.isDefault || dataDisabled;
             int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
+            boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
             callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
                     activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
                     description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
-                    mCurrentState.roaming);
+                    mCurrentState.roaming, showTriangle);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index f2b0d76..e60d5c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -66,12 +66,13 @@
          * @param isWide //TODO: unused?
          * @param subId subscription ID for which to update the UI
          * @param roaming indicates roaming
+         * @param showTriangle whether to show the mobile triangle the in status bar
          */
         default void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
                 int qsType, boolean activityIn, boolean activityOut,
                 CharSequence typeContentDescription,
                 CharSequence typeContentDescriptionHtml, CharSequence description,
-                boolean isWide, int subId, boolean roaming) {
+                boolean isWide, int subId, boolean roaming, boolean showTriangle) {
         }
 
         default void setSubs(List<SubscriptionInfo> subs) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 6c5251b..9380d91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.annotation.Nullable;
@@ -50,6 +52,8 @@
 import android.view.View;
 import android.view.ViewAnimationUtils;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.EditorInfo;
@@ -61,6 +65,8 @@
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.statusbar.IStatusBarService;
@@ -76,6 +82,7 @@
 
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.function.Consumer;
 
 /**
@@ -135,6 +142,27 @@
 
         mEditText = (RemoteEditText) getChildAt(0);
         mEditText.setInnerFocusable(false);
+        mEditText.setWindowInsetsAnimationCallback(
+                new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+            @NonNull
+            @Override
+            public WindowInsets onProgress(@NonNull WindowInsets insets,
+                    @NonNull List<WindowInsetsAnimation> runningAnimations) {
+                return insets;
+            }
+
+            @Override
+            public void onEnd(@NonNull WindowInsetsAnimation animation) {
+                super.onEnd(animation);
+                if (animation.getTypeMask() == WindowInsets.Type.ime()) {
+                    mEntry.mRemoteEditImeVisible =
+                            mEditText.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
+                    if (!mEntry.mRemoteEditImeVisible && !mEditText.mShowImeOnInputConnection) {
+                        mController.removeRemoteInput(mEntry, mToken);
+                    }
+                }
+            }
+        });
     }
 
     protected Intent prepareRemoteInputFromText() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 7042e2f..9669522 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -150,7 +150,7 @@
         callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
                 mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription,
                 dataContentDescriptionHtml, description, icons.isWide,
-                mCurrentState.subId, /* roaming= */ false);
+                mCurrentState.subId, /* roaming= */ false, /* showTriangle= */ true);
     }
 
     private int getCurrentIconIdForCarrierWifi() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 8a412bf..b452d3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -218,7 +218,7 @@
         mSignalCallback.setMobileDataIndicators(
                 mock(NetworkController.IconState.class),
                 mock(NetworkController.IconState.class),
-                0, 0, true, true, "", "", "", true, 0, true);
+                0, 0, true, true, "", "", "", true, 0, true, true);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 63bfd6a..919ddcb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager.SMALL_CORNER_RADIUS;
-
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -25,12 +23,14 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.res.Resources;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -61,10 +61,14 @@
     private ExpandableNotificationRow mSecond;
     @Mock
     private KeyguardBypassController mBypassController;
+    private float mSmallRadiusRatio;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        final Resources resources = mContext.getResources();
+        mSmallRadiusRatio = resources.getDimension(R.dimen.notification_corner_radius_small)
+                / resources.getDimension(R.dimen.notification_corner_radius);
         mRoundnessManager = new NotificationRoundnessManager(
                 mBypassController,
                 new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext));
@@ -141,7 +145,7 @@
                 createSection(null, null)
         });
         Assert.assertEquals(1.0f, mSecond.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(SMALL_CORNER_RADIUS, mSecond.getCurrentTopRoundness(), 0.0f);
+        Assert.assertEquals(mSmallRadiusRatio, mSecond.getCurrentTopRoundness(), 0.0f);
     }
 
     @Test
@@ -168,8 +172,8 @@
 
         row.setHeadsUp(false);
         mRoundnessManager.updateView(entry.getRow(), false);
-        Assert.assertEquals(SMALL_CORNER_RADIUS, row.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(SMALL_CORNER_RADIUS, row.getCurrentTopRoundness(), 0.0f);
+        Assert.assertEquals(mSmallRadiusRatio, row.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(mSmallRadiusRatio, row.getCurrentTopRoundness(), 0.0f);
     }
 
     @Test
@@ -179,7 +183,7 @@
                 createSection(null, mSecond)
         });
         Assert.assertEquals(1.0f, mSecond.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(SMALL_CORNER_RADIUS, mSecond.getCurrentTopRoundness(), 0.0f);
+        Assert.assertEquals(mSmallRadiusRatio, mSecond.getCurrentTopRoundness(), 0.0f);
     }
 
     @Test
@@ -188,7 +192,7 @@
                 createSection(mFirst, mFirst),
                 createSection(mSecond, null)
         });
-        Assert.assertEquals(SMALL_CORNER_RADIUS, mSecond.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(mSmallRadiusRatio, mSecond.getCurrentBottomRoundness(), 0.0f);
         Assert.assertEquals(1.0f, mSecond.getCurrentTopRoundness(), 0.0f);
     }
 
@@ -198,7 +202,7 @@
                 createSection(mFirst, null),
                 createSection(null, null)
         });
-        Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentBottomRoundness(), 0.0f);
         Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
     }
 
@@ -208,8 +212,8 @@
                 createSection(mSecond, mSecond),
                 createSection(null, null)
         });
-        Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentTopRoundness(), 0.0f);
+        Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentTopRoundness(), 0.0f);
     }
 
     @Test
@@ -255,8 +259,8 @@
                 createSection(mSecond, mSecond),
                 createSection(null, null)
         });
-        Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentTopRoundness(), 0.0f);
+        Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentTopRoundness(), 0.0f);
     }
 
     @Test
@@ -305,8 +309,8 @@
         });
         mFirst.setHeadsUpAnimatingAway(true);
         mFirst.setHeadsUpAnimatingAway(false);
-        Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentBottomRoundness(), 0.0f);
-        Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentTopRoundness(), 0.0f);
+        Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentBottomRoundness(), 0.0f);
+        Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentTopRoundness(), 0.0f);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index ebc45f4..c212cf3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -125,7 +125,7 @@
         int subId = 5;
         boolean roaming = true;
         mHandler.setMobileDataIndicators(status, qs, type, qsType, in, out, typeDescription,
-                typeDescriptionHtml, description, wide, subId, roaming);
+                typeDescriptionHtml, description, wide, subId, roaming, true);
         waitForCallbacks();
 
         ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class);
@@ -143,7 +143,7 @@
         Mockito.verify(mSignalCallback).setMobileDataIndicators(statusArg.capture(),
                 qsArg.capture(), typeIconArg.capture(), qsTypeIconArg.capture(), inArg.capture(),
                 outArg.capture(), typeContentArg.capture(), typeContentHtmlArg.capture(),
-                descArg.capture(), wideArg.capture(), subIdArg.capture(), eq(roaming));
+                descArg.capture(), wideArg.capture(), subIdArg.capture(), eq(roaming), eq(true));
         assertEquals(status, statusArg.getValue());
         assertEquals(qs, qsArg.getValue());
         assertEquals(type, (int) typeIconArg.getValue());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index deabcbe..da1f5d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -489,7 +489,7 @@
                     anyInt(),
                     typeIconArg.capture(), dataInArg.capture(), dataOutArg.capture(),
                     any(CharSequence.class), any(CharSequence.class), any(CharSequence.class),
-                    anyBoolean(), anyInt(), anyBoolean());
+                    anyBoolean(), anyInt(), anyBoolean(), anyBoolean());
         IconState iconState = iconArg.getValue();
         int state = SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(),
                 false);
@@ -523,7 +523,7 @@
                 typeIconArg.capture(),
                 anyInt(), anyBoolean(), anyBoolean(),
                 any(CharSequence.class), any(CharSequence.class), any(),
-                anyBoolean(), anyInt(), eq(roaming));
+                anyBoolean(), anyInt(), eq(roaming), anyBoolean());
         IconState iconState = iconArg.getValue();
         int state = icon == -1 ? 0
                 : SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(),
@@ -544,7 +544,7 @@
                 typeIconArg.capture(),
                 anyInt(), anyBoolean(), anyBoolean(),
                 any(CharSequence.class), any(CharSequence.class), any(),
-                anyBoolean(), anyInt(), anyBoolean());
+                anyBoolean(), anyInt(), anyBoolean(), anyBoolean());
         IconState iconState = iconArg.getValue();
         int state = SignalDrawable.getState(
                 level, CellSignalStrength.getNumSignalStrengthLevels(), !inet);
@@ -591,7 +591,7 @@
                 dataOutArg.capture(),
                 typeContentDescriptionArg.capture(),
                 typeContentDescriptionHtmlArg.capture(),
-                any(), anyBoolean(), anyInt(), anyBoolean());
+                any(), anyBoolean(), anyInt(), anyBoolean(), anyBoolean());
 
         IconState iconState = iconArg.getValue();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 2b82f66..10166cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -134,7 +134,7 @@
         // Still be on wifi though.
         setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
         setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
-        verifyLastMobileDataIndicators(false, DEFAULT_LEVEL, 0, true);
+        verifyLastMobileDataIndicators(true, DEFAULT_LEVEL, 0, true);
     }
 
     @Test
diff --git a/services/Android.bp b/services/Android.bp
index b11a2e8..f6bb72a 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -28,6 +28,7 @@
         ":services.profcollect-sources",
         ":services.restrictions-sources",
         ":services.searchui-sources",
+        ":services.speech-sources",
         ":services.startop.iorap-sources",
         ":services.systemcaptions-sources",
         ":services.translation-sources",
@@ -75,6 +76,7 @@
         "services.profcollect",
         "services.restrictions",
         "services.searchui",
+        "services.speech",
         "services.startop",
         "services.systemcaptions",
         "services.translation",
@@ -139,7 +141,7 @@
         last_released: {
             api_file: ":android.api.system-server.latest",
             removed_api_file: ":removed.api.system-server.latest",
-            baseline_file: ":system-server-api-incompatibilities-with-last-released"
+            baseline_file: ":android-incompatibilities.api.system-server.latest"
         },
         api_lint: {
             enabled: true,
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 74a6e07..d129b9c 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -5658,7 +5658,9 @@
         if (ns == null) {
             return;
         }
-        MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(ns);
+        if (ns instanceof MatchAllNetworkSpecifier) {
+            throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted");
+        }
     }
 
     private void ensureValid(NetworkCapabilities nc) {
@@ -6194,7 +6196,7 @@
         nai.networkAgentPortalData = lp.getCaptivePortalData();
     }
 
-    private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
+    private void updateLinkProperties(NetworkAgentInfo networkAgent, @NonNull LinkProperties newLp,
             @NonNull LinkProperties oldLp) {
         int netId = networkAgent.network.getNetId();
 
@@ -6203,8 +6205,7 @@
         // the LinkProperties for the network are accurate.
         networkAgent.clatd.fixupLinkProperties(oldLp, newLp);
 
-        updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities,
-                networkAgent.networkInfo.getType());
+        updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities);
 
         // update filtering rules, need to happen after the interface update so netd knows about the
         // new interface (the interface name -> index map becomes initialized)
@@ -6343,7 +6344,7 @@
 
     private void updateInterfaces(final @Nullable LinkProperties newLp,
             final @Nullable LinkProperties oldLp, final int netId,
-            final @Nullable NetworkCapabilities caps, final int legacyType) {
+            final @NonNull NetworkCapabilities caps) {
         final CompareResult<String> interfaceDiff = new CompareResult<>(
                 oldLp != null ? oldLp.getAllInterfaceNames() : null,
                 newLp != null ? newLp.getAllInterfaceNames() : null);
@@ -6354,7 +6355,7 @@
                     if (DBG) log("Adding iface " + iface + " to network " + netId);
                     mNetd.networkAddInterface(netId, iface);
                     wakeupModifyInterface(iface, caps, true);
-                    bs.noteNetworkInterfaceType(iface, legacyType);
+                    bs.noteNetworkInterfaceForTransports(iface, caps.getTransportTypes());
                 } catch (Exception e) {
                     loge("Exception adding interface: " + e);
                 }
@@ -6626,6 +6627,7 @@
      * maintained here that the NetworkAgent is not aware of (e.g., validated, captive portal,
      * and foreground status).
      */
+    @NonNull
     private NetworkCapabilities mixInCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) {
         // Once a NetworkAgent is connected, complain if some immutable capabilities are removed.
          // Don't complain for VPNs since they're not driven by requests and there is no risk of
@@ -6682,6 +6684,25 @@
         return newNc;
     }
 
+    private void updateNetworkInfoForRoamingAndSuspended(NetworkAgentInfo nai,
+            NetworkCapabilities prevNc, NetworkCapabilities newNc) {
+        final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+        final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+        if (prevSuspended != suspended) {
+            // TODO (b/73132094) : remove this call once the few users of onSuspended and
+            // onResumed have been removed.
+            notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
+                    : ConnectivityManager.CALLBACK_RESUMED);
+        }
+        if (prevSuspended != suspended || prevRoaming != roaming) {
+            // updateNetworkInfo will mix in the suspended info from the capabilities and
+            // take appropriate action for the network having possibly changed state.
+            updateNetworkInfo(nai, nai.networkInfo);
+        }
+    }
+
     /**
      * Update the NetworkCapabilities for {@code nai} to {@code nc}. Specifically:
      *
@@ -6713,25 +6734,13 @@
             // on this network. We might have been called by rematchNetworkAndRequests when a
             // network changed foreground state.
             processListenRequests(nai);
-            final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
-            final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
-            final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
-            final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
-            if (prevSuspended != suspended || prevRoaming != roaming) {
-                // TODO (b/73132094) : remove this call once the few users of onSuspended and
-                // onResumed have been removed.
-                notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
-                        : ConnectivityManager.CALLBACK_RESUMED);
-                // updateNetworkInfo will mix in the suspended info from the capabilities and
-                // take appropriate action for the network having possibly changed state.
-                updateNetworkInfo(nai, nai.networkInfo);
-            }
         } else {
             // If the requestable capabilities have changed or the score changed, we can't have been
             // called by rematchNetworkAndRequests, so it's safe to start a rematch.
             rematchAllNetworksAndRequests();
             notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
         }
+        updateNetworkInfoForRoamingAndSuspended(nai, prevNc, newNc);
 
         final boolean oldMetered = prevNc.isMetered();
         final boolean newMetered = newNc.isMetered();
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index b595eb1..9f91dd6 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
 
 option java_package com.android.server
 
@@ -173,6 +173,10 @@
 3120 pm_critical_info (msg|3)
 # Disk usage stats for verifying quota correctness
 3121 pm_package_stats (manual_time|2|3),(quota_time|2|3),(manual_data|2|2),(quota_data|2|2),(manual_cache|2|2),(quota_cache|2|2)
+# Snapshot statistics
+3130 pm_snapshot_stats (build_count|1|1),(reuse_count|1|1),(big_builds|1|1),(quick_rebuilds|1|1),(max_build_time|1|3),(cumm_build_time|1|3)
+# Snapshot rebuild instance
+3131 pm_snapshot_rebuild (build_time|1|3),(elapsed|1|3)
 
 # ---------------------------
 # InputMethodManagerService.java
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index e8687e5..a08d066 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -242,6 +242,7 @@
         nc.addTransportType(NetworkCapabilities.TRANSPORT_TEST);
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+        nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
         nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
         nc.setAdministratorUids(administratorUids);
         if (!isMetered) {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 3e80709..97e313e 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -102,7 +102,10 @@
     private VibrationScaler mVibrationScaler;
     private InputDeviceDelegate mInputDeviceDelegate;
 
-    private volatile VibrationThread mThread;
+    @GuardedBy("mLock")
+    private VibrationThread mThread;
+    @GuardedBy("mLock")
+    private VibrationThread mNextVibrationThread;
 
     @GuardedBy("mLock")
     private Vibration mCurrentVibration;
@@ -132,6 +135,10 @@
                 if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) {
                     mThread = null;
                     reportFinishVibrationLocked(status);
+                    if (mNextVibrationThread != null) {
+                        startVibrationThreadLocked(mNextVibrationThread);
+                        mNextVibrationThread = null;
+                    }
                 }
             }
         }
@@ -258,18 +265,14 @@
     @VisibleForTesting
     public void onVibrationComplete(int vibratorId, long vibrationId) {
         synchronized (mLock) {
-            if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) {
+            if (mCurrentVibration != null && mCurrentVibration.id == vibrationId
+                    && mThread != null) {
                 if (DEBUG) {
                     Slog.d(TAG, "Vibration onComplete callback, notifying VibrationThread");
                 }
-                if (mThread != null) {
-                    // Let the thread playing the vibration handle the callback, since it might be
-                    // expecting the vibrator to turn off multiple times during a single vibration.
-                    mThread.vibratorComplete(vibratorId);
-                } else {
-                    // No vibration is playing in the thread, but clean up service just in case.
-                    doCancelVibrateLocked(Vibration.Status.FINISHED);
-                }
+                // Let the thread playing the vibration handle the callback, since it might be
+                // expecting the vibrator to turn off multiple times during a single vibration.
+                mThread.vibratorComplete(vibratorId);
             }
         }
     }
@@ -462,8 +465,10 @@
                 try {
                     doCancelVibrateLocked(Vibration.Status.CANCELLED);
                     startVibrationLocked(vib);
+                    boolean isNextVibration = mNextVibrationThread != null
+                            && vib.equals(mNextVibrationThread.getVibration());
 
-                    if (!vib.hasEnded() && mCurrentVibration.id != vib.id) {
+                    if (!vib.hasEnded() && !vib.equals(mCurrentVibration) && !isNextVibration) {
                         // Vibration was unexpectedly ignored: add to list for debugging
                         endVibrationLocked(vib, Vibration.Status.IGNORED);
                     }
@@ -532,6 +537,7 @@
                 }
                 final long ident = Binder.clearCallingIdentity();
                 try {
+                    mNextVibrationThread = null;
                     doCancelVibrateLocked(Vibration.Status.CANCELLED);
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -546,16 +552,14 @@
         try {
             if (mThread != null) {
                 mThread.cancel();
-                mThread = null;
             }
+            mInputDeviceDelegate.cancelVibrateIfAvailable();
             if (mCurrentExternalVibration != null) {
                 endVibrationLocked(mCurrentExternalVibration, status);
                 mCurrentExternalVibration.externalVibration.mute();
                 mCurrentExternalVibration = null;
                 mVibratorController.setExternalControl(false);
             }
-            doVibratorOff();
-            reportFinishVibrationLocked(status);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
@@ -579,28 +583,30 @@
     private void startVibrationInnerLocked(Vibration vib) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
         try {
-            // Set current vibration before starting it, so callback will work.
-            mCurrentVibration = vib;
-            VibrationEffect effect = getEffect(vib);
-            Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
             boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable(
                     vib.uid, vib.opPkg, vib.getEffect(), vib.reason, vib.attrs);
             if (inputDevicesAvailable) {
-                // The set current vibration is no longer being played by this service, so drop it.
-                mCurrentVibration = null;
                 endVibrationLocked(vib, Vibration.Status.FORWARDED_TO_INPUT_DEVICES);
+            } else if (mThread == null) {
+                startVibrationThreadLocked(new VibrationThread(vib, mVibratorController, mWakeLock,
+                        mBatteryStatsService, mVibrationCallbacks));
             } else {
-                // mThread better be null here. doCancelVibrate should always be
-                // called before startVibrationInnerLocked
-                mThread = new VibrationThread(vib, mVibratorController, mWakeLock,
+                mNextVibrationThread = new VibrationThread(vib, mVibratorController, mWakeLock,
                         mBatteryStatsService, mVibrationCallbacks);
-                mThread.start();
             }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
     }
 
+    @GuardedBy("mLock")
+    private void startVibrationThreadLocked(VibrationThread thread) {
+        Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
+        mCurrentVibration = thread.getVibration();
+        mThread = thread;
+        mThread.start();
+    }
+
     /** Scale the vibration effect by the intensity as appropriate based its intent. */
     private void applyVibrationIntensityScalingLocked(Vibration vib) {
         vib.updateEffect(mVibrationScaler.scale(vib.getEffect(), vib.attrs.getUsage()));
@@ -665,13 +671,14 @@
 
     @GuardedBy("mLock")
     private void reportFinishVibrationLocked(Vibration.Status status) {
-        Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
         try {
             if (mCurrentVibration != null) {
                 endVibrationLocked(mCurrentVibration, status);
                 mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
                         mCurrentVibration.opPkg);
+
+                Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
                 mCurrentVibration = null;
             }
         } finally {
@@ -697,21 +704,6 @@
         }
     }
 
-    private void doVibratorOff() {
-        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOff");
-        try {
-            if (DEBUG) {
-                Slog.d(TAG, "Turning vibrator off.");
-            }
-            boolean inputDevicesAvailable = mInputDeviceDelegate.cancelVibrateIfAvailable();
-            if (!inputDevicesAvailable) {
-                mVibratorController.off();
-            }
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
-        }
-    }
-
     private boolean isSystemHapticFeedback(Vibration vib) {
         if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) {
             return false;
@@ -844,6 +836,7 @@
                     // haptic feedback as part of the transition.  So we don't cancel
                     // system vibrations.
                     if (mCurrentVibration != null && !isSystemHapticFeedback(mCurrentVibration)) {
+                        mNextVibrationThread = null;
                         doCancelVibrateLocked(Vibration.Status.CANCELLED);
                     }
                 }
@@ -910,6 +903,8 @@
                 return IExternalVibratorService.SCALE_MUTE;
             }
 
+            VibrationThread cancelingVibration = null;
+            int scale;
             synchronized (mLock) {
                 if (mCurrentExternalVibration != null
                         && mCurrentExternalVibration.externalVibration.equals(vib)) {
@@ -920,11 +915,9 @@
                 if (mCurrentExternalVibration == null) {
                     // If we're not under external control right now, then cancel any normal
                     // vibration that may be playing and ready the vibrator for external control.
-                    if (DEBUG) {
-                        Slog.d(TAG, "Vibrator going under external control.");
-                    }
+                    mNextVibrationThread = null;
                     doCancelVibrateLocked(Vibration.Status.CANCELLED);
-                    mVibratorController.setExternalControl(true);
+                    cancelingVibration = mThread;
                 } else {
                     endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED);
                 }
@@ -941,11 +934,24 @@
                 vib.linkToDeath(mCurrentExternalDeathRecipient);
                 mCurrentExternalVibration.scale = mVibrationScaler.getExternalVibrationScale(
                         vib.getVibrationAttributes().getUsage());
-                if (DEBUG) {
-                    Slog.e(TAG, "Playing external vibration: " + vib);
-                }
-                return mCurrentExternalVibration.scale;
+                scale = mCurrentExternalVibration.scale;
             }
+            if (cancelingVibration != null) {
+                try {
+                    cancelingVibration.join();
+                } catch (InterruptedException e) {
+                    Slog.w("Interrupted while waiting current vibration to be cancelled before "
+                            + "starting external vibration", e);
+                }
+            }
+            if (DEBUG) {
+                Slog.d(TAG, "Vibrator going under external control.");
+            }
+            mVibratorController.setExternalControl(true);
+            if (DEBUG) {
+                Slog.e(TAG, "Playing external vibration: " + vib);
+            }
+            return scale;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index b1cbb4a..c287240 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -254,6 +254,8 @@
         mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
         mStats.setPowerProfileLocked(new PowerProfile(context));
+        mStats.startTrackingSystemServerCpuTime();
+
         mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats);
     }
 
@@ -1674,11 +1676,11 @@
     }
 
     @Override
-    public void noteNetworkInterfaceType(final String iface, final int networkType) {
+    public void noteNetworkInterfaceForTransports(final String iface, int[] transportTypes) {
         enforceCallingPermission();
         synchronized (mLock) {
             mHandler.post(() -> {
-                mStats.noteNetworkInterfaceType(iface, networkType);
+                mStats.noteNetworkInterfaceForTransports(iface, transportTypes);
             });
         }
     }
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index dd09a1c..27a238d 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -80,20 +80,22 @@
 
     // Phenotype sends int configurations and we map them to the strings we'll use on device,
     // preventing a weird string value entering the kernel.
+    private static final int COMPACT_ACTION_NONE = 0;
+    private static final int COMPACT_ACTION_FILE = 1;
+    private static final int COMPACT_ACTION_ANON = 2;
+    private static final int COMPACT_ACTION_FULL = 3;
+
+    private static final String COMPACT_ACTION_STRING[] = {"", "file", "anon", "all"};
+
+    // Keeps these flags in sync with services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
     private static final int COMPACT_ACTION_FILE_FLAG = 1;
     private static final int COMPACT_ACTION_ANON_FLAG = 2;
-    private static final int COMPACT_ACTION_FULL_FLAG = 3;
-    private static final int COMPACT_ACTION_NONE_FLAG = 4;
-    private static final String COMPACT_ACTION_NONE = "";
-    private static final String COMPACT_ACTION_FILE = "file";
-    private static final String COMPACT_ACTION_ANON = "anon";
-    private static final String COMPACT_ACTION_FULL = "all";
 
     // Defaults for phenotype flags.
     @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
     @VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = false;
-    @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE_FLAG;
-    @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_FULL_FLAG;
+    @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE;
+    @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_FULL;
     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000;
     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000;
     @VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
@@ -232,6 +234,8 @@
     @VisibleForTesting
     Handler mCompactionHandler;
     private Handler mFreezeHandler;
+    @GuardedBy("mAm")
+    private boolean mFreezerOverride = false;
 
     // Maps process ID to last compaction statistics for processes that we've fully compacted. Used
     // when evaluating throttles that we only consider for "full" compaction, so we don't store
@@ -406,6 +410,14 @@
     private native void compactSystem();
 
     /**
+     * Compacts a process or app
+     * @param pid pid of process to compact
+     * @param compactionFlags selects the compaction type as defined by COMPACT_ACTION_{TYPE}_FLAG
+     *         constants
+     */
+    static private native void compactProcess(int pid, int compactionFlags);
+
+    /**
      * Reads the flag value from DeviceConfig to determine whether app compaction
      * should be enabled, and starts the freeze/compaction thread if needed.
      */
@@ -454,21 +466,35 @@
             }
         }
 
-        try {
-            enableFreezerInternal(enable);
-            return true;
-        } catch (java.lang.RuntimeException e) {
-            if (enable) {
-                mFreezerDisableCount = 0;
-            } else {
-                mFreezerDisableCount = 1;
-            }
+        // Override is applied immediately, restore is delayed
+        synchronized (mAm) {
+            int processCount = mAm.mProcessList.mLruProcesses.size();
 
-            Slog.e(TAG_AM, "Exception handling freezer state (enable: " + enable + "): "
-                    + e.toString());
+            mFreezerOverride = !enable;
+            Slog.d(TAG_AM, "freezer override set to " + mFreezerOverride);
+
+            for (int i = 0; i < processCount; i++) {
+                ProcessRecord process = mAm.mProcessList.mLruProcesses.get(i);
+
+                if (process == null) {
+                    continue;
+                }
+
+                if (enable && process.freezerOverride) {
+                    freezeAppAsync(process);
+                    process.freezerOverride = false;
+                }
+
+                if (!enable && process.frozen) {
+                    unfreezeAppLocked(process);
+
+                    // Set freezerOverride *after* calling unfreezeAppLocked (it resets the flag)
+                    process.freezerOverride = true;
+                }
+            }
         }
 
-        return false;
+        return true;
     }
 
     /**
@@ -515,7 +541,7 @@
         FileReader fr = null;
 
         try {
-            fr = new FileReader("/sys/fs/cgroup/freezer/cgroup.freeze");
+            fr = new FileReader("/sys/fs/cgroup/uid_0/cgroup.freeze");
             char state = (char) fr.read();
 
             if (state == '1' || state == '0') {
@@ -706,18 +732,11 @@
 
     @VisibleForTesting
     static String compactActionIntToString(int action) {
-        switch(action) {
-            case COMPACT_ACTION_NONE_FLAG:
-                return COMPACT_ACTION_NONE;
-            case COMPACT_ACTION_FILE_FLAG:
-                return COMPACT_ACTION_FILE;
-            case COMPACT_ACTION_ANON_FLAG:
-                return COMPACT_ACTION_ANON;
-            case COMPACT_ACTION_FULL_FLAG:
-                return COMPACT_ACTION_FULL;
-            default:
-                return COMPACT_ACTION_NONE;
+        if (action < 0 || action >= COMPACT_ACTION_STRING.length) {
+            return "";
         }
+
+        return COMPACT_ACTION_STRING[action];
     }
 
     // This will ensure app will be out of the freezer for at least FREEZE_TIMEOUT_MS
@@ -744,6 +763,8 @@
     void unfreezeAppLocked(ProcessRecord app) {
         mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
 
+        app.freezerOverride = false;
+
         if (!app.frozen) {
             if (DEBUG_FREEZER) {
                 Slog.d(TAG_AM,
@@ -753,6 +774,8 @@
             return;
         }
 
+        // Unfreeze the binder interface first, to avoid transactions triggered by timers fired
+        // right after unfreezing the process to fail
         boolean processKilled = false;
 
         try {
@@ -950,11 +973,11 @@
                             action = mCompactActionFull;
                             break;
                         default:
-                            action = COMPACT_ACTION_NONE;
+                            action = COMPACT_ACTION_STRING[COMPACT_ACTION_NONE];
                             break;
                     }
 
-                    if (COMPACT_ACTION_NONE.equals(action)) {
+                    if (COMPACT_ACTION_STRING[COMPACT_ACTION_NONE].equals(action)) {
                         return;
                     }
 
@@ -978,7 +1001,8 @@
                         return;
                     }
 
-                    if (action.equals(COMPACT_ACTION_FULL) || action.equals(COMPACT_ACTION_ANON)) {
+                    if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])
+                            || action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
                         if (mFullAnonRssThrottleKb > 0L
                                 && anonRssBefore < mFullAnonRssThrottleKb) {
                             if (DEBUG_COMPACTION) {
@@ -1054,8 +1078,8 @@
                             proc.lastCompactTime = end;
                             proc.lastCompactAction = pendingAction;
                         }
-                        if (action.equals(COMPACT_ACTION_FULL)
-                                || action.equals(COMPACT_ACTION_ANON)) {
+                        if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])
+                                || action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
                             // Remove entry and insert again to update insertion order.
                             mLastCompactionStats.remove(pid);
                             mLastCompactionStats.put(pid, new LastCompactionStats(rssAfter));
@@ -1131,12 +1155,31 @@
                     return;
                 }
 
+                if (mFreezerOverride) {
+                    proc.freezerOverride = true;
+                    Slog.d(TAG_AM, "Skipping freeze for process " + pid
+                            + " " + name + " curAdj = " + proc.curAdj
+                            + "(override)");
+                    return;
+                }
+
                 if (pid == 0 || proc.frozen) {
                     // Already frozen or not a real process, either one being
                     // launched or one being killed
                     return;
                 }
 
+                // Freeze binder interface before the process, to flush any
+                // transactions that might be pending.
+                try {
+                    freezeBinder(pid, true);
+                } catch (RuntimeException e) {
+                    Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
+                    proc.kill("Unable to freeze binder interface",
+                            ApplicationExitInfo.REASON_OTHER,
+                            ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+                }
+
                 long unfreezeTime = proc.freezeUnfreezeTime;
 
                 try {
@@ -1163,15 +1206,6 @@
 
             EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);
 
-            try {
-                freezeBinder(pid, true);
-            } catch (RuntimeException e) {
-                Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
-                proc.kill("Unable to freeze binder interface",
-                        ApplicationExitInfo.REASON_OTHER,
-                        ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
-            }
-
             // See above for why we're not taking mPhenotypeFlagLock here
             if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
                 FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED,
@@ -1229,8 +1263,12 @@
         // Compact process.
         @Override
         public void performCompaction(String action, int pid) throws IOException {
-            try (FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim")) {
-                fos.write(action.getBytes());
+            if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])) {
+                compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG);
+            } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FILE])) {
+                compactProcess(pid, COMPACT_ACTION_FILE_FLAG);
+            } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
+                compactProcess(pid, COMPACT_ACTION_ANON_FLAG);
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 520a28b..63195d3 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -179,6 +179,7 @@
     int reqCompactAction;       // The most recent compaction action requested for this app.
     int lastCompactAction;      // The most recent compaction action performed for this app.
     boolean frozen;             // True when the process is frozen.
+    boolean freezerOverride;     // An override on the freeze state is in progress.
     long freezeUnfreezeTime;    // Last time the app was (un)frozen, 0 for never
     boolean shouldNotFreeze;    // True if a process has a WPRI binding from an unfrozen process
     private int mCurSchedGroup; // Currently desired scheduling class
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index ab0360b..b282484 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -329,7 +329,7 @@
     private final QosCallbackTracker mQosCallbackTracker;
 
     public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info,
-            LinkProperties lp, NetworkCapabilities nc, int score, Context context,
+            @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, int score, Context context,
             Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
             IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber,
             int creatorUid, QosCallbackTracker qosCallbackTracker) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index a7be657..5e6b9f3 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -686,7 +686,7 @@
 
             mHostname = hostname;
             mMeasurement.description = "DNS TLS dst{" + mTarget.getHostAddress() + "} hostname{"
-                    + TextUtils.emptyIfNull(mHostname) + "}";
+                    + (mHostname == null ? "" : mHostname) + "}";
         }
 
         private SSLSocket setupSSLSocket() throws IOException {
diff --git a/services/core/java/com/android/server/display/DisplayGroup.java b/services/core/java/com/android/server/display/DisplayGroup.java
index 2ba8758..a11a745 100644
--- a/services/core/java/com/android/server/display/DisplayGroup.java
+++ b/services/core/java/com/android/server/display/DisplayGroup.java
@@ -26,8 +26,6 @@
  */
 public class DisplayGroup {
 
-    public static final int DEFAULT = 0;
-
     private final List<LogicalDisplay> mDisplays = new ArrayList<>();
     private final int mGroupId;
 
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 55103ca..c3f8d8c 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1357,7 +1357,7 @@
                 // Scan supported modes returned by display.getInfo() to find a mode with the same
                 // size as the default display mode but with the specified refresh rate instead.
                 requestedModeId = display.getDisplayInfoLocked().findDefaultModeByRefreshRate(
-                        requestedRefreshRate);
+                        requestedRefreshRate).getModeId();
             }
             mDisplayModeDirector.getAppRequestObserver().setAppRequestedMode(
                     displayId, requestedModeId);
@@ -1538,6 +1538,14 @@
         }
     }
 
+    void setDisplayModeDirectorLoggingEnabled(boolean enabled) {
+        synchronized (mSyncRoot) {
+            if (mDisplayModeDirector != null) {
+                mDisplayModeDirector.setLoggingEnabled(enabled);
+            }
+        }
+    }
+
     void setAmbientColorTemperatureOverride(float cct) {
         synchronized (mSyncRoot) {
             final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 111664a..aaea15a 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -54,6 +54,10 @@
                 return setDisplayWhiteBalanceLoggingEnabled(true);
             case "dwb-logging-disable":
                 return setDisplayWhiteBalanceLoggingEnabled(false);
+            case "dmd-logging-enable":
+                return setDisplayModeDirectorLoggingEnabled(true);
+            case "dmd-logging-disable":
+                return setDisplayModeDirectorLoggingEnabled(false);
             case "dwb-set-cct":
                 return setAmbientColorTemperatureOverride();
             case "set-fold":
@@ -82,6 +86,10 @@
         pw.println("    Enable display white-balance logging.");
         pw.println("  dwb-logging-disable");
         pw.println("    Disable display white-balance logging.");
+        pw.println("  dmd-logging-enable");
+        pw.println("    Enable display mode director logging.");
+        pw.println("  dmd-logging-disable");
+        pw.println("    Disable display mode director logging.");
         pw.println("  dwb-set-cct CCT");
         pw.println("    Sets the ambient color temperature override to CCT (use -1 to disable).");
         pw.println("  set-fold [fold|unfold|reset]");
@@ -136,6 +144,11 @@
         return 0;
     }
 
+    private int setDisplayModeDirectorLoggingEnabled(boolean enabled) {
+        mService.setDisplayModeDirectorLoggingEnabled(enabled);
+        return 0;
+    }
+
     private int setAmbientColorTemperatureOverride() {
         String cctText = getNextArg();
         if (cctText == null) {
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 006f875..dce6bd8 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -67,7 +67,7 @@
  */
 public class DisplayModeDirector {
     private static final String TAG = "DisplayModeDirector";
-    private static final boolean DEBUG = false;
+    private boolean mLoggingEnabled;
 
     private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1;
     private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
@@ -155,6 +155,14 @@
         }
     }
 
+    public void setLoggingEnabled(boolean loggingEnabled) {
+        if (mLoggingEnabled == loggingEnabled) {
+            return;
+        }
+        mLoggingEnabled = loggingEnabled;
+        mBrightnessObserver.setLoggingEnabled(loggingEnabled);
+    }
+
     @NonNull
     private SparseArray<Vote> getVotesLocked(int displayId) {
         SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId);
@@ -269,7 +277,7 @@
 
                 availableModes = filterModes(modes, primarySummary);
                 if (availableModes.length > 0) {
-                    if (DEBUG) {
+                    if (mLoggingEnabled) {
                         Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
                                 + " with lowest priority considered "
                                 + Vote.priorityToString(lowestConsideredPriority)
@@ -282,7 +290,7 @@
                     break;
                 }
 
-                if (DEBUG) {
+                if (mLoggingEnabled) {
                     Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
                             + Vote.priorityToString(lowestConsideredPriority)
                             + " and with the following constraints: "
@@ -307,7 +315,7 @@
                     Math.min(appRequestSummary.minRefreshRate, primarySummary.minRefreshRate);
             appRequestSummary.maxRefreshRate =
                     Math.max(appRequestSummary.maxRefreshRate, primarySummary.maxRefreshRate);
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.i(TAG,
                         String.format("App request range: [%.0f %.0f]",
                                 appRequestSummary.minRefreshRate,
@@ -357,7 +365,7 @@
         for (Display.Mode mode : supportedModes) {
             if (mode.getPhysicalWidth() != summary.width
                     || mode.getPhysicalHeight() != summary.height) {
-                if (DEBUG) {
+                if (mLoggingEnabled) {
                     Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size"
                             + ": desiredWidth=" + summary.width
                             + ": desiredHeight=" + summary.height
@@ -372,7 +380,7 @@
             // comparison.
             if (refreshRate < (summary.minRefreshRate - FLOAT_TOLERANCE)
                     || refreshRate > (summary.maxRefreshRate + FLOAT_TOLERANCE)) {
-                if (DEBUG) {
+                if (mLoggingEnabled) {
                     Slog.w(TAG, "Discarding mode " + mode.getModeId()
                             + ", outside refresh rate bounds"
                             + ": minRefreshRate=" + summary.minRefreshRate
@@ -516,7 +524,7 @@
     }
 
     private void updateVoteLocked(int displayId, int priority, Vote vote) {
-        if (DEBUG) {
+        if (mLoggingEnabled) {
             Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
                     + ", priority=" + Vote.priorityToString(priority)
                     + ", vote=" + vote + ")");
@@ -537,7 +545,7 @@
         }
 
         if (votes.size() == 0) {
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.i(TAG, "No votes left for display " + displayId + ", removing.");
             }
             mVotesByDisplay.remove(displayId);
@@ -1287,6 +1295,7 @@
         private boolean mShouldObserveAmbientLowChange;
         private boolean mShouldObserveDisplayHighChange;
         private boolean mShouldObserveAmbientHighChange;
+        private boolean mLoggingEnabled;
 
         private SensorManager mSensorManager;
         private Sensor mLightSensor;
@@ -1303,7 +1312,6 @@
         // changeable and low power mode off. After initialization, these states will
         // be updated from the same handler thread.
         private int mDefaultDisplayState = Display.STATE_UNKNOWN;
-        private boolean mIsDeviceActive = false;
         private boolean mRefreshRateChangeable = false;
         private boolean mLowPowerModeEnabled = false;
 
@@ -1415,6 +1423,14 @@
             mDeviceConfigDisplaySettings.startListening();
         }
 
+        public void setLoggingEnabled(boolean loggingEnabled) {
+            if (mLoggingEnabled == loggingEnabled) {
+                return;
+            }
+            mLoggingEnabled = loggingEnabled;
+            mLightSensorListener.setLoggingEnabled(loggingEnabled);
+        }
+
         public void onRefreshRateSettingChangedLocked(float min, float max) {
             boolean changeable = (max - min > 1f && max > 60f);
             if (mRefreshRateChangeable != changeable) {
@@ -1485,7 +1501,6 @@
             pw.println("    mAmbientLux: " + mAmbientLux);
             pw.println("    mBrightness: " + mBrightness);
             pw.println("    mDefaultDisplayState: " + mDefaultDisplayState);
-            pw.println("    mIsDeviceActive: " + mIsDeviceActive);
             pw.println("    mLowPowerModeEnabled: " + mLowPowerModeEnabled);
             pw.println("    mRefreshRateChangeable: " + mRefreshRateChangeable);
             pw.println("    mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange);
@@ -1691,7 +1706,7 @@
                 vote = Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone);
             }
 
-            if (DEBUG) {
+            if (mLoggingEnabled) {
                 Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " +  mAmbientLux
                         + ", Vote " + vote);
             }
@@ -1720,6 +1735,11 @@
 
         @VisibleForTesting
         public void setDefaultDisplayState(int state) {
+            if (mLoggingEnabled) {
+                Slog.d(TAG, "setDefaultDisplayState: mDefaultDisplayState = "
+                        + mDefaultDisplayState + ", state = " + state);
+            }
+
             if (mDefaultDisplayState != state) {
                 mDefaultDisplayState = state;
                 updateSensorStatus();
@@ -1731,36 +1751,58 @@
                 return;
             }
 
+            if (mLoggingEnabled) {
+                Slog.d(TAG, "updateSensorStatus: mShouldObserveAmbientLowChange = "
+                        + mShouldObserveAmbientLowChange + ", mShouldObserveAmbientHighChange = "
+                        + mShouldObserveAmbientHighChange);
+                Slog.d(TAG, "updateSensorStatus: mLowPowerModeEnabled = "
+                        + mLowPowerModeEnabled + ", mRefreshRateChangeable = "
+                        + mRefreshRateChangeable);
+            }
+
             if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
                      && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
                 mSensorManager.registerListener(mLightSensorListener,
                         mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
+                if (mLoggingEnabled) {
+                    Slog.d(TAG, "updateSensorStatus: registerListener");
+                }
             } else {
                 mLightSensorListener.removeCallbacks();
                 mSensorManager.unregisterListener(mLightSensorListener);
+                if (mLoggingEnabled) {
+                    Slog.d(TAG, "updateSensorStatus: unregisterListener");
+                }
             }
         }
 
         private boolean isDeviceActive() {
-            mIsDeviceActive = mInjector.isDeviceInteractive(mContext);
-            return (mDefaultDisplayState == Display.STATE_ON)
-                    && mIsDeviceActive;
+            return mDefaultDisplayState == Display.STATE_ON;
         }
 
         private final class LightSensorEventListener implements SensorEventListener {
             final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
             private float mLastSensorData;
             private long mTimestamp;
+            private boolean mLoggingEnabled;
 
             public void dumpLocked(PrintWriter pw) {
                 pw.println("    mLastSensorData: " + mLastSensorData);
                 pw.println("    mTimestamp: " + formatTimestamp(mTimestamp));
             }
 
+
+            public void setLoggingEnabled(boolean loggingEnabled) {
+                if (mLoggingEnabled == loggingEnabled) {
+                    return;
+                }
+                mLoggingEnabled = loggingEnabled;
+            }
+
             @Override
             public void onSensorChanged(SensorEvent event) {
                 mLastSensorData = event.values[0];
-                if (DEBUG) {
+                if (mLoggingEnabled) {
                     Slog.d(TAG, "On sensor changed: " + mLastSensorData);
                 }
 
@@ -2009,8 +2051,6 @@
 
         void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
                 @NonNull ContentObserver observer);
-
-        boolean isDeviceInteractive(@NonNull Context context);
     }
 
     @VisibleForTesting
@@ -2041,11 +2081,6 @@
             cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
                     observer, UserHandle.USER_SYSTEM);
         }
-
-        @Override
-        public boolean isDeviceInteractive(@NonNull Context ctx) {
-            return ctx.getSystemService(PowerManager.class).isInteractive();
-        }
     }
 
 }
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 5bf83db..86de159 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -71,6 +71,9 @@
 
     private final int mDisplayId;
     private final int mLayerStack;
+
+    private int mDisplayGroupId = Display.INVALID_DISPLAY_GROUP;
+
     /**
      * Override information set by the window manager. Will be reported instead of {@link #mInfo}
      * if not null.
@@ -265,6 +268,19 @@
     }
 
     /**
+     * Updates the {@link DisplayGroup} to which the logical display belongs.
+     *
+     * @param groupId Identifier for the {@link DisplayGroup}.
+     */
+    public void updateDisplayGroupIdLocked(int groupId) {
+        if (groupId != mDisplayGroupId) {
+            mDisplayGroupId = groupId;
+            mBaseDisplayInfo.displayGroupId = groupId;
+            mInfo.set(null);
+        }
+    }
+
+    /**
      * Updates the state of the logical display based on the available display devices.
      * The logical display might become invalid if it is attached to a display device
      * that no longer exists.
@@ -365,6 +381,7 @@
                     (deviceInfo.flags & DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT) != 0;
             mBaseDisplayInfo.displayCutout = maskCutout ? null : deviceInfo.displayCutout;
             mBaseDisplayInfo.displayId = mDisplayId;
+            mBaseDisplayInfo.displayGroupId = mDisplayGroupId;
             updateFrameRateOverrides(deviceInfo);
             mBaseDisplayInfo.brightnessMinimum = deviceInfo.brightnessMinimum;
             mBaseDisplayInfo.brightnessMaximum = deviceInfo.brightnessMaximum;
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index bb2fbed..e738878 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -96,7 +96,7 @@
     private final SparseArray<LogicalDisplay> mLogicalDisplays =
             new SparseArray<LogicalDisplay>();
     private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1;
-    private int mNextNonDefaultGroupId = DisplayGroup.DEFAULT + 1;
+    private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
 
     /** A mapping from logical display id to display group. */
     private final SparseArray<DisplayGroup> mDisplayGroups = new SparseArray<>();
@@ -313,7 +313,18 @@
         final int displayId = assignDisplayIdLocked(isDefault);
         final int layerStack = assignLayerStackLocked(displayId);
 
+        final DisplayGroup displayGroup;
+        final boolean addNewDisplayGroup =
+                isDefault || (deviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0;
+        if (addNewDisplayGroup) {
+            final int groupId = assignDisplayGroupIdLocked(isDefault);
+            displayGroup = new DisplayGroup(groupId);
+        } else {
+            displayGroup = mDisplayGroups.get(Display.DEFAULT_DISPLAY);
+        }
+
         LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
+        display.updateDisplayGroupIdLocked(displayGroup.getGroupId());
         display.updateLocked(mDisplayDeviceRepo);
         if (!display.isValidLocked()) {
             // This should never happen currently.
@@ -324,13 +335,6 @@
 
         mLogicalDisplays.put(displayId, display);
 
-        final DisplayGroup displayGroup;
-        if (isDefault || (deviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0) {
-            final int groupId = assignDisplayGroupIdLocked(isDefault);
-            displayGroup = new DisplayGroup(groupId);
-        } else {
-            displayGroup = mDisplayGroups.get(Display.DEFAULT_DISPLAY);
-        }
         displayGroup.addDisplay(display);
         mDisplayGroups.append(displayId, displayGroup);
 
@@ -369,6 +373,7 @@
                         final DisplayGroup displayGroup = new DisplayGroup(groupId);
                         displayGroup.addDisplay(display);
                         mDisplayGroups.append(display.getDisplayIdLocked(), displayGroup);
+                        display.updateDisplayGroupIdLocked(groupId);
                     }
                 } else {
                     // The display should be a part of the default DisplayGroup.
@@ -377,6 +382,7 @@
                         displayGroup.removeDisplay(display);
                         defaultDisplayGroup.addDisplay(display);
                         mDisplayGroups.put(displayId, defaultDisplayGroup);
+                        display.updateDisplayGroupIdLocked(defaultDisplayGroup.getGroupId());
                     }
                 }
 
@@ -406,7 +412,7 @@
     }
 
     private int assignDisplayGroupIdLocked(boolean isDefault) {
-        return isDefault ? DisplayGroup.DEFAULT : mNextNonDefaultGroupId++;
+        return isDefault ? Display.DEFAULT_DISPLAY_GROUP : mNextNonDefaultGroupId++;
     }
 
     private int assignLayerStackLocked(int displayId) {
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index bda4240..5b3db01 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -22,12 +22,15 @@
 import android.graphics.Typeface;
 import android.graphics.fonts.FontFamily;
 import android.graphics.fonts.FontFileUtil;
+import android.graphics.fonts.FontManager;
 import android.graphics.fonts.SystemFonts;
-import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.os.SharedMemory;
+import android.os.ShellCallback;
 import android.system.ErrnoException;
 import android.text.FontConfig;
+import android.util.AndroidException;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
 
@@ -48,6 +51,7 @@
 import java.nio.NioUtils;
 import java.nio.channels.FileChannel;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Map;
 
 /** A service for managing system fonts. */
@@ -55,8 +59,6 @@
 public final class FontManagerService extends IFontManager.Stub {
     private static final String TAG = "FontManagerService";
 
-    // TODO: make this a DeviceConfig flag.
-    private static final boolean ENABLE_FONT_UPDATES = false;
     private static final String FONT_FILES_DIR = "/data/fonts/files";
 
     @Override
@@ -64,6 +66,24 @@
         return getCurrentFontSettings().getSystemFontConfig();
     }
 
+    /* package */ static class SystemFontException extends AndroidException {
+        private final int mErrorCode;
+
+        SystemFontException(@FontManager.ErrorCode int errorCode, String msg, Throwable cause) {
+            super(msg, cause);
+            mErrorCode = errorCode;
+        }
+
+        SystemFontException(int errorCode, String msg) {
+            super(msg);
+            mErrorCode = errorCode;
+        }
+
+        @FontManager.ErrorCode int getErrorCode() {
+            return mErrorCode;
+        }
+    }
+
     /** Class to manage FontManagerService's lifecycle. */
     public static final class Lifecycle extends SystemService {
         private final FontManagerService mService;
@@ -151,7 +171,6 @@
 
     @Nullable
     private static UpdatableFontDir createUpdatableFontDir() {
-        if (!ENABLE_FONT_UPDATES) return null;
         // If apk verity is supported, fs-verity should be available.
         if (!FileIntegrityService.isApkVeritySupported()) return null;
         return new UpdatableFontDir(new File(FONT_FILES_DIR),
@@ -178,19 +197,34 @@
         }
     }
 
-    // TODO(b/173619554): Expose as API.
-    private boolean installFontFile(FileDescriptor fd, byte[] pkcs7Signature) {
-        if (mUpdatableFontDir == null) return false;
+    /* package */ void installFontFile(FileDescriptor fd, byte[] pkcs7Signature)
+            throws SystemFontException {
+        if (mUpdatableFontDir == null) {
+            throw new SystemFontException(
+                    FontManager.ERROR_CODE_FONT_UPDATER_DISABLED,
+                    "The font updater is disabled.");
+        }
         synchronized (FontManagerService.this) {
-            try {
-                mUpdatableFontDir.installFontFile(fd, pkcs7Signature);
-            } catch (IOException e) {
-                Slog.w(TAG, "Failed to install font file");
-                return false;
-            }
+            mUpdatableFontDir.installFontFile(fd, pkcs7Signature);
             // Create updated font map in the next getSerializedSystemFontMap() call.
             mCurrentFontSettings = null;
-            return true;
+        }
+    }
+
+    /* package */ void clearUpdates() throws SystemFontException {
+        if (mUpdatableFontDir == null) {
+            throw new SystemFontException(
+                    FontManager.ERROR_CODE_FONT_UPDATER_DISABLED,
+                    "The font updater is disabled.");
+        }
+        mUpdatableFontDir.clearUpdates();
+    }
+
+    /* package */ Map<String, File> getFontFileMap() {
+        if (mUpdatableFontDir == null) {
+            return Collections.emptyMap();
+        } else {
+            return mUpdatableFontDir.getFontFileMap();
         }
     }
 
@@ -202,11 +236,13 @@
     }
 
     @Override
-    public int handleShellCommand(@NonNull ParcelFileDescriptor in,
-            @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
-            @NonNull String[] args) {
-        return new FontManagerShellCommand(this).exec(this,
-                in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args);
+    public void onShellCommand(@Nullable FileDescriptor in,
+            @Nullable FileDescriptor out,
+            @Nullable FileDescriptor err,
+            @NonNull String[] args,
+            @Nullable ShellCallback callback,
+            @NonNull ResultReceiver result) throws RemoteException {
+        new FontManagerShellCommand(this).exec(this, in, out, err, args, callback, result);
     }
 
     /* package */ static class SystemFontSettings {
@@ -245,8 +281,7 @@
         public static @Nullable SystemFontSettings create(
                 @Nullable UpdatableFontDir updatableFontDir) {
             if (updatableFontDir != null) {
-                final FontConfig fontConfig = SystemFonts.getSystemFontConfig(
-                        updatableFontDir.getFontFileMap());
+                final FontConfig fontConfig = updatableFontDir.getSystemFontConfig();
                 final Map<String, FontFamily[]> fallback =
                         SystemFonts.buildSystemFallback(fontConfig);
                 final Map<String, Typeface> typefaceMap =
@@ -274,4 +309,5 @@
             return null;
         }
     }
+
 }
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
index acb5826..fd5c020 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
@@ -16,19 +16,33 @@
 
 package com.android.server.graphics.fonts;
 
+import static com.android.server.graphics.fonts.FontManagerService.SystemFontException;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.fonts.Font;
 import android.graphics.fonts.FontFamily;
+import android.graphics.fonts.FontManager;
 import android.graphics.fonts.FontVariationAxis;
+import android.os.Binder;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.os.ShellCommand;
 import android.text.FontConfig;
 import android.util.IndentingPrintWriter;
+import android.util.Slog;
 
 import com.android.internal.util.DumpUtils;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
 import java.io.PrintWriter;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 
@@ -38,6 +52,13 @@
 public class FontManagerShellCommand extends ShellCommand {
     private static final String TAG = "FontManagerShellCommand";
 
+    /**
+     * The maximum size of signature file.  This is just to avoid potential abuse.
+     *
+     * This is copied from VerityUtils.java.
+     */
+    private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192;
+
     @NonNull private final FontManagerService mService;
 
     FontManagerShellCommand(@NonNull FontManagerService service) {
@@ -46,12 +67,31 @@
 
     @Override
     public int onCommand(String cmd) {
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != Process.ROOT_UID && callingUid != Process.SHELL_UID) {
+            // Do not change this string since this string is expected in the CTS.
+            getErrPrintWriter().println("Only shell or root user can execute font command.");
+            return 1;
+        }
         return execCommand(this, cmd);
     }
 
     @Override
     public void onHelp() {
-        dumpHelp(getOutPrintWriter());
+        PrintWriter w = getOutPrintWriter();
+        w.println("Font service (font) commands");
+        w.println("help");
+        w.println("    Print this help text.");
+        w.println();
+        w.println("dump [family name]");
+        w.println("    Dump all font files in the specified family name.");
+        w.println("    Dump current system font configuration if no family name was specified.");
+        w.println();
+        w.println("update [font file path] [signature file path]");
+        w.println("    Update installed font files with new font file.");
+        w.println();
+        w.println("clear");
+        w.println("    Remove all installed font files and reset to the initial state.");
     }
 
     /* package */ void dumpAll(@NonNull IndentingPrintWriter w) {
@@ -165,16 +205,6 @@
         w.decreaseIndent();
     }
 
-    private static void dumpHelp(@NonNull PrintWriter w) {
-        w.println("Font service (font) commands");
-        w.println("help");
-        w.println("    Print this help text.");
-        w.println();
-        w.println("dump [family name]");
-        w.println("    Dump all font files in the specified family name.");
-        w.println("    Dump current system font configuration if no family name was specified.");
-    }
-
     private void dumpFallback(@NonNull IndentingPrintWriter writer,
             @NonNull FontFamily[] families) {
         for (FontFamily family : families) {
@@ -233,37 +263,143 @@
         writer.println(sb.toString());
     }
 
-    private int execCommand(@NonNull ShellCommand shell, @NonNull String cmd) {
+    private void writeCommandResult(ShellCommand shell, SystemFontException e) {
+        // Print short summary to the stderr.
+        PrintWriter pw = shell.getErrPrintWriter();
+        pw.println(e.getErrorCode());
+        pw.println(e.getMessage());
+
+        // Dump full stack trace to logcat.
+
+        Slog.e(TAG, "Command failed: " + Arrays.toString(shell.getAllArgs()), e);
+    }
+
+    private int dump(ShellCommand shell) {
         final Context ctx = mService.getContext();
+        final FontManagerService.SystemFontSettings settings =
+                mService.getCurrentFontSettings();
+        if (!DumpUtils.checkDumpPermission(ctx, TAG, shell.getErrPrintWriter())) {
+            return 1;
+        }
+        final IndentingPrintWriter writer =
+                new IndentingPrintWriter(shell.getOutPrintWriter(), "  ");
+        String nextArg = shell.getNextArg();
+        if (nextArg == null) {
+            dumpFontConfig(writer, settings.getSystemFontConfig());
+        } else {
+            final Map<String, FontFamily[]> fallbackMap =
+                    settings.getSystemFallbackMap();
+            FontFamily[] families = fallbackMap.get(nextArg);
+            if (families == null) {
+                writer.println("Font Family \"" + nextArg + "\" not found");
+            } else {
+                dumpFallback(writer, families);
+            }
+        }
+        return 0;
+    }
+
+    private int update(ShellCommand shell) throws SystemFontException {
+        String fontPath = shell.getNextArg();
+        if (fontPath == null) {
+            throw new SystemFontException(
+                    FontManager.ERROR_CODE_INVALID_SHELL_ARGUMENT,
+                    "Font file path argument is required.");
+        }
+        String signaturePath = shell.getNextArg();
+        if (signaturePath == null) {
+            throw new SystemFontException(
+                    FontManager.ERROR_CODE_INVALID_SHELL_ARGUMENT,
+                    "Signature file argument is required.");
+        }
+
+        ParcelFileDescriptor fontFd = shell.openFileForSystem(fontPath, "r");
+        if (fontFd == null) {
+            throw new SystemFontException(
+                    FontManager.ERROR_CODE_FAILED_TO_OPEN_FONT_FILE,
+                    "Failed to open font file");
+        }
+
+        ParcelFileDescriptor sigFd = shell.openFileForSystem(signaturePath, "r");
+        if (sigFd == null) {
+            throw new SystemFontException(
+                    FontManager.ERROR_CODE_FAILED_TO_OPEN_SIGNATURE_FILE,
+                    "Failed to open signature file");
+        }
+
+        try (FileInputStream sigFis = new FileInputStream(sigFd.getFileDescriptor())) {
+            try (FileInputStream fontFis = new FileInputStream(fontFd.getFileDescriptor())) {
+                int len = sigFis.available();
+                if (len > MAX_SIGNATURE_FILE_SIZE_BYTES) {
+                    throw new SystemFontException(
+                            FontManager.ERROR_CODE_SIGNATURE_TOO_LARGE,
+                            "Signature file is too large");
+                }
+                byte[] signature = new byte[len];
+                if (sigFis.read(signature, 0, len) != len) {
+                    throw new SystemFontException(
+                            FontManager.ERROR_CODE_INVALID_SIGNATURE_FILE,
+                            "Invalid read length");
+                }
+                mService.installFontFile(fontFis.getFD(), signature);
+            } catch (IOException e) {
+                throw new SystemFontException(
+                        FontManager.ERROR_CODE_INVALID_SIGNATURE_FILE,
+                        "Failed to read signature file.", e);
+            }
+        } catch (IOException e) {
+            throw new SystemFontException(
+                    FontManager.ERROR_CODE_INVALID_FONT_FILE,
+                    "Failed to read font files.", e);
+        }
+
+        shell.getOutPrintWriter().println("Success");  // TODO: Output more details.
+        return 0;
+    }
+
+    private int clear(ShellCommand shell) throws SystemFontException {
+        mService.clearUpdates();
+        shell.getOutPrintWriter().println("Success");
+        return 0;
+    }
+
+    private int status(ShellCommand shell) throws SystemFontException {
+        final FontManagerService.SystemFontSettings settings = mService.getCurrentFontSettings();
+        final IndentingPrintWriter writer =
+                new IndentingPrintWriter(shell.getOutPrintWriter(), "  ");
+        FontConfig config = settings.getSystemFontConfig();
+
+        writer.println("Current Version: " + config.getConfigVersion());
+        LocalDateTime dt = LocalDateTime.ofEpochSecond(config.getLastModifiedDate(), 0,
+                ZoneOffset.UTC);
+        writer.println("Last Modified Date: " + dt.format(DateTimeFormatter.ISO_DATE_TIME));
+
+        Map<String, File> fontFileMap = mService.getFontFileMap();
+        writer.println("Number of updated font files: " + fontFileMap.size());
+        return 0;
+    }
+
+    private int execCommand(@NonNull ShellCommand shell, @Nullable String cmd) {
         if (cmd == null) {
             return shell.handleDefaultCommands(null);
         }
 
-        final FontManagerService.SystemFontSettings settings = mService.getCurrentFontSettings();
-
-        switch (cmd) {
-            case "dump":
-                if (!DumpUtils.checkDumpPermission(ctx, TAG, shell.getErrPrintWriter())) {
-                    return 1;
-                }
-                final IndentingPrintWriter writer =
-                        new IndentingPrintWriter(shell.getOutPrintWriter(), "  ");
-                String nextArg = shell.getNextArg();
-                if (nextArg == null) {
-                    dumpFontConfig(writer, settings.getSystemFontConfig());
-                } else {
-                    final Map<String, FontFamily[]> fallbackMap = settings.getSystemFallbackMap();
-                    FontFamily[] families = fallbackMap.get(nextArg);
-                    if (families == null) {
-                        writer.println("Font Family \"" + nextArg + "\" not found");
-                    } else {
-                        dumpFallback(writer, families);
-                    }
-                }
-                return 0;
-            default:
-                shell.handleDefaultCommands(cmd);
+        try {
+            switch (cmd) {
+                case "dump":
+                    return dump(shell);
+                case "update":
+                    return update(shell);
+                case "clear":
+                    return clear(shell);
+                case "status":
+                    return status(shell);
+                default:
+                    return shell.handleDefaultCommands(cmd);
+            }
+        } catch (SystemFontException e) {
+            writeCommandResult(shell, e);
+            return 1;
         }
-        return 0;
     }
 }
diff --git a/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java
new file mode 100644
index 0000000..f0d14ba
--- /dev/null
+++ b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.graphics.fonts;
+
+import android.annotation.NonNull;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/* package */ class PersistentSystemFontConfig {
+    private static final String TAG = "PersistentSystemFontConfig";
+
+    private static final String TAG_ROOT = "fontConfig";
+    private static final String TAG_LAST_MODIFIED_DATE = "lastModifiedDate";
+    private static final String TAG_VALUE = "value";
+
+    /* package */ static class Config {
+        public long lastModifiedDate;
+
+        public void reset() {
+            lastModifiedDate = 0;
+        }
+
+        public void copyTo(@NonNull Config out) {
+            out.lastModifiedDate = lastModifiedDate;
+        }
+    }
+
+    /**
+     * Read config XML and write to out argument.
+     */
+    public static void loadFromXml(@NonNull InputStream is, @NonNull Config out)
+            throws XmlPullParserException, IOException {
+        out.reset();
+        TypedXmlPullParser parser = Xml.resolvePullParser(is);
+
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+            final int depth = parser.getDepth();
+            final String tag = parser.getName();
+            if (depth == 1) {
+                if (!TAG_ROOT.equals(tag)) {
+                    Slog.e(TAG, "Invalid root tag: " + tag);
+                    return;
+                }
+            } else if (depth == 2) {
+                switch (tag) {
+                    case TAG_LAST_MODIFIED_DATE:
+                        out.lastModifiedDate = parseLongAttribute(parser, TAG_VALUE, 0);
+                        break;
+                    default:
+                        Slog.w(TAG, "Skipping unknown tag: " + tag);
+                }
+            }
+        }
+
+    }
+
+    /**
+     * Write config to OutputStream as XML file.
+     */
+    public static void writeToXml(@NonNull OutputStream os, @NonNull Config config)
+            throws IOException {
+        TypedXmlSerializer out = Xml.resolveSerializer(os);
+        out.startDocument(null /* encoding */, true /* standalone */);
+
+        out.startTag(null, TAG_ROOT);
+        out.startTag(null, TAG_LAST_MODIFIED_DATE);
+        out.attribute(null, TAG_VALUE, Long.toString(config.lastModifiedDate));
+        out.endTag(null, TAG_LAST_MODIFIED_DATE);
+        out.endTag(null, TAG_ROOT);
+
+        out.endDocument();
+    }
+
+    private static long parseLongAttribute(TypedXmlPullParser parser, String attr, long defValue) {
+        final String value = parser.getAttributeValue(null /* namespace */, attr);
+        if (TextUtils.isEmpty(value)) {
+            return defValue;
+        }
+        try {
+            return Long.parseLong(value);
+        } catch (NumberFormatException e) {
+            return defValue;
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index 8da579f..720105d 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -16,18 +16,30 @@
 
 package com.android.server.graphics.fonts;
 
+import static com.android.server.graphics.fonts.FontManagerService.SystemFontException;
+
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.fonts.FontManager;
+import android.graphics.fonts.SystemFonts;
 import android.os.FileUtils;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.text.FontConfig;
 import android.util.Base64;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.security.SecureRandom;
+import java.time.Instant;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -39,6 +51,8 @@
     // TODO: Support .otf
     private static final String ALLOWED_EXTENSION = ".ttf";
 
+    private static final String CONFIG_XML_FILE = "/data/fonts/config/config.xml";
+
     /** Interface to mock font file access in tests. */
     interface FontFileParser {
         String getPostScriptName(File file) throws IOException;
@@ -55,6 +69,15 @@
         boolean rename(File src, File dest);
     }
 
+    /** Interface to mock persistent configuration */
+    interface PersistentConfig {
+        void loadFromXml(PersistentSystemFontConfig.Config out)
+                throws XmlPullParserException, IOException;
+        void writeToXml(PersistentSystemFontConfig.Config config)
+                throws IOException;
+        boolean rename(File src, File dest);
+    }
+
     /** Data class to hold font file path and revision. */
     private static final class FontFileInfo {
         private final File mFile;
@@ -87,6 +110,16 @@
     private final List<File> mPreinstalledFontDirs;
     private final FontFileParser mParser;
     private final FsverityUtil mFsverityUtil;
+    private final File mConfigFile;
+    private final File mTmpConfigFile;
+
+    @GuardedBy("UpdatableFontDir.this")
+    private final PersistentSystemFontConfig.Config mConfig =
+            new PersistentSystemFontConfig.Config();
+
+    @GuardedBy("UpdatableFontDir.this")
+    private int mConfigVersion = 1;
+
     /**
      * A mutable map containing mapping from font file name (e.g. "NotoColorEmoji.ttf") to {@link
      * FontFileInfo}. All files in this map are validated, and have higher revision numbers than
@@ -101,6 +134,20 @@
         mPreinstalledFontDirs = preinstalledFontDirs;
         mParser = parser;
         mFsverityUtil = fsverityUtil;
+        mConfigFile = new File(CONFIG_XML_FILE);
+        mTmpConfigFile = new File(CONFIG_XML_FILE + ".tmp");
+        loadFontFileMap();
+    }
+
+    // For unit testing
+    UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser,
+            FsverityUtil fsverityUtil, File configFile) {
+        mFilesDir = filesDir;
+        mPreinstalledFontDirs = preinstalledFontDirs;
+        mParser = parser;
+        mFsverityUtil = fsverityUtil;
+        mConfigFile = configFile;
+        mTmpConfigFile = new File(configFile.getAbsoluteFile() + ".tmp");
         loadFontFileMap();
     }
 
@@ -108,6 +155,13 @@
         // TODO: SIGBUS crash protection
         synchronized (UpdatableFontDir.this) {
             boolean success = false;
+
+            try (FileInputStream fis = new FileInputStream(mConfigFile)) {
+                PersistentSystemFontConfig.loadFromXml(fis, mConfig);
+            } catch (IOException | XmlPullParserException e) {
+                mConfig.reset();
+            }
+
             mFontFileInfoMap.clear();
             try {
                 File[] dirs = mFilesDir.listFiles();
@@ -117,13 +171,13 @@
                     File[] files = dir.listFiles();
                     if (files == null || files.length != 1) return;
                     FontFileInfo fontFileInfo = validateFontFile(files[0]);
-                    if (fontFileInfo == null) {
-                        Slog.w(TAG, "Broken file is found. Clearing files.");
-                        return;
-                    }
-                    addFileToMapLocked(fontFileInfo, true /* deleteOldFile */);
+                    addFileToMapIfNewerLocked(fontFileInfo, true /* deleteOldFile */);
                 }
                 success = true;
+            } catch (Throwable t) {
+                // If something happened during loading system fonts, clear all contents in finally
+                // block. Here, just dumping errors.
+                Slog.e(TAG, "Failed to load font mappings.", t);
             } finally {
                 // Delete all files just in case if we find a problematic file.
                 if (!success) {
@@ -134,6 +188,24 @@
         }
     }
 
+    /* package */ void clearUpdates() throws SystemFontException {
+        synchronized (UpdatableFontDir.this) {
+            mFontFileInfoMap.clear();
+            FileUtils.deleteContents(mFilesDir);
+
+            mConfig.reset();
+            mConfig.lastModifiedDate = Instant.now().getEpochSecond();
+            try (FileOutputStream fos = new FileOutputStream(mConfigFile)) {
+                PersistentSystemFontConfig.writeToXml(fos, mConfig);
+            } catch (Exception e) {
+                throw new SystemFontException(
+                        FontManager.ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE,
+                        "Failed to write config XML.", e);
+            }
+            mConfigVersion++;
+        }
+    }
+
     /**
      * Installs a new font file, or updates an existing font file.
      *
@@ -143,38 +215,108 @@
      *
      * @param fd             A file descriptor to the font file.
      * @param pkcs7Signature A PKCS#7 detached signature to enable fs-verity for the font file.
+     * @throws SystemFontException if error occurs.
      */
-    void installFontFile(FileDescriptor fd, byte[] pkcs7Signature) throws IOException {
+    void installFontFile(FileDescriptor fd, byte[] pkcs7Signature) throws SystemFontException {
         synchronized (UpdatableFontDir.this) {
             File newDir = getRandomDir(mFilesDir);
             if (!newDir.mkdir()) {
-                // TODO: Define and return an error code for API
-                throw new IOException("Failed to create a new dir");
+                throw new SystemFontException(
+                        FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
+                        "Failed to create font directory.");
+            }
+            try {
+                // Make newDir executable so that apps can access font file inside newDir.
+                Os.chmod(newDir.getAbsolutePath(), 0711);
+            } catch (ErrnoException e) {
+                throw new SystemFontException(
+                        FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
+                        "Failed to change mode to 711", e);
             }
             boolean success = false;
             try {
                 File tempNewFontFile = new File(newDir, "font.ttf");
                 try (FileOutputStream out = new FileOutputStream(tempNewFontFile)) {
                     FileUtils.copy(fd, out.getFD());
+                } catch (IOException e) {
+                    throw new SystemFontException(
+                            FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
+                            "Failed to write font file to storage.", e);
                 }
-                // Do not parse font file before setting up fs-verity.
-                // setUpFsverity throws IOException if failed.
-                mFsverityUtil.setUpFsverity(tempNewFontFile.getAbsolutePath(), pkcs7Signature);
-                String postScriptName = mParser.getPostScriptName(tempNewFontFile);
+                try {
+                    // Do not parse font file before setting up fs-verity.
+                    // setUpFsverity throws IOException if failed.
+                    mFsverityUtil.setUpFsverity(tempNewFontFile.getAbsolutePath(),
+                            pkcs7Signature);
+                } catch (IOException e) {
+                    throw new SystemFontException(
+                            FontManager.ERROR_CODE_VERIFICATION_FAILURE,
+                            "Failed to setup fs-verity.", e);
+                }
+                String postScriptName;
+                try {
+                    postScriptName = mParser.getPostScriptName(tempNewFontFile);
+                } catch (IOException e) {
+                    throw new SystemFontException(
+                            FontManager.ERROR_CODE_INVALID_FONT_FILE,
+                            "Failed to read PostScript name from font file", e);
+                }
+                if (postScriptName == null) {
+                    throw new SystemFontException(
+                            FontManager.ERROR_CODE_MISSING_POST_SCRIPT_NAME,
+                            "Failed to read PostScript name from font file");
+                }
                 File newFontFile = new File(newDir, postScriptName + ALLOWED_EXTENSION);
                 if (!mFsverityUtil.rename(tempNewFontFile, newFontFile)) {
-                    // TODO: Define and return an error code for API
-                    throw new IOException("Failed to rename");
+                    throw new SystemFontException(
+                            FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
+                            "Failed to move verified font file.");
+                }
+                try {
+                    // Make the font file readable by apps.
+                    Os.chmod(newFontFile.getAbsolutePath(), 0644);
+                } catch (ErrnoException e) {
+                    throw new SystemFontException(
+                            FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
+                            "Failed to change mode to 711", e);
                 }
                 FontFileInfo fontFileInfo = validateFontFile(newFontFile);
-                if (fontFileInfo == null) {
-                    // TODO: Define and return an error code for API
-                    throw new IllegalArgumentException("Invalid file");
+
+                // Write config file.
+                PersistentSystemFontConfig.Config copied = new PersistentSystemFontConfig.Config();
+                mConfig.copyTo(copied);
+
+                copied.lastModifiedDate = Instant.now().getEpochSecond();
+                try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) {
+                    PersistentSystemFontConfig.writeToXml(fos, copied);
+                } catch (Exception e) {
+                    throw new SystemFontException(
+                            FontManager.ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE,
+                            "Failed to write config XML.", e);
                 }
-                if (!addFileToMapLocked(fontFileInfo, false)) {
-                    // TODO: Define and return an error code for API
-                    throw new IllegalArgumentException("Version downgrade");
+
+                // Backup the mapping for rollback.
+                HashMap<String, FontFileInfo> backup = new HashMap<>(mFontFileInfoMap);
+                if (!addFileToMapIfNewerLocked(fontFileInfo, false)) {
+                    throw new SystemFontException(
+                            FontManager.ERROR_CODE_DOWNGRADING,
+                            "Downgrading font file is forbidden.");
                 }
+
+                if (!mFsverityUtil.rename(mTmpConfigFile, mConfigFile)) {
+                    // If we fail to stage the config file, need to rollback the config.
+                    mFontFileInfoMap.clear();
+                    mFontFileInfoMap.putAll(backup);
+                    throw new SystemFontException(
+                            FontManager.ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE,
+                            "Failed to stage the config file.");
+                }
+
+
+                // Now font update is succeeded. Update config version.
+                copied.copyTo(mConfig);
+                mConfigVersion++;
+
                 success = true;
             } finally {
                 if (!success) {
@@ -207,7 +349,7 @@
      * higher than the currently used font file (either in {@link #mFontFileInfoMap} or {@link
      * #mPreinstalledFontDirs}).
      */
-    private boolean addFileToMapLocked(FontFileInfo fontFileInfo, boolean deleteOldFile) {
+    private boolean addFileToMapIfNewerLocked(FontFileInfo fontFileInfo, boolean deleteOldFile) {
         String name = fontFileInfo.getFile().getName();
         FontFileInfo existingInfo = mFontFileInfoMap.get(name);
         final boolean shouldAddToMap;
@@ -224,13 +366,12 @@
                 FileUtils.deleteContentsAndDir(existingInfo.getRandomizedFontDir());
             }
             mFontFileInfoMap.put(name, fontFileInfo);
-            return true;
         } else {
             if (deleteOldFile) {
                 FileUtils.deleteContentsAndDir(fontFileInfo.getRandomizedFontDir());
             }
-            return false;
         }
+        return shouldAddToMap;
     }
 
     private long getPreinstalledFontRevision(String name) {
@@ -255,20 +396,23 @@
      * returns a {@link FontFileInfo} on success. This method does not check if the font revision
      * is higher than the currently used font.
      */
-    @Nullable
-    private FontFileInfo validateFontFile(File file) {
+    @NonNull
+    private FontFileInfo validateFontFile(File file) throws SystemFontException {
         if (!mFsverityUtil.hasFsverity(file.getAbsolutePath())) {
-            Slog.w(TAG, "Font validation failed. Fs-verity is not enabled: " + file);
-            return null;
+            throw new SystemFontException(
+                    FontManager.ERROR_CODE_VERIFICATION_FAILURE,
+                    "Font validation failed. Fs-verity is not enabled: " + file);
         }
         if (!validateFontFileName(file)) {
-            Slog.w(TAG, "Font validation failed. Could not validate font file name: " + file);
-            return null;
+            throw new SystemFontException(
+                    FontManager.ERROR_CODE_FONT_NAME_MISMATCH,
+                    "Font validation failed. Could not validate font file name: " + file);
         }
         long revision = getFontRevision(file);
         if (revision == -1) {
-            Slog.w(TAG, "Font validation failed. Could not read font revision: " + file);
-            return null;
+            throw new SystemFontException(
+                    FontManager.ERROR_CODE_INVALID_FONT_FILE,
+                    "Font validation failed. Could not read font revision: " + file);
         }
         return new FontFileInfo(file, revision);
     }
@@ -318,4 +462,14 @@
         }
         return map;
     }
+
+    /* package */ FontConfig getSystemFontConfig() {
+        synchronized (UpdatableFontDir.this) {
+            return SystemFonts.getSystemFontConfig(
+                    getFontFileMap(),
+                    mConfig.lastModifiedDate,
+                    mConfigVersion
+            );
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 143ec15..6308ace 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3947,58 +3947,61 @@
     }
 
     @Override
-    public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
-        // By this IPC call, only a process which shares the same uid with the IME can add
-        // additional input method subtypes to the IME.
-        if (TextUtils.isEmpty(imiId) || subtypes == null) return;
-        final ArrayList<InputMethodSubtype> toBeAdded = new ArrayList<>();
-        for (InputMethodSubtype subtype : subtypes) {
-            if (!toBeAdded.contains(subtype)) {
-                toBeAdded.add(subtype);
-            } else {
-                Slog.w(TAG, "Duplicated subtype definition found: "
-                        + subtype.getLocale() + ", " + subtype.getMode());
+    public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes,
+            IVoidResultCallback resultCallback) {
+        CallbackUtils.onResult(resultCallback, () -> {
+            // By this IPC call, only a process which shares the same uid with the IME can add
+            // additional input method subtypes to the IME.
+            if (TextUtils.isEmpty(imiId) || subtypes == null) return;
+            final ArrayList<InputMethodSubtype> toBeAdded = new ArrayList<>();
+            for (InputMethodSubtype subtype : subtypes) {
+                if (!toBeAdded.contains(subtype)) {
+                    toBeAdded.add(subtype);
+                } else {
+                    Slog.w(TAG, "Duplicated subtype definition found: "
+                            + subtype.getLocale() + ", " + subtype.getMode());
+                }
             }
-        }
-        synchronized (mMethodMap) {
-            if (!calledFromValidUserLocked()) {
-                return;
-            }
-            if (!mSystemReady) {
-                return;
-            }
-            final InputMethodInfo imi = mMethodMap.get(imiId);
-            if (imi == null) return;
-            final String[] packageInfos;
-            try {
-                packageInfos = mIPackageManager.getPackagesForUid(Binder.getCallingUid());
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to get package infos");
-                return;
-            }
-            if (packageInfos != null) {
-                final int packageNum = packageInfos.length;
-                for (int i = 0; i < packageNum; ++i) {
-                    if (packageInfos[i].equals(imi.getPackageName())) {
-                        if (subtypes.length > 0) {
-                            mAdditionalSubtypeMap.put(imi.getId(), toBeAdded);
-                        } else {
-                            mAdditionalSubtypeMap.remove(imi.getId());
+            synchronized (mMethodMap) {
+                if (!calledFromValidUserLocked()) {
+                    return;
+                }
+                if (!mSystemReady) {
+                    return;
+                }
+                final InputMethodInfo imi = mMethodMap.get(imiId);
+                if (imi == null) return;
+                final String[] packageInfos;
+                try {
+                    packageInfos = mIPackageManager.getPackagesForUid(Binder.getCallingUid());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to get package infos");
+                    return;
+                }
+                if (packageInfos != null) {
+                    final int packageNum = packageInfos.length;
+                    for (int i = 0; i < packageNum; ++i) {
+                        if (packageInfos[i].equals(imi.getPackageName())) {
+                            if (subtypes.length > 0) {
+                                mAdditionalSubtypeMap.put(imi.getId(), toBeAdded);
+                            } else {
+                                mAdditionalSubtypeMap.remove(imi.getId());
+                            }
+                            AdditionalSubtypeUtils.save(mAdditionalSubtypeMap, mMethodMap,
+                                    mSettings.getCurrentUserId());
+                            final long ident = Binder.clearCallingIdentity();
+                            try {
+                                buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
+                            } finally {
+                                Binder.restoreCallingIdentity(ident);
+                            }
+                            return;
                         }
-                        AdditionalSubtypeUtils.save(mAdditionalSubtypeMap, mMethodMap,
-                                mSettings.getCurrentUserId());
-                        final long ident = Binder.clearCallingIdentity();
-                        try {
-                            buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
-                        } finally {
-                            Binder.restoreCallingIdentity(ident);
-                        }
-                        return;
                     }
                 }
             }
-        }
-        return;
+            return;
+        });
     }
 
     /**
@@ -4103,16 +4106,21 @@
     }
 
     @Override
-    public void removeImeSurface() {
-        mContext.enforceCallingPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW, null);
-        mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE));
+    public void removeImeSurface(IVoidResultCallback resultCallback) {
+        CallbackUtils.onResult(resultCallback, () -> {
+            mContext.enforceCallingPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW, null);
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE));
+        });
     }
 
     @Override
-    public void removeImeSurfaceFromWindow(IBinder windowToken) {
-        // No permission check, because we'll only execute the request if the calling window is
-        // also the current IME client.
-        mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE_FROM_WINDOW, windowToken).sendToTarget();
+    public void removeImeSurfaceFromWindow(IBinder windowToken,
+            IVoidResultCallback resultCallback) {
+        CallbackUtils.onResult(resultCallback, () -> {
+            // No permission check, because we'll only execute the request if the calling window is
+            // also the current IME client.
+            mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE_FROM_WINDOW, windowToken).sendToTarget();
+        });
     }
 
     /**
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 2dd7096..7f9c766 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1502,14 +1502,17 @@
 
         @BinderThread
         @Override
-        public void removeImeSurface() {
+        public void removeImeSurface(IVoidResultCallback resultCallback) {
             reportNotSupported();
+            CallbackUtils.onResult(resultCallback, () -> { });
         }
 
         @BinderThread
         @Override
-        public void removeImeSurfaceFromWindow(IBinder windowToken) {
+        public void removeImeSurfaceFromWindow(IBinder windowToken,
+                IVoidResultCallback resultCallback) {
             reportNotSupported();
+            CallbackUtils.onResult(resultCallback, () -> { });
         }
 
         @BinderThread
@@ -1815,8 +1818,10 @@
 
         @BinderThread
         @Override
-        public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
+        public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes,
+                IVoidResultCallback resultCallback) {
             reportNotSupported();
+            CallbackUtils.onResult(resultCallback, () -> { });
         }
 
         @BinderThread
diff --git a/services/core/java/com/android/server/location/timezone/OWNERS b/services/core/java/com/android/server/location/timezone/OWNERS
deleted file mode 100644
index 28aff18..0000000
--- a/services/core/java/com/android/server/location/timezone/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 847766
-nfuller@google.com
-include /core/java/android/app/timedetector/OWNERS
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 7dd961a..b92a83f 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -89,6 +89,7 @@
     private static final String CHILD_PROFILE_LOCK_FILE = "gatekeeper.profile.key";
 
     private static final String REBOOT_ESCROW_FILE = "reboot.escrow.key";
+    private static final String REBOOT_ESCROW_SERVER_BLOB = "reboot.escrow.server.blob.key";
 
     private static final String SYNTHETIC_PASSWORD_DIRECTORY = "spblob/";
 
@@ -318,6 +319,22 @@
         deleteFile(getRebootEscrowFile(userId));
     }
 
+    public void writeRebootEscrowServerBlob(byte[] serverBlob) {
+        writeFile(getRebootEscrowServerBlob(), serverBlob);
+    }
+
+    public byte[] readRebootEscrowServerBlob() {
+        return readFile(getRebootEscrowServerBlob());
+    }
+
+    public boolean hasRebootEscrowServerBlob() {
+        return hasFile(getRebootEscrowServerBlob());
+    }
+
+    public void removeRebootEscrowServerBlob() {
+        deleteFile(getRebootEscrowServerBlob());
+    }
+
     public boolean hasPassword(int userId) {
         return hasFile(getLockPasswordFilename(userId));
     }
@@ -446,6 +463,12 @@
         return getLockCredentialFilePathForUser(userId, REBOOT_ESCROW_FILE);
     }
 
+    @VisibleForTesting
+    String getRebootEscrowServerBlob() {
+        // There is a single copy of server blob for all users.
+        return getLockCredentialFilePathForUser(UserHandle.USER_SYSTEM, REBOOT_ESCROW_SERVER_BLOB);
+    }
+
     private String getLockCredentialFilePathForUser(int userId, String basename) {
         String dataSystemDirectory = Environment.getDataDirectory().getAbsolutePath() +
                         SYSTEM_DIRECTORY;
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index fbec915..06962d4 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -124,26 +124,28 @@
     static class Injector {
         protected Context mContext;
         private final RebootEscrowKeyStoreManager mKeyStoreManager;
-        private final RebootEscrowProviderInterface mRebootEscrowProvider;
+        private final LockSettingsStorage mStorage;
+        private RebootEscrowProviderInterface mRebootEscrowProvider;
 
-        Injector(Context context) {
+        Injector(Context context, LockSettingsStorage storage) {
             mContext = context;
+            mStorage = storage;
             mKeyStoreManager = new RebootEscrowKeyStoreManager();
+        }
 
-            RebootEscrowProviderInterface rebootEscrowProvider = null;
-            // TODO(xunchang) add implementation for server based ror.
+        private RebootEscrowProviderInterface createRebootEscrowProvider() {
+            RebootEscrowProviderInterface rebootEscrowProvider;
             if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
                     "server_based_ror_enabled", false)) {
-                Slog.e(TAG, "Server based ror isn't implemented yet.");
+                rebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mContext, mStorage);
             } else {
                 rebootEscrowProvider = new RebootEscrowProviderHalImpl();
             }
 
-            if (rebootEscrowProvider != null && rebootEscrowProvider.hasRebootEscrowSupport()) {
-                mRebootEscrowProvider = rebootEscrowProvider;
-            } else {
-                mRebootEscrowProvider = null;
+            if (rebootEscrowProvider.hasRebootEscrowSupport()) {
+                return rebootEscrowProvider;
             }
+            return null;
         }
 
         public Context getContext() {
@@ -159,6 +161,12 @@
         }
 
         public RebootEscrowProviderInterface getRebootEscrowProvider() {
+            // Initialize for the provider lazily. Because the device_config and service
+            // implementation apps may change when system server is running.
+            if (mRebootEscrowProvider == null) {
+                mRebootEscrowProvider = createRebootEscrowProvider();
+            }
+
             return mRebootEscrowProvider;
         }
 
@@ -177,7 +185,7 @@
     }
 
     RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage) {
-        this(new Injector(context), callbacks, storage);
+        this(new Injector(context, storage), callbacks, storage);
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
new file mode 100644
index 0000000..ba1a680
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.RemoteException;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection;
+
+import java.io.IOException;
+import java.util.concurrent.TimeoutException;
+
+import javax.crypto.SecretKey;
+
+/**
+ * An implementation of the {@link RebootEscrowProviderInterface} by communicating with server to
+ * encrypt & decrypt the blob.
+ */
+class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterface {
+    private static final String TAG = "RebootEscrowProvider";
+
+    // Timeout for service binding
+    private static final long DEFAULT_SERVICE_TIMEOUT_IN_SECONDS = 10;
+
+    /**
+     * Use the default lifetime of 10 minutes. The lifetime covers the following activities:
+     * Server wrap secret -> device reboot -> server unwrap blob.
+     */
+    private static final long DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS = 600_1000;
+
+    private final LockSettingsStorage mStorage;
+
+    private final Injector mInjector;
+
+    static class Injector {
+        private ResumeOnRebootServiceConnection mServiceConnection = null;
+
+        Injector(Context context) {
+            mServiceConnection = new ResumeOnRebootServiceProvider(context).getServiceConnection();
+            if (mServiceConnection == null) {
+                Slog.e(TAG, "Failed to resolve resume on reboot server service.");
+            }
+        }
+
+        Injector(ResumeOnRebootServiceConnection serviceConnection) {
+            mServiceConnection = serviceConnection;
+        }
+
+        @Nullable
+        private ResumeOnRebootServiceConnection getServiceConnection() {
+            return mServiceConnection;
+        }
+
+        long getServiceTimeoutInSeconds() {
+            return DeviceConfig.getLong(DeviceConfig.NAMESPACE_OTA,
+                    "server_based_service_timeout_in_seconds",
+                    DEFAULT_SERVICE_TIMEOUT_IN_SECONDS);
+        }
+
+        long getServerBlobLifetimeInMillis() {
+            return DeviceConfig.getLong(DeviceConfig.NAMESPACE_OTA,
+                    "server_based_server_blob_lifetime_in_millis",
+                    DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS);
+        }
+    }
+
+    RebootEscrowProviderServerBasedImpl(Context context, LockSettingsStorage storage) {
+        this(storage, new Injector(context));
+    }
+
+    @VisibleForTesting
+    RebootEscrowProviderServerBasedImpl(LockSettingsStorage storage, Injector injector) {
+        mStorage = storage;
+        mInjector = injector;
+    }
+
+    @Override
+    public boolean hasRebootEscrowSupport() {
+        return mInjector.getServiceConnection() != null;
+    }
+
+    private byte[] unwrapServerBlob(byte[] serverBlob, SecretKey decryptionKey) throws
+            TimeoutException, RemoteException, IOException {
+        ResumeOnRebootServiceConnection serviceConnection = mInjector.getServiceConnection();
+        if (serviceConnection == null) {
+            Slog.w(TAG, "Had reboot escrow data for users, but resume on reboot server"
+                    + " service is unavailable");
+            return null;
+        }
+
+        // Decrypt with k_k from the key store first.
+        byte[] decryptedBlob = AesEncryptionUtil.decrypt(decryptionKey, serverBlob);
+        if (decryptedBlob == null) {
+            Slog.w(TAG, "Decrypted server blob should not be null");
+            return null;
+        }
+
+        // Ask the server connection service to decrypt the inner layer, to get the reboot
+        // escrow key (k_s).
+        serviceConnection.bindToService(mInjector.getServiceTimeoutInSeconds());
+        byte[] escrowKeyBytes = serviceConnection.unwrap(decryptedBlob,
+                mInjector.getServiceTimeoutInSeconds());
+        serviceConnection.unbindService();
+
+        return escrowKeyBytes;
+    }
+
+    @Override
+    public RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) {
+        byte[] serverBlob = mStorage.readRebootEscrowServerBlob();
+        // Delete the server blob in storage.
+        mStorage.removeRebootEscrowServerBlob();
+        if (serverBlob == null) {
+            Slog.w(TAG, "Failed to read reboot escrow server blob from storage");
+            return null;
+        }
+
+        try {
+            byte[] escrowKeyBytes = unwrapServerBlob(serverBlob, decryptionKey);
+            if (escrowKeyBytes == null) {
+                Slog.w(TAG, "Decrypted reboot escrow key bytes should not be null");
+                return null;
+            } else if (escrowKeyBytes.length != 32) {
+                Slog.e(TAG, "Decrypted reboot escrow key has incorrect size "
+                        + escrowKeyBytes.length);
+                return null;
+            }
+
+            return RebootEscrowKey.fromKeyBytes(escrowKeyBytes);
+        } catch (TimeoutException | RemoteException | IOException e) {
+            Slog.w(TAG, "Failed to decrypt the server blob ", e);
+            return null;
+        }
+    }
+
+    @Override
+    public void clearRebootEscrowKey() {
+        mStorage.removeRebootEscrowServerBlob();
+    }
+
+    private byte[] wrapEscrowKey(byte[] escrowKeyBytes, SecretKey encryptionKey) throws
+            TimeoutException, RemoteException, IOException {
+        ResumeOnRebootServiceConnection serviceConnection = mInjector.getServiceConnection();
+        if (serviceConnection == null) {
+            Slog.w(TAG, "Failed to encrypt the reboot escrow key: resume on reboot server"
+                    + " service is unavailable");
+            return null;
+        }
+
+        serviceConnection.bindToService(mInjector.getServiceTimeoutInSeconds());
+        // Ask the server connection service to encrypt the reboot escrow key.
+        byte[] serverEncryptedBlob = serviceConnection.wrapBlob(escrowKeyBytes,
+                mInjector.getServerBlobLifetimeInMillis(), mInjector.getServiceTimeoutInSeconds());
+        serviceConnection.unbindService();
+
+        if (serverEncryptedBlob == null) {
+            Slog.w(TAG, "Server encrypted reboot escrow key cannot be null");
+            return null;
+        }
+
+        // Additionally wrap the server blob with a local key.
+        return AesEncryptionUtil.encrypt(encryptionKey, serverEncryptedBlob);
+    }
+
+    @Override
+    public boolean storeRebootEscrowKey(RebootEscrowKey escrowKey, SecretKey encryptionKey) {
+        mStorage.removeRebootEscrowServerBlob();
+        try {
+            byte[] wrappedBlob = wrapEscrowKey(escrowKey.getKeyBytes(), encryptionKey);
+            if (wrappedBlob == null) {
+                Slog.w(TAG, "Failed to encrypt the reboot escrow key");
+                return false;
+            }
+            mStorage.writeRebootEscrowServerBlob(wrappedBlob);
+
+            Slog.i(TAG, "Reboot escrow key encrypted and stored.");
+            return true;
+        } catch (TimeoutException | RemoteException | IOException e) {
+            Slog.w(TAG, "Failed to encrypt the reboot escrow key ", e);
+        }
+
+        return false;
+    }
+}
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index 0a4d17f..e2e5046 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -141,6 +141,11 @@
                 packageName != null ? packageName : "");
     }
 
+    public static MediaButtonReceiverHolder create(int userId, ComponentName broadcastReceiver) {
+        return new MediaButtonReceiverHolder(userId, null, broadcastReceiver,
+                COMPONENT_TYPE_BROADCAST);
+    }
+
     private MediaButtonReceiverHolder(int userId, PendingIntent pendingIntent,
             ComponentName componentName, @ComponentType int componentType) {
         mUserId = userId;
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index ea6e7d7..ae58d4c 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -18,6 +18,7 @@
 
 import android.annotation.Nullable;
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
@@ -858,6 +859,21 @@
         }
 
         @Override
+        public void setMediaButtonBroadcastReceiver(ComponentName receiver) throws RemoteException {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
+                        != 0) {
+                    return;
+                }
+                mMediaButtonReceiverHolder = MediaButtonReceiverHolder.create(mUserId, receiver);
+                mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
         public void setLaunchPendingIntent(PendingIntent pi) throws RemoteException {
             mLaunchIntent = pi;
         }
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index a12932a8..de85d9e 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -30,9 +30,9 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
 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;
@@ -508,7 +508,8 @@
 
             for (ApexInfo ai : allPkgs) {
                 File apexFile = new File(ai.modulePath);
-                parallelPackageParser.submit(apexFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
+                parallelPackageParser.submit(apexFile,
+                        ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
                 parsingApexInfo.put(apexFile, ai);
             }
 
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index ff3a12a..5373f99 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -24,7 +24,7 @@
 import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
 import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
 import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
-import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
+import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION;
 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
@@ -39,6 +39,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageParser;
 import android.content.pm.Signature;
+import android.content.pm.parsing.ApkLiteParseUtils;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.os.incremental.IncrementalManager;
@@ -171,7 +172,7 @@
      * @throws IllegalArgumentException if the code path is not an .apk.
      */
     public static String buildDigestsPathForApk(String codePath) {
-        if (!PackageParser.isApkPath(codePath)) {
+        if (!ApkLiteParseUtils.isApkPath(codePath)) {
             throw new IllegalStateException("Code path is not an apk " + codePath);
         }
         return codePath.substring(0, codePath.length() - APK_FILE_EXTENSION.length())
diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java
index 780c522..f5ec595 100644
--- a/services/core/java/com/android/server/pm/IncrementalStates.java
+++ b/services/core/java/com/android/server/pm/IncrementalStates.java
@@ -61,12 +61,12 @@
 
     public IncrementalStates() {
         // By default the package is not startable and not fully loaded (i.e., is loading)
-        this(false, true);
+        this(false, true, 0);
     }
 
-    public IncrementalStates(boolean isStartable, boolean isLoading) {
+    public IncrementalStates(boolean isStartable, boolean isLoading, float loadingProgress) {
         mStartableState = new StartableState(isStartable);
-        mLoadingState = new LoadingState(isLoading);
+        mLoadingState = new LoadingState(isLoading, loadingProgress);
         mStatusConsumer = new StatusConsumer();
     }
 
@@ -405,9 +405,10 @@
         private boolean mIsLoading;
         private float mProgress;
 
-        LoadingState(boolean isLoading) {
+        LoadingState(boolean isLoading, float loadingProgress) {
             mIsLoading = isLoading;
-            mProgress = isLoading ? 0 : 1;
+            // loading progress is reset to 1 if loading has finished
+            mProgress = isLoading ? loadingProgress : 1;
         }
 
         public boolean isLoading() {
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 71b99bd..13fe8a0 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -17,7 +17,7 @@
 package com.android.server.pm;
 
 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
-import static android.content.pm.PackageParser.isApkFile;
+import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.os.incremental.IncrementalManager.isIncrementalPath;
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index e143bd0..e218dc1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -81,11 +81,12 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.ApkLite;
-import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.parsing.ApkLite;
 import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.graphics.Bitmap;
@@ -2671,16 +2672,17 @@
 
         // Populate package name of the apex session
         mPackageName = null;
-        final ApkLite apk;
-        try {
-            apk = PackageParser.parseApkLite(
-                    mResolvedBaseFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
-        } catch (PackageParserException e) {
-            throw PackageManagerException.from(e);
+        final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+        final ParseResult<ApkLite> ret = ApkLiteParseUtils.parseApkLite(input.reset(),
+                mResolvedBaseFile, ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
+        if (ret.isError()) {
+            throw new PackageManagerException(ret.getErrorCode(), ret.getErrorMessage(),
+                    ret.getException());
         }
+        final ApkLite apk = ret.getResult();
 
         if (mPackageName == null) {
-            mPackageName = apk.packageName;
+            mPackageName = apk.getPackageName();
             mVersionCode = apk.getLongVersionCode();
         }
     }
@@ -2745,29 +2747,29 @@
 
         // Verify that all staged packages are internally consistent
         final ArraySet<String> stagedSplits = new ArraySet<>();
-        final ArrayMap<String, PackageParser.ApkLite> splitApks = new ArrayMap<>();
-        ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+        final ArrayMap<String, ApkLite> splitApks = new ArrayMap<>();
+        final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
         for (File addedFile : addedFiles) {
-            ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(input.reset(),
-                    addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
+            final ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(input.reset(),
+                    addedFile, ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
             if (result.isError()) {
                 throw new PackageManagerException(result.getErrorCode(),
                         result.getErrorMessage(), result.getException());
             }
 
             final ApkLite apk = result.getResult();
-            if (!stagedSplits.add(apk.splitName)) {
+            if (!stagedSplits.add(apk.getSplitName())) {
                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
-                        "Split " + apk.splitName + " was defined multiple times");
+                        "Split " + apk.getSplitName() + " was defined multiple times");
             }
 
             // Use first package to define unknown values
             if (mPackageName == null) {
-                mPackageName = apk.packageName;
+                mPackageName = apk.getPackageName();
                 mVersionCode = apk.getLongVersionCode();
             }
             if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
-                mSigningDetails = apk.signingDetails;
+                mSigningDetails = apk.getSigningDetails();
             }
 
             assertApkConsistentLocked(String.valueOf(addedFile), apk);
@@ -2780,10 +2782,10 @@
             }
 
             // Yell loudly if installers drop attribute installLocation when apps explicitly set.
-            if (apk.installLocation != PackageInfo.INSTALL_LOCATION_UNSPECIFIED) {
+            if (apk.getInstallLocation() != PackageInfo.INSTALL_LOCATION_UNSPECIFIED) {
                 final String installerPackageName = getInstallerPackageName();
                 if (installerPackageName != null
-                        && (params.installLocation != apk.installLocation)) {
+                        && (params.installLocation != apk.getInstallLocation())) {
                     Slog.wtf(TAG, installerPackageName
                             + " drops manifest attribute android:installLocation in " + targetName
                             + " for " + mPackageName);
@@ -2791,14 +2793,14 @@
             }
 
             final File targetFile = new File(stageDir, targetName);
-            resolveAndStageFileLocked(addedFile, targetFile, apk.splitName);
+            resolveAndStageFileLocked(addedFile, targetFile, apk.getSplitName());
 
             // Base is coming from session
-            if (apk.splitName == null) {
+            if (apk.getSplitName() == null) {
                 mResolvedBaseFile = targetFile;
                 baseApk = apk;
             } else {
-                splitApks.put(apk.splitName, apk);
+                splitApks.put(apk.getSplitName(), apk);
             }
         }
 
@@ -2854,7 +2856,7 @@
                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                         "Full install must include a base package");
             }
-            if (baseApk.isSplitRequired && stagedSplits.size() <= 1) {
+            if (baseApk.isSplitRequired() && stagedSplits.size() <= 1) {
                 throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
                         "Missing split for " + mPackageName);
             }
@@ -2879,10 +2881,10 @@
             }
             final PackageLite existing = pkgLiteResult.getResult();
             packageLite = existing;
-            assertPackageConsistentLocked("Existing", existing.packageName,
+            assertPackageConsistentLocked("Existing", existing.getPackageName(),
                     existing.getLongVersionCode());
             final PackageParser.SigningDetails signingDetails =
-                    unsafeGetCertsWithoutVerification(existing.baseCodePath);
+                    unsafeGetCertsWithoutVerification(existing.getBaseApkPath());
             if (!mSigningDetails.signaturesMatchExactly(signingDetails)) {
                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                         "Existing signatures are inconsistent");
@@ -2895,10 +2897,10 @@
             }
 
             // Inherit splits if not overridden.
-            if (!ArrayUtils.isEmpty(existing.splitNames)) {
-                for (int i = 0; i < existing.splitNames.length; i++) {
-                    final String splitName = existing.splitNames[i];
-                    final File splitFile = new File(existing.splitCodePaths[i]);
+            if (!ArrayUtils.isEmpty(existing.getSplitNames())) {
+                for (int i = 0; i < existing.getSplitNames().length; i++) {
+                    final String splitName = existing.getSplitNames()[i];
+                    final File splitFile = new File(existing.getSplitApkPaths()[i]);
                     final boolean splitRemoved = removeSplitList.contains(splitName);
                     if (!stagedSplits.contains(splitName) && !splitRemoved) {
                         inheritFileLocked(splitFile);
@@ -2978,8 +2980,8 @@
                 }
             }
             // For the case of split required, failed if no splits existed
-            if (packageLite.isSplitRequired) {
-                final int existingSplits = ArrayUtils.size(existing.splitNames);
+            if (packageLite.isSplitRequired()) {
+                final int existingSplits = ArrayUtils.size(existing.getSplitNames());
                 final boolean allSplitsRemoved = (existingSplits == removeSplitList.size());
                 final boolean onlyBaseFileStaged = (stagedSplits.size() == 1
                         && stagedSplits.contains(null));
@@ -2989,7 +2991,7 @@
                 }
             }
         }
-        if (packageLite.useEmbeddedDex) {
+        if (packageLite.isUseEmbeddedDex()) {
             for (File file : mResolvedStagedFiles) {
                 if (file.getName().endsWith(".apk")
                         && !DexManager.auditUncompressedDexInApk(file.getPath())) {
@@ -3002,7 +3004,7 @@
 
         final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID);
         if (isInstallerShell && isIncrementalInstallation() && mIncrementalFileStorages != null) {
-            if (!packageLite.debuggable && !packageLite.profilableByShell) {
+            if (!packageLite.isDebuggable() && !packageLite.isProfileableByShell()) {
                 mIncrementalFileStorages.disallowReadLogs();
             }
         }
@@ -3174,8 +3176,8 @@
     @GuardedBy("mLock")
     private void assertApkConsistentLocked(String tag, ApkLite apk)
             throws PackageManagerException {
-        assertPackageConsistentLocked(tag, apk.packageName, apk.getLongVersionCode());
-        if (!mSigningDetails.signaturesMatchExactly(apk.signingDetails)) {
+        assertPackageConsistentLocked(tag, apk.getPackageName(), apk.getLongVersionCode());
+        if (!mSigningDetails.signaturesMatchExactly(apk.getSigningDetails())) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                     tag + " signatures are inconsistent");
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 68b0698..c27e670 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -101,7 +101,7 @@
 import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
 import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE;
 import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
-import static android.content.pm.PackageParser.isApkFile;
+import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.os.incremental.IncrementalManager.isIncrementalPath;
 import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
@@ -211,9 +211,7 @@
 import android.content.pm.PackageManagerInternal.PackageListObserver;
 import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
 import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.ParseFlags;
 import android.content.pm.PackageParser.SigningDetails;
 import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.PackagePartitions;
@@ -241,7 +239,9 @@
 import android.content.pm.dex.DexMetadataHelper;
 import android.content.pm.dex.IArtManager;
 import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
 import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.ParsingPackageUtils.ParseFlags;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedInstrumentation;
 import android.content.pm.parsing.component.ParsedIntentInfo;
@@ -518,8 +518,7 @@
     public static final boolean DEBUG_PERMISSIONS = false;
     private static final boolean DEBUG_SHARED_LIBRARIES = false;
     public static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE;
-    public static final boolean DEBUG_CACHES = false;
-    public static final boolean TRACE_CACHES = false;
+    public static final boolean TRACE_SNAPSHOTS = false;
     private static final boolean DEBUG_PER_UID_READ_TIMEOUTS = false;
 
     // Debug output for dexopting. This is shared between PackageManagerService, OtaDexoptService
@@ -2015,7 +2014,7 @@
     }
 
     /**
-     * A computer provides the functional interface to the cache
+     * A computer provides the functional interface to the snapshot.
      */
     private interface Computer {
 
@@ -2181,13 +2180,11 @@
         private final ResolveInfo mInstantAppInstallerInfo;
         private final InstantAppRegistry mInstantAppRegistry;
         private final ApplicationInfo mLocalAndroidApplication;
+        private final AppsFilter mAppsFilter;
 
         // Immutable service attribute
         private final String mAppPredictionServicePackage;
 
-        // TODO: create cache copies of the following attributes
-        private final AppsFilter mAppsFilter;
-
         // The following are not cloned since changes to these have never
         // been guarded by the PMS lock.
         private final Context mContext;
@@ -4738,11 +4735,11 @@
     private final Computer mLiveComputer;
     // A lock-free cache for frequently called functions.
     private volatile Computer mSnapshotComputer;
-    // If true, the cached computer object is invalid (the cache is stale).
-    // The attribute is static since it may be set from outside classes.
+    // If true, the snapshot is invalid (stale).  The attribute is static since it may be
+    // set from outside classes.
     private static volatile boolean sSnapshotInvalid = true;
-    // If true, the cache is corked.  Do not create a new cache but continue to use the
-    // existing one.  This throttles cache creation during periods of churn in Package
+    // If true, the snapshot is corked.  Do not create a new snapshot but use the live
+    // computer.  This throttles snapshot creation during periods of churn in Package
     // Manager.
     private static volatile boolean sSnapshotCorked = false;
 
@@ -4754,13 +4751,121 @@
      */
     private final Object mSnapshotLock = new Object();
 
-    // A counter of all queries that hit the cache.
-    private AtomicInteger mSnapshotHits = new AtomicInteger(0);
+    // A counter of all queries that hit the current snapshot.
+    @GuardedBy("mSnapshotLock")
+    private int mSnapshotHits = 0;
 
-    // The number of queries at the last miss.  This is updated when the cache is rebuilt
-    // (guarded by mLock) and is used to report the hit run-length.
+    // A class to record snapshot statistics.
+    private static class SnapshotStatistics {
+        // A build time is "big" if it takes longer than 5ms.
+        private static final long SNAPSHOT_BIG_BUILD_TIME_NS = TimeUnit.MILLISECONDS.toNanos(5);
+
+        // A snapshot is in quick succession to the previous snapshot if it less than
+        // 100ms since the previous snapshot.
+        private static final long SNAPSHOT_QUICK_REBUILD_INTERVAL_NS =
+                TimeUnit.MILLISECONDS.toNanos(100);
+
+        // The interval between snapshot statistics logging, in ns.
+        private static final long SNAPSHOT_LOG_INTERVAL_NS = TimeUnit.MINUTES.toNanos(10);
+
+        // The throttle parameters for big build reporting.  Do not report more than this
+        // many events in a single log interval.
+        private static final int SNAPSHOT_BUILD_REPORT_LIMIT = 10;
+
+        // The time the snapshot statistics were last logged.
+        private long mStatisticsSent = 0;
+
+        // The number of build events logged since the last periodic log.
+        private int mLoggedBuilds = 0;
+
+        // The time of the last build.
+        private long mLastBuildTime = 0;
+
+        // The number of times the snapshot has been rebuilt since the statistics were
+        // last logged.
+        private int mRebuilds = 0;
+
+        // The number of times the snapshot has been used since it was rebuilt.
+        private int mReused = 0;
+
+        // The number of "big" build times since the last log.  "Big" is defined by
+        // SNAPSHOT_BIG_BUILD_TIME.
+        private int mBigBuilds = 0;
+
+        // The number of quick rebuilds.  "Quick" is defined by
+        // SNAPSHOT_QUICK_REBUILD_INTERVAL_NS.
+        private int mQuickRebuilds = 0;
+
+        // The time take to build a snapshot.  This is cumulative over the rebuilds recorded
+        // in mRebuilds, so the average time to build a snapshot is given by
+        // mBuildTimeNs/mRebuilds.
+        private int mBuildTimeNs = 0;
+
+        // The maximum build time since the last log.
+        private long mMaxBuildTimeNs = 0;
+
+        // The constant that converts ns to ms.  This is the divisor.
+        private final long NS_TO_MS = TimeUnit.MILLISECONDS.toNanos(1);
+
+        // Convert ns to an int ms.  The maximum range of this method is about 24 days.
+        // There is no expectation that an event will take longer than that.
+        private int nsToMs(long ns) {
+            return (int) (ns / NS_TO_MS);
+        }
+
+        // The single method records a rebuild.  The "now" parameter is passed in because
+        // the caller needed it to computer the duration, so pass it in to avoid
+        // recomputing it.
+        private void rebuild(long now, long done, int hits) {
+            if (mStatisticsSent == 0) {
+                mStatisticsSent = now;
+            }
+            final long elapsed = now - mLastBuildTime;
+            final long duration = done - now;
+            mLastBuildTime = now;
+
+            if (mMaxBuildTimeNs < duration) {
+                mMaxBuildTimeNs = duration;
+            }
+            mRebuilds++;
+            mReused += hits;
+            mBuildTimeNs += duration;
+
+            boolean log_build = false;
+            if (duration > SNAPSHOT_BIG_BUILD_TIME_NS) {
+                log_build = true;
+                mBigBuilds++;
+            }
+            if (elapsed < SNAPSHOT_QUICK_REBUILD_INTERVAL_NS) {
+                log_build = true;
+                mQuickRebuilds++;
+            }
+            if (log_build && mLoggedBuilds < SNAPSHOT_BUILD_REPORT_LIMIT) {
+                EventLogTags.writePmSnapshotRebuild(nsToMs(duration), nsToMs(elapsed));
+                mLoggedBuilds++;
+            }
+
+            final long log_interval = now - mStatisticsSent;
+            if (log_interval >= SNAPSHOT_LOG_INTERVAL_NS) {
+                EventLogTags.writePmSnapshotStats(mRebuilds, mReused,
+                                                  mBigBuilds, mQuickRebuilds,
+                                                  nsToMs(mMaxBuildTimeNs),
+                                                  nsToMs(mBuildTimeNs));
+                mStatisticsSent = now;
+                mRebuilds = 0;
+                mReused = 0;
+                mBuildTimeNs = 0;
+                mMaxBuildTimeNs = 0;
+                mBigBuilds = 0;
+                mQuickRebuilds = 0;
+                mLoggedBuilds = 0;
+            }
+        }
+    }
+
+    // Snapshot statistics.
     @GuardedBy("mLock")
-    private int mSnapshotRebuilt = 0;
+    private final SnapshotStatistics mSnapshotStatistics = new SnapshotStatistics();
 
     // The snapshot disable/enable switch.  An image with the flag set true uses snapshots
     // and an image with the flag set false does not use snapshots.
@@ -4786,23 +4891,21 @@
             // yet invalidated the snapshot.  Always give the thread the live computer.
             return mLiveComputer;
         }
-        int hits = 0;
-        if (TRACE_CACHES) {
-            hits = mSnapshotHits.incrementAndGet();
-        }
         synchronized (mSnapshotLock) {
             Computer c = mSnapshotComputer;
             if (sSnapshotCorked && (c != null)) {
                 // Snapshots are corked, which means new ones should not be built right now.
                 return c;
             }
+            // Deliberately capture the value pre-increment
+            final int hits = mSnapshotHits++;
             if (sSnapshotInvalid || (c == null)) {
                 // The snapshot is invalid if it is marked as invalid or if it is null.  If it
                 // is null, then it is currently being rebuilt by rebuildSnapshot().
                 synchronized (mLock) {
                     // Rebuild the snapshot if it is invalid.  Note that the snapshot might be
                     // invalidated as it is rebuilt.  However, the snapshot is still
-                    // self-consistent (the lock is being held)and is current as of the time
+                    // self-consistent (the lock is being held) and is current as of the time
                     // this function is entered.
                     if (sSnapshotInvalid) {
                         rebuildSnapshot(hits);
@@ -4820,22 +4923,20 @@
     }
 
     /**
-     * Rebuild the cached computer.  mSnapshotComputer is temporarily set to null to block
-     * other threads from using the invalid computer until it is rebuilt.
+     * Rebuild the cached computer.  mSnapshotComputer is temporarily set to null to block other
+     * threads from using the invalid computer until it is rebuilt.
      */
     @GuardedBy("mLock")
     private void rebuildSnapshot(int hits) {
+        final long now = System.nanoTime();
         mSnapshotComputer = null;
         sSnapshotInvalid = false;
         final Snapshot args = new Snapshot(Snapshot.SNAPPED);
         mSnapshotComputer = new ComputerEngine(args);
+        final long done = System.nanoTime();
 
-        // Still guarded by mLock
-        final int run = hits - mSnapshotRebuilt;
-        mSnapshotRebuilt = hits;
-        if (TRACE_CACHES) {
-            Log.w(TAG, "computer: rebuild after " + run + " hits");
-        }
+        mSnapshotStatistics.rebuild(now, done, hits);
+        mSnapshotHits = 0;
     }
 
     /**
@@ -4852,8 +4953,8 @@
      * @hide
      */
     public static void onChange(@Nullable Watchable what) {
-        if (TRACE_CACHES) {
-            Log.e(TAG, "computer: onChange(" + what + ")");
+        if (TRACE_SNAPSHOTS) {
+            Log.e(TAG, "snapshot: onChange(" + what + ")");
         }
         sSnapshotInvalid = true;
     }
@@ -6251,7 +6352,7 @@
 
         if (separateProcesses != null && separateProcesses.length() > 0) {
             if ("*".equals(separateProcesses)) {
-                mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
+                mDefParseFlags = ParsingPackageUtils.PARSE_IGNORE_PROCESSES;
                 mSeparateProcesses = null;
                 Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");
             } else {
@@ -6451,7 +6552,7 @@
                 scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
             }
 
-            final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR;
+            final int systemParseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
             final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
 
             PackageParser2 packageParser = injector.getScanningCachingPackageParser();
@@ -7091,7 +7192,7 @@
      */
     private boolean enableCompressedPackage(AndroidPackage stubPkg,
             @NonNull PackageSetting stubPkgSetting) {
-        final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
+        final int parseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_CHATTY
                 | PackageParser.PARSE_ENFORCE_CODE;
         synchronized (mInstallLock) {
             final AndroidPackage pkg;
@@ -11317,7 +11418,8 @@
             @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
             @Nullable UserHandle user)
                     throws PackageManagerException {
-        final boolean scanSystemPartition = (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0;
+        final boolean scanSystemPartition =
+                (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
         final String renamedPkgName;
         final PackageSetting disabledPkgSetting;
         final boolean isSystemPkgUpdated;
@@ -11360,7 +11462,7 @@
                             0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
                     : null;
             if (DEBUG_PACKAGE_SCANNING
-                    && (parseFlags & PackageParser.PARSE_CHATTY) != 0
+                    && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
                     && sharedUserSetting != null) {
                 Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
                         + " (uid=" + sharedUserSetting.userId + "):"
@@ -13179,10 +13281,11 @@
                 sharedUserSetting = mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
                         0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
                 if (DEBUG_PACKAGE_SCANNING) {
-                    if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+                    if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
                         Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
                                 + " (uid=" + sharedUserSetting.userId + "):"
                                 + " packages=" + sharedUserSetting.packages);
+                    }
                 }
             }
             String platformPackageName = mPlatformPackage == null
@@ -13339,7 +13442,7 @@
         final int userId = user == null ? 0 : user.getIdentifier();
         // Modify state for the given package setting
         commitPackageSettings(pkg, oldPkg, pkgSetting, scanFlags,
-                (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
+                (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
         if (pkgSetting.getInstantApp(userId)) {
             mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
         }
@@ -13548,8 +13651,9 @@
         List<String> changedAbiCodePath = null;
 
         if (DEBUG_PACKAGE_SCANNING) {
-            if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+            if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
                 Log.d(TAG, "Scanning package " + parsedPackage.getPackageName());
+            }
         }
 
         // Initialize package source and resource directories
@@ -13816,7 +13920,7 @@
         } else if (pkgSetting.firstInstallTime == 0) {
             // We need *something*.  Take time time stamp of the file.
             pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
-        } else if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
+        } else if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) {
             if (scanFileTime != pkgSetting.timeStamp) {
                 // A package on the system image has changed; consider this
                 // to be an update.
@@ -14013,7 +14117,7 @@
     private void assertPackageIsValid(AndroidPackage pkg, final @ParseFlags int parseFlags,
             final @ScanFlags int scanFlags)
                     throws PackageManagerException {
-        if ((parseFlags & PackageParser.PARSE_ENFORCE_CODE) != 0) {
+        if ((parseFlags & ParsingPackageUtils.PARSE_ENFORCE_CODE) != 0) {
             assertCodePolicy(pkg);
         }
 
@@ -14270,7 +14374,7 @@
                 if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
                     // We are scanning a system overlay. This can be the first scan of the
                     // system/vendor/oem partition, or an update to the system overlay.
-                    if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+                    if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
                         // This must be an update to a system overlay. Immutable overlays cannot be
                         // upgraded.
                         Objects.requireNonNull(mOverlayConfig,
@@ -14350,7 +14454,7 @@
 
             // If the package is not on a system partition ensure it is signed with at least the
             // minimum signature scheme version required for its target SDK.
-            if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+            if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
                 int minSignatureSchemeVersion =
                         ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
                                 pkg.getTargetSdkVersion());
@@ -18015,11 +18119,12 @@
             // Try enumerating all code paths before deleting
             List<String> allCodePaths = Collections.EMPTY_LIST;
             if (codeFile != null && codeFile.exists()) {
-                try {
-                    final PackageLite pkg = PackageParser.parsePackageLite(codeFile, 0);
-                    allCodePaths = pkg.getAllCodePaths();
-                } catch (PackageParserException e) {
-                    // Ignored; we tried our best
+                final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+                final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
+                        input.reset(), codeFile, /* flags */ 0);
+                if (result.isSuccess()) {
+                    // Ignore error; we tried our best
+                    allCodePaths = result.getResult().getAllApkPaths();
                 }
             }
 
@@ -18637,7 +18742,7 @@
                     // We just determined the app is signed correctly, so bring
                     // over the latest parsed certs.
                 } else {
-                    if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+                    if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
                         throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                                 "Package " + parsedPackage.getPackageName()
                                         + " upgrade keys do not match the previously installed"
@@ -18687,7 +18792,7 @@
                         }
                     }
                 } catch (PackageManagerException e) {
-                    if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+                    if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
                         throw new ReconcileFailure(e);
                     }
                     signingDetails = parsedPackage.getSigningDetails();
@@ -18766,7 +18871,8 @@
             // apps are scanned to avoid dependency based scanning.
             final ScanResult scanResult = scannedPackages.get(installPackageName);
             if ((scanResult.request.scanFlags & SCAN_BOOTING) != 0
-                    || (scanResult.request.parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
+                    || (scanResult.request.parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR)
+                    != 0) {
                 continue;
             }
             try {
@@ -19652,9 +19758,9 @@
         }
 
         // Retrieve PackageSettings and parse package
-        @ParseFlags final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
-                | PackageParser.PARSE_ENFORCE_CODE
-                | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
+        @ParseFlags final int parseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_CHATTY
+                | ParsingPackageUtils.PARSE_ENFORCE_CODE
+                | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0);
 
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
         final ParsedPackage parsedPackage;
@@ -21384,8 +21490,8 @@
         final File codePath = new File(codePathString);
         @ParseFlags int parseFlags =
                 mDefParseFlags
-                | PackageParser.PARSE_MUST_BE_APK
-                | PackageParser.PARSE_IS_SYSTEM_DIR;
+                | ParsingPackageUtils.PARSE_MUST_BE_APK
+                | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
         @ScanFlags int scanFlags = SCAN_AS_SYSTEM;
         for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
             ScanPartition partition = mDirsToScanAsSystem.get(i);
@@ -24837,7 +24943,7 @@
 
         final ArrayList<PackageFreezer> freezers = new ArrayList<>();
         final ArrayList<AndroidPackage> loaded = new ArrayList<>();
-        final int parseFlags = mDefParseFlags | PackageParser.PARSE_EXTERNAL_STORAGE;
+        final int parseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_EXTERNAL_STORAGE;
 
         final VersionInfo ver;
         final List<PackageSetting> packages;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index d3d7c60..ee94b85 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -35,9 +35,12 @@
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.Signature;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
 import android.os.Build;
 import android.os.Debug;
 import android.os.Environment;
@@ -819,8 +822,8 @@
     /**
      * Parse given package and return minimal details.
      */
-    public static PackageInfoLite getMinimalPackageInfo(Context context,
-            PackageParser.PackageLite pkg, String packagePath, int flags, String abiOverride) {
+    public static PackageInfoLite getMinimalPackageInfo(Context context, PackageLite pkg,
+            String packagePath, int flags, String abiOverride) {
         final PackageInfoLite ret = new PackageInfoLite();
         if (packagePath == null || pkg == null) {
             Slog.i(TAG, "Invalid package file " + packagePath);
@@ -843,19 +846,19 @@
         }
 
         final int recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
-                pkg.packageName, pkg.installLocation, sizeBytes, flags);
+                pkg.getPackageName(), pkg.getInstallLocation(), sizeBytes, flags);
 
-        ret.packageName = pkg.packageName;
-        ret.splitNames = pkg.splitNames;
-        ret.versionCode = pkg.versionCode;
-        ret.versionCodeMajor = pkg.versionCodeMajor;
-        ret.baseRevisionCode = pkg.baseRevisionCode;
-        ret.splitRevisionCodes = pkg.splitRevisionCodes;
-        ret.installLocation = pkg.installLocation;
-        ret.verifiers = pkg.verifiers;
+        ret.packageName = pkg.getPackageName();
+        ret.splitNames = pkg.getSplitNames();
+        ret.versionCode = pkg.getVersionCode();
+        ret.versionCodeMajor = pkg.getVersionCodeMajor();
+        ret.baseRevisionCode = pkg.getBaseRevisionCode();
+        ret.splitRevisionCodes = pkg.getSplitRevisionCodes();
+        ret.installLocation = pkg.getInstallLocation();
+        ret.verifiers = pkg.getVerifiers();
         ret.recommendedInstallLocation = recommendedInstallLocation;
-        ret.multiArch = pkg.multiArch;
-        ret.debuggable = pkg.debuggable;
+        ret.multiArch = pkg.isMultiArch();
+        ret.debuggable = pkg.isDebuggable();
 
         return ret;
     }
@@ -868,11 +871,16 @@
      */
     public static long calculateInstalledSize(String packagePath, String abiOverride) {
         final File packageFile = new File(packagePath);
-        final PackageParser.PackageLite pkg;
         try {
-            pkg = PackageParser.parsePackageLite(packageFile, 0);
-            return PackageHelper.calculateInstalledSize(pkg, abiOverride);
-        } catch (PackageParserException | IOException e) {
+            final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+            final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
+                    input.reset(), packageFile, /* flags */ 0);
+            if (result.isError()) {
+                throw new PackageManagerException(result.getErrorCode(),
+                        result.getErrorMessage(), result.getException());
+            }
+            return PackageHelper.calculateInstalledSize(result.getResult(), abiOverride);
+        } catch (PackageManagerException | IOException e) {
             Slog.w(TAG, "Failed to calculate installed size: " + e);
             return -1;
         }
@@ -931,16 +939,23 @@
 
         try {
             final File packageFile = new File(packagePath);
-            final PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packageFile, 0);
-            copyFile(pkg.baseCodePath, targetDir, "base.apk");
-            if (!ArrayUtils.isEmpty(pkg.splitNames)) {
-                for (int i = 0; i < pkg.splitNames.length; i++) {
-                    copyFile(pkg.splitCodePaths[i], targetDir,
-                            "split_" + pkg.splitNames[i] + ".apk");
+            final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+            final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
+                    input.reset(), packageFile, /* flags */ 0);
+            if (result.isError()) {
+                Slog.w(TAG, "Failed to parse package at " + packagePath);
+                return result.getErrorCode();
+            }
+            final PackageLite pkg = result.getResult();
+            copyFile(pkg.getBaseApkPath(), targetDir, "base.apk");
+            if (!ArrayUtils.isEmpty(pkg.getSplitNames())) {
+                for (int i = 0; i < pkg.getSplitNames().length; i++) {
+                    copyFile(pkg.getSplitApkPaths()[i], targetDir,
+                            "split_" + pkg.getSplitNames()[i] + ".apk");
                 }
             }
             return PackageManager.INSTALL_SUCCEEDED;
-        } catch (PackageParserException | IOException | ErrnoException e) {
+        } catch (IOException | ErrnoException e) {
             Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
             return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 446342a..3207d56a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -49,8 +49,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser.ApkLite;
-import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
@@ -61,7 +59,9 @@
 import android.content.pm.dex.ArtManager;
 import android.content.pm.dex.DexMetadataHelper;
 import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
+import android.content.pm.parsing.ApkLite;
 import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.content.res.AssetManager;
@@ -555,8 +555,8 @@
                             apkLiteResult.getException());
                 }
                 final ApkLite apkLite = apkLiteResult.getResult();
-                PackageLite pkgLite = new PackageLite(null, apkLite.codePath, apkLite, null, null,
-                        null, null, null, null);
+                final PackageLite pkgLite = new PackageLite(null, apkLite.getPath(), apkLite, null,
+                        null, null, null, null, null);
                 sessionSize += PackageHelper.calculateInstalledSize(pkgLite,
                         params.sessionParams.abiOverride, fd.getFileDescriptor());
             } catch (IOException e) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3369a4f..2929568 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -73,6 +73,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.incremental.IncrementalManager;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
 import android.service.pm.PackageServiceDumpProto;
@@ -2957,6 +2958,8 @@
         if (pkg.isPackageLoading()) {
             serializer.attributeBoolean(null, "isLoading", true);
         }
+        serializer.attributeFloat(null, "loadingProgress",
+                pkg.getIncrementalStates().getProgress());
 
         writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
 
@@ -3699,6 +3702,7 @@
         boolean installedForceQueryable = false;
         boolean isStartable = false;
         boolean isLoading = false;
+        float loadingProgress = 0;
         try {
             name = parser.getAttributeValue(null, ATTR_NAME);
             realName = parser.getAttributeValue(null, "realName");
@@ -3717,6 +3721,7 @@
             installedForceQueryable = parser.getAttributeBoolean(null, "forceQueryable", false);
             isStartable = parser.getAttributeBoolean(null, "isStartable", false);
             isLoading = parser.getAttributeBoolean(null, "isLoading", false);
+            loadingProgress = parser.getAttributeFloat(null, "loadingProgress", 0);
 
             if (primaryCpuAbiString == null && legacyCpuAbiString != null) {
                 primaryCpuAbiString = legacyCpuAbiString;
@@ -3864,7 +3869,8 @@
             packageSetting.secondaryCpuAbiString = secondaryCpuAbiString;
             packageSetting.updateAvailable = updateAvailable;
             packageSetting.forceQueryableOverride = installedForceQueryable;
-            packageSetting.incrementalStates = new IncrementalStates(isStartable, isLoading);
+            packageSetting.incrementalStates = new IncrementalStates(isStartable, isLoading,
+                    loadingProgress);
             // Handle legacy string here for single-user mode
             final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
             if (enabledStr != null) {
@@ -4814,6 +4820,10 @@
             pw.print(prefix); pw.print("  installerAttributionTag=");
             pw.println(ps.installSource.installerAttributionTag);
         }
+        if (IncrementalManager.isIncrementalPath(ps.getPathString())) {
+            pw.print(prefix); pw.println("  loadingProgress="
+                    + (int) (ps.getIncrementalStates().getProgress() * 100) + "%");
+        }
         if (ps.volumeUuid != null) {
             pw.print(prefix); pw.print("  volumeUuid=");
                     pw.println(ps.volumeUuid);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 314510b..f43240b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -47,6 +47,7 @@
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.UserInfo;
 import android.content.pm.UserInfo.UserInfoFlag;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.os.Binder;
@@ -92,6 +93,7 @@
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.util.TimeUtils;
+import android.util.TypedValue;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
@@ -139,6 +141,7 @@
 import java.util.Set;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Service for {@link UserManager}.
@@ -461,6 +464,26 @@
         }
     };
 
+    /**
+     * Cache the owner name string, since it could be read repeatedly on a critical code path
+     * but hit by slow IO. This could be eliminated once we have the cached UserInfo in place.
+     */
+    private final AtomicReference<String> mOwnerName = new AtomicReference<>();
+
+    private final TypedValue mOwnerNameTypedValue = new TypedValue();
+
+    private final Configuration mLastConfiguration = new Configuration();
+
+    private final BroadcastReceiver mConfigurationChangeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!Intent.ACTION_CONFIGURATION_CHANGED.equals(intent.getAction())) {
+                return;
+            }
+            invalidateOwnerNameIfNecessary(context.getResources(), false /* forceUpdate */);
+        }
+    };
+
     // TODO(b/161915546): remove once userWithName() is fixed / removed
     // Use to debug / dump when user 0 is allocated at userWithName()
     public static final boolean DBG_ALLOCATION = false; // DO NOT SUBMIT WITH TRUE
@@ -636,6 +659,7 @@
         mHandler = new MainHandler();
         mUserDataPreparer = userDataPreparer;
         mUserTypes = UserTypeFactory.getUserTypes();
+        invalidateOwnerNameIfNecessary(context.getResources(), true /* forceUpdate */);
         synchronized (mPackagesLock) {
             mUsersDir = new File(dataDir, USER_INFO_DIR);
             mUsersDir.mkdirs();
@@ -669,6 +693,10 @@
         mContext.registerReceiver(mDisableQuietModeCallback,
                 new IntentFilter(ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK),
                 null, mHandler);
+
+        mContext.registerReceiver(mConfigurationChangeReceiver,
+                new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED),
+                null, mHandler);
     }
 
     /**
@@ -2851,7 +2879,16 @@
     }
 
     private String getOwnerName() {
-        return mContext.getResources().getString(com.android.internal.R.string.owner_name);
+        return mOwnerName.get();
+    }
+
+    private void invalidateOwnerNameIfNecessary(@NonNull Resources res, boolean forceUpdate) {
+        final int configChanges = mLastConfiguration.updateFrom(res.getConfiguration());
+        if (forceUpdate || (configChanges & mOwnerNameTypedValue.changingConfigurations) != 0) {
+            res.getValue(com.android.internal.R.string.owner_name, mOwnerNameTypedValue, true);
+            final CharSequence ownerName = mOwnerNameTypedValue.coerceToString();
+            mOwnerName.set(ownerName != null ? ownerName.toString() : null);
+        }
     }
 
     private void scheduleWriteUser(UserData userData) {
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
index a13680a..471a4d3 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
@@ -24,6 +24,7 @@
 import android.content.pm.PackageParser;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.parsing.ParsingPackageRead;
+import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedIntentInfo;
 import android.content.pm.parsing.component.ParsedPermissionGroup;
@@ -56,7 +57,7 @@
 
     /**
      * The names of packages to adopt ownership of permissions from, parsed under
-     * {@link PackageParser#TAG_ADOPT_PERMISSIONS}.
+     * {@link ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}.
      * @see R.styleable#AndroidManifestOriginalPackage_name
      */
     @NonNull
@@ -84,7 +85,7 @@
 
     /**
      * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in
-     * {@link PackageParser#TAG_KEY_SETS}.
+     * {@link ParsingPackageUtils#TAG_KEY_SETS}.
      * @see R.styleable#AndroidManifestKeySet
      * @see R.styleable#AndroidManifestPublicKey
      */
@@ -230,7 +231,7 @@
 
     /**
      * For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in
-     * {@link PackageParser#TAG_KEY_SETS}.
+     * {@link ParsingPackageUtils#TAG_KEY_SETS}.
      * @see R.styleable#AndroidManifestUpgradeKeySet
      */
     @NonNull
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index ab25a7c..37dfea4 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -21,12 +21,12 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.VersionedPackage;
 import android.content.pm.dex.DexMetadataHelper;
 import android.content.pm.parsing.ParsingPackageRead;
+import android.content.pm.parsing.ParsingPackageUtils;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedInstrumentation;
 import android.content.pm.parsing.component.ParsedProvider;
@@ -233,7 +233,7 @@
     }
 
     public static int getIcon(ParsingPackageRead pkg) {
-        return (PackageParser.sUseRoundIcon && pkg.getRoundIconRes() != 0)
+        return (ParsingPackageUtils.sUseRoundIcon && pkg.getRoundIconRes() != 0)
                 ? pkg.getRoundIconRes() : pkg.getIconRes();
     }
 
diff --git a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java b/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java
index 6477552..2fc3e40 100644
--- a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java
+++ b/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java
@@ -25,7 +25,6 @@
 import android.view.Display;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.server.display.DisplayGroup;
 
 /**
  * Responsible for creating {@link DisplayPowerRequest}s and associating them with
@@ -110,8 +109,8 @@
             DisplayManagerInternal displayManagerInternal, Handler handler) {
         mDisplayManagerInternal = displayManagerInternal;
         displayManager.registerDisplayListener(mDisplayListener, handler);
-        mDisplayPowerRequests.append(DisplayGroup.DEFAULT, new DisplayPowerRequest());
-        mDisplayGroupIds.append(Display.DEFAULT_DISPLAY, DisplayGroup.DEFAULT);
+        mDisplayPowerRequests.append(Display.DEFAULT_DISPLAY_GROUP, new DisplayPowerRequest());
+        mDisplayGroupIds.append(Display.DEFAULT_DISPLAY, Display.DEFAULT_DISPLAY_GROUP);
     }
 
     DisplayPowerRequest get(int displayId) {
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index bd66aa3..a4459d0 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -33,9 +33,9 @@
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.VersionedPackage;
+import android.content.pm.parsing.ApkLite;
 import android.content.pm.parsing.ApkLiteParseUtils;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
@@ -831,24 +831,24 @@
         }
 
         // Get information about the package to be installed.
-        ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
-        ParseResult<PackageParser.ApkLite> parseResult = ApkLiteParseUtils.parseApkLite(
+        final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+        final ParseResult<ApkLite> parseResult = ApkLiteParseUtils.parseApkLite(
                 input.reset(), new File(session.resolvedBaseCodePath), 0);
         if (parseResult.isError()) {
             Slog.e(TAG, "Unable to parse new package: " + parseResult.getErrorMessage(),
                     parseResult.getException());
             return false;
         }
-        PackageParser.ApkLite newPackage = parseResult.getResult();
+        final ApkLite newPackage = parseResult.getResult();
 
-        String packageName = newPackage.packageName;
-        int rollbackDataPolicy = computeRollbackDataPolicy(
-                session.rollbackDataPolicy, newPackage.rollbackDataPolicy);
+        final String packageName = newPackage.getPackageName();
+        final int rollbackDataPolicy = computeRollbackDataPolicy(
+                session.rollbackDataPolicy, newPackage.getRollbackDataPolicy());
         Slog.i(TAG, "Enabling rollback for install of " + packageName
                 + ", session:" + session.sessionId
                 + ", rollbackDataPolicy=" + rollbackDataPolicy);
 
-        String installerPackageName = session.getInstallerPackageName();
+        final String installerPackageName = session.getInstallerPackageName();
         if (!enableRollbackAllowed(installerPackageName, packageName)) {
             Slog.e(TAG, "Installer " + installerPackageName
                     + " is not allowed to enable rollback on " + packageName);
@@ -900,7 +900,7 @@
          * a rollback object is inconsistent because it doesn't count apk-in-apex.
          */
         ApplicationInfo appInfo = pkgInfo.applicationInfo;
-        return rollback.enableForPackage(packageName, newPackage.versionCode,
+        return rollback.enableForPackage(packageName, newPackage.getVersionCode(),
                 pkgInfo.getLongVersionCode(), isApex, appInfo.sourceDir,
                 appInfo.splitSourceDirs, rollbackDataPolicy);
     }
diff --git a/services/core/java/com/android/server/rotationresolver/OWNERS b/services/core/java/com/android/server/rotationresolver/OWNERS
new file mode 100644
index 0000000..81b6f05
--- /dev/null
+++ b/services/core/java/com/android/server/rotationresolver/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/rotationresolver/OWNERS
diff --git a/services/core/java/com/android/server/speech/Android.bp b/services/core/java/com/android/server/speech/Android.bp
new file mode 100644
index 0000000..379b075
--- /dev/null
+++ b/services/core/java/com/android/server/speech/Android.bp
@@ -0,0 +1,13 @@
+filegroup {
+    name: "services.speech-sources",
+    srcs: ["java/**/*.java"],
+    path: "java",
+    visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+    name: "services.speech",
+    defaults: ["platform_service_defaults"],
+    srcs: [":services.speech-sources"],
+    libs: ["services.core"],
+}
diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
new file mode 100644
index 0000000..96248c3
--- /dev/null
+++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.speech;
+
+import static com.android.internal.infra.AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.speech.IRecognitionListener;
+import android.speech.IRecognitionService;
+import android.speech.RecognitionService;
+import android.util.Slog;
+
+import com.android.internal.infra.ServiceConnector;
+
+final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecognitionService> {
+    private static final String TAG = RemoteSpeechRecognitionService.class.getSimpleName();
+    private static final boolean DEBUG = true;
+
+    RemoteSpeechRecognitionService(Context context, ComponentName serviceName, int userId) {
+        super(context,
+                new Intent(RecognitionService.SERVICE_INTERFACE).setComponent(serviceName),
+                Context.BIND_AUTO_CREATE
+                        | Context.BIND_FOREGROUND_SERVICE
+                        | Context.BIND_INCLUDE_CAPABILITIES
+                        | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
+                userId,
+                IRecognitionService.Stub::asInterface);
+
+        if (DEBUG) {
+            Slog.i(TAG, "Bound to recognition service at: " + serviceName.flattenToString());
+        }
+    }
+
+    void startListening(Intent recognizerIntent, IRecognitionListener listener, String packageName,
+            String featureId) throws RemoteException {
+        if (DEBUG) {
+            Slog.i(TAG, "#startListening for package: " + packageName + ", feature=" + featureId);
+        }
+        run(service -> service.startListening(recognizerIntent, listener, packageName, featureId));
+    }
+
+    void stopListening(IRecognitionListener listener, String packageName, String featureId)
+            throws RemoteException {
+        if (DEBUG) {
+            Slog.i(TAG, "#stopListening for package: " + packageName + ", feature=" + featureId);
+        }
+        run(service -> service.stopListening(listener, packageName, featureId));
+    }
+
+    void cancel(IRecognitionListener listener, String packageName, String featureId)
+            throws RemoteException {
+        if (DEBUG) {
+            Slog.i(TAG, "#cancel for package: " + packageName + ", feature=" + featureId);
+        }
+        run(service -> service.cancel(listener, packageName, featureId));
+    }
+
+    @Override // from ServiceConnector.Impl
+    protected void onServiceConnectionStatusChanged(
+            IRecognitionService service, boolean connected) {
+        if (!DEBUG) {
+            return;
+        }
+
+        if (connected) {
+            Slog.i(TAG, "Connected to ASR service");
+        } else {
+            Slog.w(TAG, "Disconnected from ASR service");
+        }
+    }
+
+    @Override // from AbstractRemoteService
+    protected long getAutoDisconnectTimeoutMs() {
+        return PERMANENT_BOUND_TIMEOUT_MS;
+    }
+}
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
new file mode 100644
index 0000000..592ba9e
--- /dev/null
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.speech;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.UserHandle;
+import android.speech.IRecognitionServiceManager;
+import android.speech.IRecognitionServiceManagerCallback;
+
+import com.android.internal.R;
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+
+/**
+ * System service implementation for Speech Recognizer.
+ *
+ * <p>This service uses RemoteSpeechRecognitionService to bind to a default implementation of
+ * ISpeechRecognition. It relays all the requests from the client to the default system impl of
+ * ISpeechRecognition service (denoted by {@code
+ * R.string.config_defaultOnDeviceSpeechRecognitionService}).
+ */
+public final class SpeechRecognitionManagerService extends
+        AbstractMasterSystemService<SpeechRecognitionManagerService,
+                SpeechRecognitionManagerServiceImpl> {
+    private static final String TAG = SpeechRecognitionManagerService.class.getSimpleName();
+
+    public SpeechRecognitionManagerService(@NonNull Context context) {
+        super(context,
+                new FrameworkResourcesServiceNameResolver(
+                        context,
+                        R.string.config_defaultOnDeviceSpeechRecognitionService),
+                /*disallowProperty=*/ null);
+    }
+
+    @Override // from SystemService
+    public void onStart() {
+        SpeechRecognitionManagerServiceStub serviceStub = new SpeechRecognitionManagerServiceStub();
+        publishBinderService(Context.SPEECH_RECOGNITION_SERVICE, serviceStub);
+    }
+
+    @Override
+    protected SpeechRecognitionManagerServiceImpl newServiceLocked(
+            @UserIdInt int resolvedUserId, boolean disabled) {
+        return new SpeechRecognitionManagerServiceImpl(this, mLock, resolvedUserId, disabled);
+    }
+
+    final class SpeechRecognitionManagerServiceStub extends IRecognitionServiceManager.Stub {
+
+        @Override
+        public void createSession(IRecognitionServiceManagerCallback callback) {
+            int userId = UserHandle.getCallingUserId();
+            synchronized (mLock) {
+                SpeechRecognitionManagerServiceImpl service = getServiceForUserLocked(userId);
+                service.createSessionLocked(callback);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
new file mode 100644
index 0000000..bcaf174
--- /dev/null
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.speech;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.speech.IRecognitionListener;
+import android.speech.IRecognitionService;
+import android.speech.IRecognitionServiceManagerCallback;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+final class SpeechRecognitionManagerServiceImpl extends
+        AbstractPerUserSystemService<SpeechRecognitionManagerServiceImpl,
+            SpeechRecognitionManagerService> {
+
+    private static final String TAG = SpeechRecognitionManagerServiceImpl.class.getSimpleName();
+
+    @GuardedBy("mLock")
+    @Nullable
+    private RemoteSpeechRecognitionService mRemoteService;
+
+    SpeechRecognitionManagerServiceImpl(
+            @NonNull SpeechRecognitionManagerService master,
+            @NonNull Object lock, @UserIdInt int userId, boolean disabled) {
+        super(master, lock, userId);
+        updateRemoteServiceLocked();
+    }
+
+    @GuardedBy("mLock")
+    @Override // from PerUserSystemService
+    protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+            throws PackageManager.NameNotFoundException {
+        try {
+            return AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+                    PackageManager.GET_META_DATA, mUserId);
+        } catch (RemoteException e) {
+            throw new PackageManager.NameNotFoundException(
+                    "Could not get service for " + serviceComponent);
+        }
+    }
+
+    @GuardedBy("mLock")
+    @Override // from PerUserSystemService
+    protected boolean updateLocked(boolean disabled) {
+        final boolean enabledChanged = super.updateLocked(disabled);
+        updateRemoteServiceLocked();
+        return enabledChanged;
+    }
+
+    /**
+     * Updates the reference to the remote service.
+     */
+    @GuardedBy("mLock")
+    private void updateRemoteServiceLocked() {
+        if (mRemoteService != null) {
+            if (mMaster.debug) {
+                Slog.d(TAG, "updateRemoteService(): destroying old remote service");
+            }
+            mRemoteService.unbind();
+            mRemoteService = null;
+        }
+    }
+
+    void createSessionLocked(IRecognitionServiceManagerCallback callback) {
+        // TODO(b/176578753): check clients have record audio permission.
+        // TODO(b/176578753): verify caller package is the one supplied
+
+        RemoteSpeechRecognitionService service = ensureRemoteServiceLocked();
+
+        if (service == null) {
+            tryRespondWithError(callback);
+            return;
+        }
+
+        service.connect().thenAccept(binderService -> {
+            if (binderService != null) {
+                try {
+                    callback.onSuccess(new IRecognitionService.Stub() {
+                        @Override
+                        public void startListening(Intent recognizerIntent,
+                                IRecognitionListener listener,
+                                String packageName, String featureId) throws RemoteException {
+                            service.startListening(
+                                    recognizerIntent, listener, packageName, featureId);
+                        }
+
+                        @Override
+                        public void stopListening(IRecognitionListener listener,
+                                String packageName,
+                                String featureId) throws RemoteException {
+                            service.stopListening(listener, packageName, featureId);
+                        }
+
+                        @Override
+                        public void cancel(IRecognitionListener listener,
+                                String packageName,
+                                String featureId) throws RemoteException {
+                            service.cancel(listener, packageName, featureId);
+                        }
+                    });
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Error creating a speech recognition session", e);
+                    tryRespondWithError(callback);
+                }
+            } else {
+                tryRespondWithError(callback);
+            }
+        });
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    private RemoteSpeechRecognitionService ensureRemoteServiceLocked() {
+        if (mRemoteService == null) {
+            final String serviceName = getComponentNameLocked();
+            if (serviceName == null) {
+                if (mMaster.verbose) {
+                    Slog.v(TAG, "ensureRemoteServiceLocked(): no service component name.");
+                }
+                return null;
+            }
+            final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+            mRemoteService =
+                    new RemoteSpeechRecognitionService(getContext(), serviceComponent, mUserId);
+        }
+        return mRemoteService;
+    }
+
+    private static void tryRespondWithError(IRecognitionServiceManagerCallback callback) {
+        try {
+            callback.onError();
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to respond with error");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
similarity index 89%
rename from services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
rename to services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
index 210fb5c..c0c9e6d 100644
--- a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
-import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/location/timezone/ControllerCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerCallbackImpl.java
similarity index 96%
rename from services/core/java/com/android/server/location/timezone/ControllerCallbackImpl.java
rename to services/core/java/com/android/server/timezonedetector/location/ControllerCallbackImpl.java
index cd9aa2f..46eaad0 100644
--- a/services/core/java/com/android/server/location/timezone/ControllerCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerCallbackImpl.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import android.annotation.NonNull;
 
diff --git a/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
similarity index 97%
rename from services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
rename to services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
index d896f6e..83b33ee 100644
--- a/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import android.annotation.NonNull;
 
diff --git a/services/core/java/com/android/server/location/timezone/ControllerImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
similarity index 94%
rename from services/core/java/com/android/server/location/timezone/ControllerImpl.java
rename to services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
index 0d284fc..fb2a184 100644
--- a/services/core/java/com/android/server/location/timezone/ControllerImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
@@ -14,20 +14,20 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
-import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
-import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
-import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE;
-import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION;
-import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
+import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE;
+import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION;
+import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
 
 import android.annotation.DurationMillisLong;
 import android.annotation.NonNull;
@@ -36,9 +36,9 @@
 import android.util.IndentingPrintWriter;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue;
 import com.android.server.timezonedetector.ConfigurationInternal;
 import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.location.ThreadingDomain.SingleRunnableQueue;
 
 import java.time.Duration;
 import java.util.List;
diff --git a/services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java b/services/core/java/com/android/server/timezonedetector/location/HandlerThreadingDomain.java
similarity index 98%
rename from services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java
rename to services/core/java/com/android/server/timezonedetector/location/HandlerThreadingDomain.java
index 3055ff8..0dd2922 100644
--- a/services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java
+++ b/services/core/java/com/android/server/timezonedetector/location/HandlerThreadingDomain.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import android.annotation.DurationMillisLong;
 import android.annotation.NonNull;
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
similarity index 99%
rename from services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
rename to services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index 54535eb..5bee7ee 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME;
 import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DISABLED;
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerServiceState.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java
similarity index 95%
rename from services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerServiceState.java
rename to services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java
index b1dd55f..113926a 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerServiceState.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
-import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
 import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState;
 
 import java.util.ArrayList;
 import java.util.Collections;
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
similarity index 92%
rename from services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java
rename to services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
index 6f9863c..b53150c 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import static android.app.time.LocationTimeZoneManager.DUMP_STATE_OPTION_PROTO;
 import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME;
@@ -28,13 +28,13 @@
 import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_START;
 import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_STOP;
 
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_UNKNOWN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_UNKNOWN;
 
 import android.annotation.NonNull;
 import android.app.time.GeolocationTimeZoneSuggestionProto;
@@ -47,8 +47,8 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.dump.DualDumpOutputStream;
-import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
 import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
similarity index 94%
rename from services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
rename to services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
index 9a7b775..ef2f357 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
@@ -14,22 +14,22 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
 import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
 
-import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
-import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
-import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE;
-import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION;
-import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
+import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE;
+import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION;
+import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
 
 import android.annotation.ElapsedRealtimeLong;
 import android.annotation.IntDef;
@@ -42,11 +42,11 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
-import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue;
 import com.android.server.timezonedetector.ConfigurationInternal;
 import com.android.server.timezonedetector.Dumpable;
 import com.android.server.timezonedetector.ReferenceWithHistory;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
+import com.android.server.timezonedetector.location.ThreadingDomain.SingleRunnableQueue;
 
 import java.time.Duration;
 import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java
similarity index 97%
rename from services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
rename to services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java
index ec2bc13..b4aff3e 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import android.annotation.DurationMillisLong;
 import android.annotation.NonNull;
 import android.os.Handler;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
 import com.android.server.timezonedetector.ConfigurationInternal;
 import com.android.server.timezonedetector.Dumpable;
 import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState;
 
 import java.time.Duration;
 import java.util.Objects;
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderProxy.java
similarity index 98%
rename from services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java
rename to services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderProxy.java
index 8368b5e..43b1b5f0 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderProxy.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/NullLocationTimeZoneProviderProxy.java
similarity index 98%
rename from services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProviderProxy.java
rename to services/core/java/com/android/server/timezonedetector/location/NullLocationTimeZoneProviderProxy.java
index c2abbf9..1f45e82 100644
--- a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/NullLocationTimeZoneProviderProxy.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
similarity index 98%
rename from services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
rename to services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
index 0904ba4..38211ef 100644
--- a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
 import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
 
-import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog;
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog;
 
 import android.Manifest;
 import android.annotation.NonNull;
diff --git a/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/SimulatedLocationTimeZoneProviderProxy.java
similarity index 99%
rename from services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java
rename to services/core/java/com/android/server/timezonedetector/location/SimulatedLocationTimeZoneProviderProxy.java
index 66ccaed..02b0a84 100644
--- a/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/SimulatedLocationTimeZoneProviderProxy.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND;
 import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_ON_UNBIND;
diff --git a/services/core/java/com/android/server/location/timezone/TestCommand.java b/services/core/java/com/android/server/timezonedetector/location/TestCommand.java
similarity index 98%
rename from services/core/java/com/android/server/location/timezone/TestCommand.java
rename to services/core/java/com/android/server/timezonedetector/location/TestCommand.java
index 0df3ca0..21482ea 100644
--- a/services/core/java/com/android/server/location/timezone/TestCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/location/TestCommand.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import android.annotation.NonNull;
 import android.net.Uri;
diff --git a/services/core/java/com/android/server/location/timezone/ThreadingDomain.java b/services/core/java/com/android/server/timezonedetector/location/ThreadingDomain.java
similarity index 98%
rename from services/core/java/com/android/server/location/timezone/ThreadingDomain.java
rename to services/core/java/com/android/server/timezonedetector/location/ThreadingDomain.java
index 4ada6f5..9e3497f 100644
--- a/services/core/java/com/android/server/location/timezone/ThreadingDomain.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ThreadingDomain.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import android.annotation.DurationMillisLong;
 import android.annotation.NonNull;
diff --git a/services/core/java/com/android/server/location/timezone/TimeZoneProviderEvent.java b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEvent.java
similarity index 98%
rename from services/core/java/com/android/server/location/timezone/TimeZoneProviderEvent.java
rename to services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEvent.java
index 2d6f8ad..3e224e0 100644
--- a/services/core/java/com/android/server/location/timezone/TimeZoneProviderEvent.java
+++ b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEvent.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
diff --git a/services/core/java/com/android/server/location/timezone/TimeZoneProviderRequest.java b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java
similarity index 98%
rename from services/core/java/com/android/server/location/timezone/TimeZoneProviderRequest.java
rename to services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java
index 649a74b..1482031 100644
--- a/services/core/java/com/android/server/location/timezone/TimeZoneProviderRequest.java
+++ b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index c36375e..5355252 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -74,6 +74,7 @@
         void onVibrationEnded(long vibrationId, Vibration.Status status);
     }
 
+    private final Object mLock = new Object();
     private final WorkSource mWorkSource = new WorkSource();
     private final PowerManager.WakeLock mWakeLock;
     private final IBatteryStats mBatteryStatsService;
@@ -81,10 +82,10 @@
     private final VibrationCallbacks mCallbacks;
     private final SparseArray<VibratorController> mVibrators;
 
-    @GuardedBy("this")
+    @GuardedBy("mLock")
     @Nullable
     private VibrateStep mCurrentVibrateStep;
-    @GuardedBy("this")
+    @GuardedBy("mLock")
     private boolean mForceStop;
 
     // TODO(b/159207608): Remove this constructor once VibratorService is removed
@@ -113,6 +114,10 @@
         }
     }
 
+    public Vibration getVibration() {
+        return mVibration;
+    }
+
     @Override
     public void binderDied() {
         cancel();
@@ -136,15 +141,15 @@
 
     /** Cancel current vibration and shuts down the thread gracefully. */
     public void cancel() {
-        synchronized (this) {
+        synchronized (mLock) {
             mForceStop = true;
-            notify();
+            mLock.notify();
         }
     }
 
     /** Notify current vibration that a step has completed on given vibrator. */
     public void vibratorComplete(int vibratorId) {
-        synchronized (this) {
+        synchronized (mLock) {
             if (mCurrentVibrateStep != null) {
                 mCurrentVibrateStep.vibratorComplete(vibratorId);
             }
@@ -168,7 +173,7 @@
             final int stepCount = steps.size();
             for (int i = 0; i < stepCount; i++) {
                 Step step = steps.get(i);
-                synchronized (this) {
+                synchronized (mLock) {
                     if (step instanceof VibrateStep) {
                         mCurrentVibrateStep = (VibrateStep) step;
                     } else {
@@ -295,21 +300,48 @@
      * Sleeps until given {@code wakeUpTime}.
      *
      * <p>This stops immediately when {@link #cancel()} is called.
+     *
+     * @return true if waited until wake-up time, false if it was cancelled.
      */
-    private void waitUntil(long wakeUpTime) {
-        synchronized (this) {
+    private boolean waitUntil(long wakeUpTime) {
+        synchronized (mLock) {
             long durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
             while (durationRemaining > 0) {
                 try {
-                    VibrationThread.this.wait(durationRemaining);
+                    mLock.wait(durationRemaining);
                 } catch (InterruptedException e) {
                 }
                 if (mForceStop) {
-                    break;
+                    return false;
                 }
                 durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
             }
         }
+        return true;
+    }
+
+    /**
+     * Sleeps until given {@link VibrateStep#isVibrationComplete()}, or until {@code wakeUpTime}.
+     *
+     * <p>This stops immediately when {@link #cancel()} is called.
+     *
+     * @return true if finished on vibration complete, false if it was cancelled or timed out.
+     */
+    private boolean waitForVibrationComplete(VibrateStep step, long wakeUpTime) {
+        synchronized (mLock) {
+            long durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
+            while (!step.isVibrationComplete() && durationRemaining > 0) {
+                try {
+                    mLock.wait(durationRemaining);
+                } catch (InterruptedException e) {
+                }
+                if (mForceStop) {
+                    return false;
+                }
+                durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
+            }
+        }
+        return step.isVibrationComplete();
     }
 
     private void noteVibratorOn(long duration) {
@@ -341,6 +373,9 @@
     private interface VibrateStep extends Step {
         /** Callback to notify a vibrator has finished playing a effect. */
         void vibratorComplete(int vibratorId);
+
+        /** Returns true if the vibration played by this step is complete. */
+        boolean isVibrationComplete();
     }
 
     /** Represent a vibration on a single vibrator. */
@@ -348,11 +383,20 @@
         private final VibratorController mVibrator;
         private final VibrationEffect mEffect;
 
+        @GuardedBy("mLock")
+        private boolean mVibrationComplete;
+
         SingleVibrateStep(VibratorController vibrator, VibrationEffect effect) {
             mVibrator = vibrator;
             mEffect = effect;
         }
 
+        @GuardedBy("mLock")
+        @Override
+        public boolean isVibrationComplete() {
+            return mVibrationComplete;
+        }
+
         @Override
         public void vibratorComplete(int vibratorId) {
             if (mVibrator.getVibratorInfo().getId() != vibratorId) {
@@ -364,8 +408,9 @@
                 return;
             }
             mVibrator.off();
-            synchronized (VibrationThread.this) {
-                VibrationThread.this.notify();
+            synchronized (mLock) {
+                mVibrationComplete = true;
+                mLock.notify();
             }
         }
 
@@ -384,12 +429,13 @@
                     noteVibratorOn(duration);
                     // Vibration is playing with no need to control amplitudes, just wait for native
                     // callback or timeout.
-                    waitUntil(startTime + duration + CALLBACKS_EXTRA_TIMEOUT);
-                    if (mForceStop) {
-                        mVibrator.off();
-                        return Vibration.Status.CANCELLED;
+                    if (waitForVibrationComplete(this,
+                            startTime + duration + CALLBACKS_EXTRA_TIMEOUT)) {
+                        return Vibration.Status.FINISHED;
                     }
-                    return Vibration.Status.FINISHED;
+                    // Timed out or vibration cancelled. Stop vibrator anyway.
+                    mVibrator.off();
+                    return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
                 }
 
                 startTime = SystemClock.uptimeMillis();
@@ -407,8 +453,7 @@
                     noteVibratorOn(duration);
                 }
                 while (amplitudeStep != null) {
-                    waitUntil(amplitudeStep.startTime);
-                    if (mForceStop) {
+                    if (!waitUntil(amplitudeStep.startTime)) {
                         mVibrator.off();
                         return Vibration.Status.CANCELLED;
                     }
@@ -482,7 +527,7 @@
         private final int mRequiredCapabilities;
         private final int[] mVibratorIds;
 
-        @GuardedBy("VibrationThread.this")
+        @GuardedBy("mLock")
         private int mActiveVibratorCounter;
 
         SyncedVibrateStep(SparseArray<VibrationEffect> effects) {
@@ -496,6 +541,12 @@
             }
         }
 
+        @GuardedBy("mLock")
+        @Override
+        public boolean isVibrationComplete() {
+            return mActiveVibratorCounter <= 0;
+        }
+
         @Override
         public void vibratorComplete(int vibratorId) {
             VibrationEffect effect = mEffects.get(vibratorId);
@@ -508,10 +559,9 @@
                 return;
             }
             mVibrators.get(vibratorId).off();
-            synchronized (VibrationThread.this) {
-                if (--mActiveVibratorCounter <= 0) {
-                    VibrationThread.this.notify();
-                }
+            synchronized (mLock) {
+                --mActiveVibratorCounter;
+                mLock.notify();
             }
         }
 
@@ -532,8 +582,7 @@
 
                 while (!nextSteps.isEmpty()) {
                     AmplitudeStep step = nextSteps.poll();
-                    waitUntil(step.startTime);
-                    if (mForceStop) {
+                    if (!waitUntil(step.startTime)) {
                         stopAllVibrators();
                         return Vibration.Status.CANCELLED;
                     }
@@ -541,7 +590,7 @@
                     AmplitudeStep nextStep = step.nextStep();
                     if (nextStep == null) {
                         // This vibrator has finished playing the effect for this step.
-                        synchronized (VibrationThread.this) {
+                        synchronized (mLock) {
                             mActiveVibratorCounter--;
                         }
                     } else {
@@ -549,19 +598,18 @@
                     }
                 }
 
-                // All OneShot and Waveform effects have finished. Just wait for the other effects
-                // to end via native callbacks before finishing this synced step.
-                synchronized (VibrationThread.this) {
-                    if (mActiveVibratorCounter > 0) {
-                        waitUntil(startTime + timeout + CALLBACKS_EXTRA_TIMEOUT);
+                synchronized (mLock) {
+                    // All OneShot and Waveform effects have finished. Just wait for the other
+                    // effects to end via native callbacks before finishing this synced step.
+                    final long wakeUpTime = startTime + timeout + CALLBACKS_EXTRA_TIMEOUT;
+                    if (mActiveVibratorCounter <= 0 || waitForVibrationComplete(this, wakeUpTime)) {
+                        return Vibration.Status.FINISHED;
                     }
-                }
-                if (mForceStop) {
-                    stopAllVibrators();
-                    return Vibration.Status.CANCELLED;
-                }
 
-                return Vibration.Status.FINISHED;
+                    // Timed out or vibration cancelled. Stop all vibrators anyway.
+                    stopAllVibrators();
+                    return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
+                }
             } finally {
                 if (timeout > 0) {
                     noteVibratorOff();
@@ -774,8 +822,10 @@
                 if (DEBUG) {
                     Slog.d(TAG, "DelayStep of " + mDelay + "ms starting...");
                 }
-                waitUntil(SystemClock.uptimeMillis() + mDelay);
-                return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
+                if (waitUntil(SystemClock.uptimeMillis() + mDelay)) {
+                    return Vibration.Status.FINISHED;
+                }
+                return Vibration.Status.CANCELLED;
             } finally {
                 if (DEBUG) {
                     Slog.d(TAG, "DelayStep done.");
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index c25f1b4..5fe853a 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -63,6 +63,7 @@
 import android.view.RemoteAnimationDefinition;
 
 import com.android.internal.app.AssistUtils;
+import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.LocalServices;
 import com.android.server.Watchdog;
@@ -1019,6 +1020,50 @@
     }
 
     @Override
+    public void restartActivityProcessIfVisible(IBinder token) {
+        ActivityTaskManagerService.enforceTaskPermission("restartActivityProcess");
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
+                if (r != null) {
+                    r.restartProcessIfVisible();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    @Override
+    public void invalidateHomeTaskSnapshot(IBinder token) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
+            if (r != null && r.isActivityTypeHome()) {
+                mService.mWindowManager.mTaskSnapshotController.removeSnapshotCache(
+                        r.getTask().mTaskId);
+            }
+        }
+    }
+
+    @Override
+    public void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback,
+            CharSequence message) {
+        if (message != null) {
+            mService.mAmInternal.enforceCallingPermission(
+                    android.Manifest.permission.SHOW_KEYGUARD_MESSAGE, "dismissKeyguard");
+        }
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mService.mKeyguardController.dismissKeyguard(token, callback, message);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    @Override
     public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) {
         mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
                 "registerRemoteAnimations");
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5610573..f0db3f9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -231,7 +231,6 @@
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.TransferPipe;
-import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.KeyguardDismissCallback;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ArrayUtils;
@@ -1796,23 +1795,6 @@
     }
 
     @Override
-    public void restartActivityProcessIfVisible(IBinder activityToken) {
-        enforceTaskPermission("restartActivityProcess()");
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityRecord r = ActivityRecord.isInRootTaskLocked(activityToken);
-                if (r == null) {
-                    return;
-                }
-                r.restartProcessIfVisible();
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
-        }
-    }
-
-    @Override
     public boolean removeTask(int taskId) {
         enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()");
         synchronized (mGlobalLock) {
@@ -3263,7 +3245,7 @@
             // If the keyguard is showing or occluded, then try and dismiss it before
             // entering picture-in-picture (this will prompt the user to authenticate if the
             // device is currently locked).
-            dismissKeyguard(r.appToken, new KeyguardDismissCallback() {
+            mActivityClientController.dismissKeyguard(r.appToken, new KeyguardDismissCallback() {
                 @Override
                 public void onDismissSucceeded() {
                     mH.post(enterPipRunnable);
@@ -3388,23 +3370,6 @@
     }
 
     @Override
-    public void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback,
-            CharSequence message) {
-        if (message != null) {
-            mAmInternal.enforceCallingPermission(
-                    Manifest.permission.SHOW_KEYGUARD_MESSAGE, "dismissKeyguard()");
-        }
-        final long callingId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                mKeyguardController.dismissKeyguard(token, callback, message);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(callingId);
-        }
-    }
-
-    @Override
     public void cancelTaskWindowTransition(int taskId) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS,
                 "cancelTaskWindowTransition()");
@@ -3450,17 +3415,6 @@
         return task.getSnapshot(isLowResolution, restoreFromDisk);
     }
 
-    @Override
-    public void invalidateHomeTaskSnapshot(IBinder token) {
-        synchronized (mGlobalLock) {
-            final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
-            if (r == null || !r.isActivityTypeHome()) {
-                return;
-            }
-            mWindowManager.mTaskSnapshotController.removeSnapshotCache(r.getTask().mTaskId);
-        }
-    }
-
     /** Return the user id of the last resumed activity. */
     @Override
     public @UserIdInt
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8841a9b..2a40500 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -669,9 +669,8 @@
     // Used in updating override configurations
     private final Configuration mTempConfig = new Configuration();
 
-    // Used in performing layout, to record the insets provided by other windows above the current
-    // window.
-    private InsetsState mTmpAboveInsetsState = new InsetsState();
+    // Used in performing layout
+    private boolean mTmpWindowsBehindIme;
 
     /**
      * Used to prevent recursions when calling
@@ -770,11 +769,17 @@
                     + " parentHidden=" + w.isParentWindowHidden());
         }
 
-        // Sets mAboveInsets for each window. Windows behind the window providing the insets can
-        // receive the insets.
-        if (!w.mAboveInsetsState.equals(mTmpAboveInsetsState)) {
-            w.mAboveInsetsState.set(mTmpAboveInsetsState);
-            mWinInsetsChanged.add(w);
+        // Sets mBehindIme for each window. Windows behind IME can get IME insets.
+        if (w.mBehindIme != mTmpWindowsBehindIme) {
+            w.mBehindIme = mTmpWindowsBehindIme;
+            if (getInsetsStateController().getRawInsetsState().getSourceOrDefaultVisibility(
+                    ITYPE_IME)) {
+                // If IME is invisible, behind IME or not doesn't make the insets different.
+                mWinInsetsChanged.add(w);
+            }
+        }
+        if (w == mInputMethodWindow) {
+            mTmpWindowsBehindIme = true;
         }
 
         // If this view is GONE, then skip it -- keep the current frame, and let the caller know
@@ -810,16 +815,8 @@
                     + " mContainingFrame=" + w.getContainingFrame()
                     + " mDisplayFrame=" + w.getDisplayFrame());
         }
-        provideInsetsByWindow(w);
     };
 
-    private void provideInsetsByWindow(WindowState w) {
-        for (int i = 0; i < w.mProvidedInsetsSources.size(); i++) {
-            final InsetsSource providedSource = w.mProvidedInsetsSources.valueAt(i);
-            mTmpAboveInsetsState.addSource(providedSource);
-        }
-    }
-
     private final Consumer<WindowState> mPerformLayoutAttached = w -> {
         if (w.mLayoutAttached) {
             if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + w + " mHaveFrame=" + w.mHaveFrame
@@ -4272,20 +4269,14 @@
                     + " dh=" + mDisplayInfo.logicalHeight);
         }
 
-        // Used to indicate that we have processed the insets windows. This needs to be after
-        // beginLayoutLw to ensure the raw insets state display related info is initialized.
-        final InsetsState rawInsetsState = getInsetsStateController().getRawInsetsState();
-        mTmpAboveInsetsState = new InsetsState();
-        mTmpAboveInsetsState.setDisplayFrame(rawInsetsState.getDisplayFrame());
-        mTmpAboveInsetsState.setDisplayCutout(rawInsetsState.getDisplayCutout());
-        mTmpAboveInsetsState.mirrorAlwaysVisibleInsetsSources(rawInsetsState);
-
         int seq = mLayoutSeq + 1;
         if (seq < 0) seq = 0;
         mLayoutSeq = seq;
 
         mTmpInitial = initial;
 
+        // Used to indicate that we have processed the IME window.
+        mTmpWindowsBehindIme = false;
 
         // First perform layout of any root windows (not attached to another window).
         forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 7d0854d..1692df6 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.os.Build.IS_DEBUGGABLE;
 import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
@@ -152,7 +151,6 @@
             // animate-out as new one animates-in.
             mWin.cancelAnimation();
             mWin.mPendingPositionChanged = null;
-            mWin.mProvidedInsetsSources.remove(mSource.getType());
         }
         ProtoLog.d(WM_DEBUG_IME, "InsetsSource setWin %s", win);
         mWin = win;
@@ -162,14 +160,11 @@
             setServerVisible(false);
             mSource.setFrame(new Rect());
             mSource.setVisibleFrame(null);
-        } else {
-            mWin.mProvidedInsetsSources.put(mSource.getType(), mSource);
-            if (mControllable) {
-                mWin.setControllableInsetProvider(this);
-                if (mPendingControlTarget != null) {
-                    updateControlForTarget(mPendingControlTarget, true /* force */);
-                    mPendingControlTarget = null;
-                }
+        } else if (mControllable) {
+            mWin.setControllableInsetProvider(this);
+            if (mPendingControlTarget != null) {
+                updateControlForTarget(mPendingControlTarget, true /* force */);
+                mPendingControlTarget = null;
             }
         }
     }
@@ -557,11 +552,6 @@
                 // TODO: use 0 alpha and remove t.hide() once b/138459974 is fixed.
                 t.setAlpha(animationLeash, 1 /* alpha */);
                 t.hide(animationLeash);
-
-                // TODO(b/175954493): Remove this after finding root cause.
-                if (IS_DEBUGGABLE) {
-                    animationLeash.setDebugRelease(true);
-                }
             }
             ProtoLog.i(WM_DEBUG_IME,
                     "ControlAdapter startAnimation mSource: %s controlTarget: %s", mSource,
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 7b709ea..398049f 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -104,8 +104,6 @@
      * visible to the target. e.g., the source which represents the target window itself, and the
      * IME source when the target is above IME. We also need to exclude certain types of insets
      * source for client within specific windowing modes.
-     * This is to get the insets for a window layout on the screen. If the window is not there, use
-     * the {@link #getInsetsForWindowMetrics} to get insets instead.
      *
      * @param target The window associate with the perspective.
      * @return The state stripped of the necessary information.
@@ -119,7 +117,7 @@
         final @InternalInsetsType int type = provider != null
                 ? provider.getSource().getType() : ITYPE_INVALID;
         return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(),
-                target.mAboveInsetsState);
+                isAboveIme(target));
     }
 
     InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
@@ -134,7 +132,19 @@
         final @WindowingMode int windowingMode = token != null
                 ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
         final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
-        return getInsetsForTarget(type, windowingMode, alwaysOnTop, mState);
+        return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token));
+    }
+
+    private boolean isAboveIme(WindowContainer target) {
+        final WindowState imeWindow = mDisplayContent.mInputMethodWindow;
+        if (target == null || imeWindow == null) {
+            return false;
+        }
+        if (target instanceof WindowState) {
+            final WindowState win = (WindowState) target;
+            return win.needsRelativeLayeringToIme() || !win.mBehindIme;
+        }
+        return false;
     }
 
     private static @InternalInsetsType
@@ -170,12 +180,11 @@
      * @see #getInsetsForWindowMetrics
      */
     private InsetsState getInsetsForTarget(@InternalInsetsType int type,
-            @WindowingMode int windowingMode, boolean isAlwaysOnTop, InsetsState state) {
-        boolean stateCopied = false;
+            @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) {
+        InsetsState state = mState;
 
         if (type != ITYPE_INVALID) {
             state = new InsetsState(state);
-            stateCopied = true;
             state.removeSource(type);
 
             // Navigation bar doesn't get influenced by anything else
@@ -210,15 +219,23 @@
 
         if (WindowConfiguration.isFloating(windowingMode)
                 || (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
-            if (!stateCopied) {
-                state = new InsetsState(state);
-                stateCopied = true;
-            }
+            state = new InsetsState(state);
             state.removeSource(ITYPE_STATUS_BAR);
             state.removeSource(ITYPE_NAVIGATION_BAR);
             state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
         }
 
+        if (aboveIme) {
+            InsetsSource imeSource = state.peekSource(ITYPE_IME);
+            if (imeSource != null && imeSource.isVisible()) {
+                imeSource = new InsetsSource(imeSource);
+                imeSource.setVisible(false);
+                imeSource.setFrame(0, 0, 0, 0);
+                state = new InsetsState(state);
+                state.addSource(imeSource);
+            }
+        }
+
         return state;
     }
 
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
index 91014aa..26871d1 100644
--- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -28,7 +28,7 @@
  */
 class RefreshRatePolicy {
 
-    private final int mLowRefreshRateId;
+    private final Mode mLowRefreshRateMode;
     private final ArraySet<String> mNonHighRefreshRatePackages = new ArraySet<>();
     private final HighRefreshRateDenylist mHighRefreshRateDenylist;
     private final WindowManagerService mWmService;
@@ -56,7 +56,7 @@
 
     RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo,
             HighRefreshRateDenylist denylist) {
-        mLowRefreshRateId = findLowRefreshRateModeId(displayInfo);
+        mLowRefreshRateMode = findLowRefreshRateMode(displayInfo);
         mHighRefreshRateDenylist = denylist;
         mWmService = wmService;
     }
@@ -65,7 +65,7 @@
      * Finds the mode id with the lowest refresh rate which is >= 60hz and same resolution as the
      * default mode.
      */
-    private int findLowRefreshRateModeId(DisplayInfo displayInfo) {
+    private Mode findLowRefreshRateMode(DisplayInfo displayInfo) {
         Mode mode = displayInfo.getDefaultMode();
         float[] refreshRates = displayInfo.getDefaultRefreshRates();
         float bestRefreshRate = mode.getRefreshRate();
@@ -104,13 +104,9 @@
 
         // If app is using Camera, force it to default (lower) refresh rate.
         if (mNonHighRefreshRatePackages.contains(packageName)) {
-            return mLowRefreshRateId;
+            return mLowRefreshRateMode.getModeId();
         }
 
-        // If app is denylisted using higher refresh rate, return default (lower) refresh rate
-        if (mHighRefreshRateDenylist.isDenylisted(packageName)) {
-            return mLowRefreshRateId;
-        }
         return 0;
     }
 
@@ -137,4 +133,18 @@
         }
         return LAYER_PRIORITY_UNSET;
     }
+
+    float getPreferredRefreshRate(WindowState w) {
+        // If app is animating, it's not able to control refresh rate because we want the animation
+        // to run in default refresh rate.
+        if (w.isAnimating(TRANSITION | PARENTS)) {
+            return 0;
+        }
+
+        final String packageName = w.getOwningPackage();
+        if (mHighRefreshRateDenylist.isDenylisted(packageName)) {
+            return mLowRefreshRateMode.getRefreshRate();
+        }
+        return 0;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 79d1123..ec1588d 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -124,6 +124,7 @@
 import static com.android.server.wm.Task.ActivityState.STARTED;
 import static com.android.server.wm.Task.ActivityState.STOPPING;
 import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
+import static com.android.server.wm.TaskProto.AFFINITY;
 import static com.android.server.wm.TaskProto.BOUNDS;
 import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
 import static com.android.server.wm.TaskProto.DISPLAY_ID;
@@ -1244,27 +1245,20 @@
         mCallingFeatureId = r.launchedFromFeatureId;
         setIntent(intent != null ? intent : r.intent, info != null ? info : r.info);
         setLockTaskAuth(r);
-
-        final WindowContainer parent = getParent();
-        if (parent != null) {
-            final Task t = parent.asTask();
-            if (t != null) {
-                t.setIntent(r);
-            }
-        }
     }
 
     /** Sets the original intent, _without_ updating the calling uid or package. */
     private void setIntent(Intent _intent, ActivityInfo info) {
-        final boolean isLeaf = isLeafTask();
+        if (!isLeafTask()) return;
+
         if (intent == null) {
             mNeverRelinquishIdentity =
                     (info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
-        } else if (mNeverRelinquishIdentity && isLeaf) {
+        } else if (mNeverRelinquishIdentity) {
             return;
         }
 
-        affinity = isLeaf ? info.taskAffinity : null;
+        affinity = info.taskAffinity;
         if (intent == null) {
             // If this task already has an intent associated with it, don't set the root
             // affinity -- we don't want it changing after initially set, but the initially
@@ -7809,6 +7803,7 @@
         }
 
         proto.write(CREATED_BY_ORGANIZER, mCreatedByOrganizer);
+        proto.write(AFFINITY, affinity);
 
         proto.end(token);
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3be4e78..093106f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -212,7 +212,6 @@
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
 import android.util.MergedConfiguration;
@@ -233,6 +232,7 @@
 import android.view.InsetsSource;
 import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
+import android.view.Surface;
 import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
@@ -647,14 +647,9 @@
     boolean mSeamlesslyRotated = false;
 
     /**
-     * The insets state of sources provided by windows above the current window.
+     * Indicates if this window is behind IME. Only windows behind IME can get insets from IME.
      */
-    InsetsState mAboveInsetsState = new InsetsState();
-
-    /**
-     * The insets sources provided by this window.
-     */
-    ArrayMap<Integer, InsetsSource> mProvidedInsetsSources = new ArrayMap<>();
+    boolean mBehindIme = false;
 
     /**
      * Surface insets from the previous call to relayout(), used to track
@@ -730,6 +725,13 @@
      */
     int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET;
 
+    /**
+     * This is the frame rate which is passed to SurfaceFlinger if the window is part of the
+     * high refresh rate deny list. The variable is cached, so we do not send too many updates to
+     * SF.
+     */
+    float mDenyListFrameRate = 0f;
+
     static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */
 
     private final WindowProcessController mWpcForDisplayAreaConfigChanges;
@@ -5233,7 +5235,6 @@
         return (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 && mOwnerCanUseBackgroundBlur;
     }
 
-
     /**
      * Notifies SF about the priority of the window, if it changed. SF then uses this information
      * to decide which window's desired rendering rate should have a priority when deciding about
@@ -5242,13 +5243,21 @@
      */
     @VisibleForTesting
     void updateFrameRateSelectionPriorityIfNeeded() {
-        final int priority = getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
-                .calculatePriority(this);
+        RefreshRatePolicy refreshRatePolicy =
+                getDisplayContent().getDisplayPolicy().getRefreshRatePolicy();
+        final int priority = refreshRatePolicy.calculatePriority(this);
         if (mFrameRateSelectionPriority != priority) {
             mFrameRateSelectionPriority = priority;
             getPendingTransaction().setFrameRateSelectionPriority(mSurfaceControl,
                     mFrameRateSelectionPriority);
         }
+
+        final float refreshRate = refreshRatePolicy.getPreferredRefreshRate(this);
+        if (mDenyListFrameRate != refreshRate) {
+            mDenyListFrameRate = refreshRate;
+            getPendingTransaction().setFrameRate(
+                    mSurfaceControl, mDenyListFrameRate, Surface.FRAME_RATE_COMPATIBILITY_EXACT);
+        }
     }
 
     private void updateGlobalScaleIfNeeded() {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 9d013c1..345b246 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -70,6 +70,7 @@
         "frameworks/base/libs",
         "frameworks/native/services",
         "system/gatekeeper/include",
+        "system/memory/libmeminfo/include",
     ],
 
     header_libs: [
@@ -97,6 +98,7 @@
         "libhardware_legacy",
         "libhidlbase",
         "libkeystore_binder",
+        "libmeminfo",
         "libmtp",
         "libnativehelper",
         "libnativewindow",
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 678308a..156ef79 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -17,15 +17,25 @@
 #define LOG_TAG "CachedAppOptimizer"
 //#define LOG_NDEBUG 0
 
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <cutils/compiler.h>
 #include <dirent.h>
+#include <jni.h>
+#include <linux/errno.h>
+#include <log/log.h>
+#include <meminfo/procmeminfo.h>
+#include <nativehelper/JNIHelp.h>
 #include <stddef.h>
 #include <stdio.h>
+#include <sys/mman.h>
 #include <sys/stat.h>
+#include <sys/syscall.h>
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <android-base/stringprintf.h>
-#include <android-base/file.h>
+#include <algorithm>
 
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
@@ -35,12 +45,149 @@
 
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
+using android::meminfo::ProcMemInfo;
+using namespace android::meminfo;
+
+// This is temporarily hard-coded and should be removed once
+// bionic/libc/kernel/uapi/asm-generic/unistd.h are updated with process_madvise syscall header
+#ifndef __NR_process_madvise
+#define __NR_process_madvise 440
+#define MADV_COLD 20 /* deactivate these pages */
+#define MADV_PAGEOUT 21
+#endif
+
+#define COMPACT_ACTION_FILE_FLAG 1
+#define COMPACT_ACTION_ANON_FLAG 2
+
+using VmaToAdviseFunc = std::function<int(const Vma&)>;
 
 #define SYNC_RECEIVED_WHILE_FROZEN (1)
 #define ASYNC_RECEIVED_WHILE_FROZEN (2)
 
 namespace android {
 
+// Legacy method for compacting processes, any new code should
+// use compactProcess instead.
+static inline void compactProcessProcfs(int pid, const std::string& compactionType) {
+    std::string reclaim_path = StringPrintf("/proc/%d/reclaim", pid);
+    WriteStringToFile(compactionType, reclaim_path);
+}
+
+static int compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType) {
+    // UIO_MAXIOV is currently a small value and we might have more addresses
+    // we do multiple syscalls if we exceed its maximum
+    static struct iovec vmasToKernel[UIO_MAXIOV];
+
+    int err = 0;
+
+    if (vmas.empty()) {
+        return err;
+    }
+
+    int pidfd = syscall(__NR_pidfd_open, pid, 0);
+    err = -errno;
+    if (err < 0) {
+        // Skip compaction if failed to open pidfd with any error
+        return err;
+    }
+
+    for (int iBase = 0; iBase < vmas.size(); iBase += UIO_MAXIOV) {
+        int totalVmasToKernel = std::min(UIO_MAXIOV, (int)(vmas.size() - iBase));
+        for (int iVec = 0, iVma = iBase; iVec < totalVmasToKernel; ++iVec, ++iVma) {
+            vmasToKernel[iVec].iov_base = (void*)vmas[iVma].start;
+            vmasToKernel[iVec].iov_len = vmas[iVma].end - vmas[iVma].start;
+        }
+
+        process_madvise(pidfd, vmasToKernel, totalVmasToKernel, madviseType, 0);
+        err = -errno;
+        if (CC_UNLIKELY(err == -ENOSYS)) {
+            // Syscall does not exist, skip trying more calls process_madvise
+            break;
+        }
+    }
+
+    close(pidfd);
+
+    return err;
+}
+
+static int getFilePageAdvice(const Vma& vma) {
+    if (vma.inode > 0 && !vma.is_shared) {
+        return MADV_COLD;
+    }
+    return -1;
+}
+static int getAnonPageAdvice(const Vma& vma) {
+    if (vma.inode == 0 && !vma.is_shared) {
+        return MADV_PAGEOUT;
+    }
+    return -1;
+}
+static bool getAnyPageAdvice(const Vma& vma) {
+    if (vma.inode == 0 && !vma.is_shared) {
+        return MADV_PAGEOUT;
+    }
+    return MADV_COLD;
+}
+
+// Perform a full process compaction using process_madvise syscall
+// reading all filtering VMAs and filtering pages as specified by pageFilter
+static int compactProcess(int pid, VmaToAdviseFunc vmaToAdviseFunc) {
+    ProcMemInfo meminfo(pid);
+    std::vector<Vma> pageoutVmas, coldVmas;
+    auto vmaCollectorCb = [&](Vma vma) {
+        int advice = vmaToAdviseFunc(vma);
+        switch (advice) {
+            case MADV_COLD:
+                coldVmas.push_back(vma);
+                break;
+            case MADV_PAGEOUT:
+                pageoutVmas.push_back(vma);
+                break;
+        }
+    };
+    meminfo.ForEachVma(vmaCollectorCb);
+
+    int err = compactMemory(pageoutVmas, pid, MADV_PAGEOUT);
+    if (!err) {
+        err = compactMemory(coldVmas, pid, MADV_COLD);
+    }
+    return err;
+}
+
+// Compact process using process_madvise syscall or fallback to procfs in
+// case syscall does not exist.
+static void compactProcessOrFallback(int pid, int compactionFlags) {
+    if ((compactionFlags & (COMPACT_ACTION_ANON_FLAG | COMPACT_ACTION_FILE_FLAG)) == 0) return;
+
+    bool compactAnon = compactionFlags & COMPACT_ACTION_ANON_FLAG;
+    bool compactFile = compactionFlags & COMPACT_ACTION_FILE_FLAG;
+
+    // Set when the system does not support process_madvise syscall to avoid
+    // gathering VMAs in subsequent calls prior to falling back to procfs
+    static bool shouldForceProcFs = false;
+    std::string compactionType;
+    VmaToAdviseFunc vmaToAdviseFunc;
+
+    if (compactAnon) {
+        if (compactFile) {
+            compactionType = "all";
+            vmaToAdviseFunc = getAnyPageAdvice;
+        } else {
+            compactionType = "anon";
+            vmaToAdviseFunc = getAnonPageAdvice;
+        }
+    } else {
+        compactionType = "file";
+        vmaToAdviseFunc = getFilePageAdvice;
+    }
+
+    if (shouldForceProcFs || compactProcess(pid, vmaToAdviseFunc) == -ENOSYS) {
+        shouldForceProcFs = true;
+        compactProcessProcfs(pid, compactionType);
+    }
+}
+
 // This performs per-process reclaim on all processes belonging to non-app UIDs.
 // For the most part, these are non-zygote processes like Treble HALs, but it
 // also includes zygote-derived processes that run in system UIDs, like bluetooth
@@ -74,11 +221,17 @@
             continue;
         }
 
-        std::string reclaim_path = StringPrintf("/proc/%s/reclaim", current->d_name);
-        WriteStringToFile(std::string("all"), reclaim_path);
+        int pid = atoi(current->d_name);
+
+        compactProcessOrFallback(pid, COMPACT_ACTION_ANON_FLAG | COMPACT_ACTION_FILE_FLAG);
     }
 }
 
+static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, jobject, jint pid,
+                                                                    jint compactionFlags) {
+    compactProcessOrFallback(pid, compactionFlags);
+}
+
 static void com_android_server_am_CachedAppOptimizer_enableFreezerInternal(
         JNIEnv *env, jobject clazz, jboolean enable) {
     bool success = true;
@@ -126,14 +279,14 @@
 }
 
 static const JNINativeMethod sMethods[] = {
-    /* name, signature, funcPtr */
-    {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
-    {"enableFreezerInternal", "(Z)V",
-        (void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal},
-    {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
-    {"getBinderFreezeInfo", "(I)I",
-        (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo}
-};
+        /* name, signature, funcPtr */
+        {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
+        {"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess},
+        {"enableFreezerInternal", "(Z)V",
+         (void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal},
+        {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
+        {"getBinderFreezeInfo", "(I)I",
+         (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo}};
 
 int register_android_server_am_CachedAppOptimizer(JNIEnv* env)
 {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 12595af..fd8dfc3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -326,13 +326,15 @@
     private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS =
             "com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle";
     private static final String LOCATION_TIME_ZONE_MANAGER_SERVICE_CLASS =
-            "com.android.server.location.timezone.LocationTimeZoneManagerService$Lifecycle";
+            "com.android.server.timezonedetector.location.LocationTimeZoneManagerService$Lifecycle";
     private static final String GNSS_TIME_UPDATE_SERVICE_CLASS =
             "com.android.server.timedetector.GnssTimeUpdateService$Lifecycle";
     private static final String ACCESSIBILITY_MANAGER_SERVICE_CLASS =
             "com.android.server.accessibility.AccessibilityManagerService$Lifecycle";
     private static final String ADB_SERVICE_CLASS =
             "com.android.server.adb.AdbService$Lifecycle";
+    private static final String SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS =
+            "com.android.server.speech.SpeechRecognitionManagerService";
     private static final String APP_PREDICTION_MANAGER_SERVICE_CLASS =
             "com.android.server.appprediction.AppPredictionManagerService";
     private static final String CONTENT_SUGGESTIONS_SERVICE_CLASS =
@@ -1629,12 +1631,21 @@
                         "MusicRecognitionManagerService not defined by OEM or disabled by flag");
             }
 
-
             startContentCaptureService(context, t);
             startAttentionService(context, t);
             startRotationResolverService(context, t);
             startSystemCaptionsManagerService(context, t);
 
+            // System Speech Recognition Service
+            if (deviceHasConfigString(context,
+                    R.string.config_defaultOnDeviceSpeechRecognitionService)) {
+                t.traceBegin("StartSpeechRecognitionManagerService");
+                mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS);
+                t.traceEnd();
+            } else {
+                Slog.d(TAG, "System speech recognition is not defined by OEM");
+            }
+
             // App prediction manager service
             if (deviceHasConfigString(context, R.string.config_defaultAppPredictionService)) {
                 t.traceBegin("StartAppPredictionService");
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 26c304f..2e3178b 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -818,10 +818,5 @@
                         PEAK_REFRESH_RATE_URI);
             }
         }
-
-        @Override
-        public boolean isDeviceInteractive(@NonNull Context context) {
-            return true;
-        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java
new file mode 100644
index 0000000..c10cee9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.graphics.fonts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class PersistentSystemFontConfigTest {
+
+    @Test
+    public void testWriteRead() throws IOException, XmlPullParserException {
+        long expectedModifiedDate = 1234567890;
+        PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
+        config.lastModifiedDate = expectedModifiedDate;
+
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            PersistentSystemFontConfig.writeToXml(baos, config);
+
+            byte[] written = baos.toByteArray();
+            assertThat(written).isNotEmpty();
+
+            try (ByteArrayInputStream bais = new ByteArrayInputStream(written)) {
+                PersistentSystemFontConfig.Config another = new PersistentSystemFontConfig.Config();
+                PersistentSystemFontConfig.loadFromXml(bais, another);
+
+                assertThat(another.lastModifiedDate).isEqualTo(expectedModifiedDate);
+            }
+        }
+    }
+
+    @Test
+    public void testWrongType() throws IOException, XmlPullParserException {
+        String xml = "<fontConfig>"
+                + "  <lastModifiedDate value=\"string\" />"
+                + "</fontConfig>";
+
+        try (ByteArrayInputStream bais =
+                     new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+            PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
+            PersistentSystemFontConfig.loadFromXml(bais, config);
+            assertThat(config.lastModifiedDate).isEqualTo(0);
+        }
+    }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index 75bf1e6..f437d1f 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -22,8 +22,10 @@
 import static org.junit.Assert.fail;
 
 import android.content.Context;
+import android.graphics.fonts.FontManager;
 import android.os.FileUtils;
 import android.platform.test.annotations.Presubmit;
+import android.system.Os;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -36,6 +38,7 @@
 
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
@@ -107,6 +110,7 @@
 
     private File mCacheDir;
     private File mUpdatableFontFilesDir;
+    private File mConfigFile;
     private List<File> mPreinstalledFontDirs;
 
     @SuppressWarnings("ResultOfMethodCallIgnored")
@@ -124,6 +128,7 @@
         for (File dir : mPreinstalledFontDirs) {
             dir.mkdir();
         }
+        mConfigFile = new File(mCacheDir, "config.xml");
     }
 
     @After
@@ -133,19 +138,30 @@
 
     @Test
     public void construct() throws Exception {
+        long expectedModifiedDate = 1234567890;
         FakeFontFileParser parser = new FakeFontFileParser();
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
+        config.lastModifiedDate = expectedModifiedDate;
+        writeConfig(config, mConfigFile);
         UpdatableFontDir dirForPreparation = new UpdatableFontDir(
-                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
+        assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedDate())
+                .isEqualTo(expectedModifiedDate);
         installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
         installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
         installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
         installFontFile(dirForPreparation, "bar,4", GOOD_SIGNATURE);
         // Four font dirs are created.
         assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
+        //
+        assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedDate())
+                .isNotEqualTo(expectedModifiedDate);
 
         UpdatableFontDir dir = new UpdatableFontDir(
-                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
         assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
         assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(3);
         assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
@@ -159,7 +175,8 @@
         FakeFontFileParser parser = new FakeFontFileParser();
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
         UpdatableFontDir dir = new UpdatableFontDir(
-                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
         assertThat(dir.getFontFileMap()).isEmpty();
     }
 
@@ -168,7 +185,8 @@
         FakeFontFileParser parser = new FakeFontFileParser();
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
         UpdatableFontDir dirForPreparation = new UpdatableFontDir(
-                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
         installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
         installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
         installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
@@ -179,7 +197,8 @@
         fakeFsverityUtil.remove(
                 dirForPreparation.getFontFileMap().get("foo.ttf").getAbsolutePath());
         UpdatableFontDir dir = new UpdatableFontDir(
-                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
         assertThat(dir.getFontFileMap()).isEmpty();
         // All font dirs (including dir for "bar.ttf") should be deleted.
         assertThat(mUpdatableFontFilesDir.list()).hasLength(0);
@@ -190,7 +209,8 @@
         FakeFontFileParser parser = new FakeFontFileParser();
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
         UpdatableFontDir dirForPreparation = new UpdatableFontDir(
-                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
         installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
         installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
         installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
@@ -202,7 +222,8 @@
         FileUtils.stringToFile(dirForPreparation.getFontFileMap().get("foo.ttf"), "bar,4");
 
         UpdatableFontDir dir = new UpdatableFontDir(
-                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
         assertThat(dir.getFontFileMap()).isEmpty();
         // All font dirs (including dir for "bar.ttf") should be deleted.
         assertThat(mUpdatableFontFilesDir.list()).hasLength(0);
@@ -213,7 +234,8 @@
         FakeFontFileParser parser = new FakeFontFileParser();
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
         UpdatableFontDir dirForPreparation = new UpdatableFontDir(
-                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
         installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
         installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
         installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
@@ -226,7 +248,8 @@
         FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "bar.ttf"), "bar,1");
         FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(1), "bar.ttf"), "bar,2");
         UpdatableFontDir dir = new UpdatableFontDir(
-                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
         // For foo.ttf, preinstalled font (revision 5) should be used.
         assertThat(dir.getFontFileMap()).doesNotContainKey("foo.ttf");
         // For bar.ttf, updated font (revision 4) should be used.
@@ -239,15 +262,30 @@
     }
 
     @Test
+    public void construct_failedToLoadConfig() throws Exception {
+        FakeFontFileParser parser = new FakeFontFileParser();
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                new File("/dev/null"));
+        assertThat(dir.getFontFileMap()).isEmpty();
+    }
+
+    @Test
     public void installFontFile() throws Exception {
         FakeFontFileParser parser = new FakeFontFileParser();
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
         UpdatableFontDir dir = new UpdatableFontDir(
-                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
 
         installFontFile(dir, "test,1", GOOD_SIGNATURE);
         assertThat(dir.getFontFileMap()).containsKey("test.ttf");
         assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1);
+        File fontFile = dir.getFontFileMap().get("test.ttf");
+        assertThat(Os.stat(fontFile.getAbsolutePath()).st_mode & 0777).isEqualTo(0644);
+        File fontDir = fontFile.getParentFile();
+        assertThat(Os.stat(fontDir.getAbsolutePath()).st_mode & 0777).isEqualTo(0711);
     }
 
     @Test
@@ -255,7 +293,8 @@
         FakeFontFileParser parser = new FakeFontFileParser();
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
         UpdatableFontDir dir = new UpdatableFontDir(
-                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
 
         installFontFile(dir, "test,1", GOOD_SIGNATURE);
         Map<String, File> mapBeforeUpgrade = dir.getFontFileMap();
@@ -272,14 +311,15 @@
         FakeFontFileParser parser = new FakeFontFileParser();
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
         UpdatableFontDir dir = new UpdatableFontDir(
-                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
 
         installFontFile(dir, "test,2", GOOD_SIGNATURE);
         try {
             installFontFile(dir, "test,1", GOOD_SIGNATURE);
             fail("Expect IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // Expect
+        } catch (FontManagerService.SystemFontException e) {
+            assertThat(e.getErrorCode()).isEqualTo(FontManager.ERROR_CODE_DOWNGRADING);
         }
         assertThat(dir.getFontFileMap()).containsKey("test.ttf");
         assertWithMessage("Font should not be downgraded to an older revision")
@@ -291,7 +331,8 @@
         FakeFontFileParser parser = new FakeFontFileParser();
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
         UpdatableFontDir dir = new UpdatableFontDir(
-                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
 
         installFontFile(dir, "foo,1", GOOD_SIGNATURE);
         installFontFile(dir, "bar,2", GOOD_SIGNATURE);
@@ -302,17 +343,19 @@
     }
 
     @Test
-    public void installFontFile_invalidSignature() {
+    public void installFontFile_invalidSignature() throws Exception {
         FakeFontFileParser parser = new FakeFontFileParser();
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
         UpdatableFontDir dir = new UpdatableFontDir(
-                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
 
         try {
             installFontFile(dir, "test,1", "Invalid signature");
-            fail("Expect IOException");
-        } catch (IOException e) {
-            // Expect
+            fail("Expect SystemFontException");
+        } catch (FontManagerService.SystemFontException e) {
+            assertThat(e.getErrorCode())
+                    .isEqualTo(FontManager.ERROR_CODE_VERIFICATION_FAILURE);
         }
         assertThat(dir.getFontFileMap()).isEmpty();
     }
@@ -323,23 +366,155 @@
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
         FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test,1");
         UpdatableFontDir dir = new UpdatableFontDir(
-                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
 
         try {
             installFontFile(dir, "test,1", GOOD_SIGNATURE);
             fail("Expect IllegalArgumentException");
-        } catch (IllegalArgumentException e) {
-            // Expect
+        } catch (FontManagerService.SystemFontException e) {
+            assertThat(e.getErrorCode()).isEqualTo(FontManager.ERROR_CODE_DOWNGRADING);
+        }
+        assertThat(dir.getFontFileMap()).isEmpty();
+    }
+
+    @Test
+    public void installFontFile_failedToWriteConfigXml() throws Exception {
+        long expectedModifiedDate = 1234567890;
+        FakeFontFileParser parser = new FakeFontFileParser();
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test,1");
+
+        File readonlyDir = new File(mCacheDir, "readonly");
+        assertThat(readonlyDir.mkdir()).isTrue();
+        File readonlyFile = new File(readonlyDir, "readonly_config.xml");
+
+        PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
+        config.lastModifiedDate = expectedModifiedDate;
+        writeConfig(config, readonlyFile);
+
+        assertThat(readonlyDir.setWritable(false, false)).isTrue();
+        try {
+            UpdatableFontDir dir = new UpdatableFontDir(
+                    mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                    readonlyFile);
+
+            try {
+                installFontFile(dir, "test,2", GOOD_SIGNATURE);
+            } catch (FontManagerService.SystemFontException e) {
+                assertThat(e.getErrorCode())
+                        .isEqualTo(FontManager.ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE);
+            }
+            assertThat(dir.getSystemFontConfig().getLastModifiedDate())
+                    .isEqualTo(expectedModifiedDate);
+            assertThat(dir.getFontFileMap()).isEmpty();
+        } finally {
+            assertThat(readonlyDir.setWritable(true, true)).isTrue();
+        }
+    }
+
+    @Test
+    public void installFontFile_failedToParsePostScript() throws Exception {
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs,
+                new UpdatableFontDir.FontFileParser() {
+                    @Override
+                    public String getPostScriptName(File file) throws IOException {
+                        return null;
+                    }
+
+                    @Override
+                    public long getRevision(File file) throws IOException {
+                        return 0;
+                    }
+                }, fakeFsverityUtil, mConfigFile);
+
+        try {
+            installFontFile(dir, "foo,1", GOOD_SIGNATURE);
+            fail("Expect SystemFontException");
+        } catch (FontManagerService.SystemFontException e) {
+            assertThat(e.getErrorCode())
+                    .isEqualTo(FontManager.ERROR_CODE_MISSING_POST_SCRIPT_NAME);
+        }
+        assertThat(dir.getFontFileMap()).isEmpty();
+    }
+
+    @Test
+    public void installFontFile_failedToParsePostScriptName_invalidFont() throws Exception {
+        FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs,
+                new UpdatableFontDir.FontFileParser() {
+                    @Override
+                    public String getPostScriptName(File file) throws IOException {
+                        throw new IOException();
+                    }
+
+                    @Override
+                    public long getRevision(File file) throws IOException {
+                        return 0;
+                    }
+                }, fakeFsverityUtil, mConfigFile);
+
+        try {
+            installFontFile(dir, "foo,1", GOOD_SIGNATURE);
+            fail("Expect SystemFontException");
+        } catch (FontManagerService.SystemFontException e) {
+            assertThat(e.getErrorCode())
+                    .isEqualTo(FontManager.ERROR_CODE_INVALID_FONT_FILE);
+        }
+        assertThat(dir.getFontFileMap()).isEmpty();
+    }
+
+    @Test
+    public void installFontFile_renameToPsNameFailure() throws Exception {
+        UpdatableFontDir.FsverityUtil fakeFsverityUtil = new UpdatableFontDir.FsverityUtil() {
+            private final FakeFsverityUtil mFake = new FakeFsverityUtil();
+
+            @Override
+            public boolean hasFsverity(String path) {
+                return mFake.hasFsverity(path);
+            }
+
+            @Override
+            public void setUpFsverity(String path, byte[] pkcs7Signature) throws IOException {
+                mFake.setUpFsverity(path, pkcs7Signature);
+            }
+
+            @Override
+            public boolean rename(File src, File dest) {
+                return false;
+            }
+        };
+        FakeFontFileParser parser = new FakeFontFileParser();
+        UpdatableFontDir dir = new UpdatableFontDir(
+                mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+                mConfigFile);
+
+        try {
+            installFontFile(dir, "foo,1", GOOD_SIGNATURE);
+            fail("Expect SystemFontException");
+        } catch (FontManagerService.SystemFontException e) {
+            assertThat(e.getErrorCode())
+                    .isEqualTo(FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE);
         }
         assertThat(dir.getFontFileMap()).isEmpty();
     }
 
     private void installFontFile(UpdatableFontDir dir, String content, String signature)
-            throws IOException {
+            throws Exception {
         File file = File.createTempFile("font", "ttf", mCacheDir);
         FileUtils.stringToFile(file, content);
         try (FileInputStream in = new FileInputStream(file)) {
             dir.installFontFile(in.getFD(), signature.getBytes());
         }
     }
+
+    private void writeConfig(PersistentSystemFontConfig.Config config,
+            File file) throws IOException {
+        try (FileOutputStream fos = new FileOutputStream(file)) {
+            PersistentSystemFontConfig.writeToXml(fos, config);
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS b/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS
deleted file mode 100644
index 28aff18..0000000
--- a/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 847766
-nfuller@google.com
-include /core/java/android/app/timedetector/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
index 1581d9a..691d174 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
@@ -82,6 +82,11 @@
     }
 
     @Override
+    String getRebootEscrowServerBlob() {
+        return makeDirs(mStorageDir, super.getRebootEscrowServerBlob()).getAbsolutePath();
+    }
+
+    @Override
     protected File getSyntheticPasswordDirectoryForUser(int userId) {
         return makeDirs(mStorageDir, super.getSyntheticPasswordDirectoryForUser(
                 userId).getAbsolutePath());
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index f74e45b..a4ba4c8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -26,6 +26,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doNothing;
@@ -52,6 +53,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.widget.RebootEscrowListener;
+import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -92,6 +94,7 @@
     private UserManager mUserManager;
     private RebootEscrowManager.Callbacks mCallbacks;
     private IRebootEscrow mRebootEscrow;
+    private ResumeOnRebootServiceConnection mServiceConnection;
     private RebootEscrowKeyStoreManager mKeyStoreManager;
 
     LockSettingsStorageTestable mStorage;
@@ -108,6 +111,7 @@
 
     static class MockInjector extends RebootEscrowManager.Injector {
         private final IRebootEscrow mRebootEscrow;
+        private final ResumeOnRebootServiceConnection mServiceConnection;
         private final RebootEscrowProviderInterface mRebootEscrowProvider;
         private final UserManager mUserManager;
         private final MockableRebootEscrowInjected mInjected;
@@ -116,10 +120,11 @@
         MockInjector(Context context, UserManager userManager,
                 IRebootEscrow rebootEscrow,
                 RebootEscrowKeyStoreManager keyStoreManager,
+                LockSettingsStorageTestable storage,
                 MockableRebootEscrowInjected injected) {
-            super(context);
+            super(context, storage);
             mRebootEscrow = rebootEscrow;
-
+            mServiceConnection = null;
             RebootEscrowProviderHalImpl.Injector halInjector =
                     new RebootEscrowProviderHalImpl.Injector() {
                         @Override
@@ -133,6 +138,22 @@
             mInjected = injected;
         }
 
+        MockInjector(Context context, UserManager userManager,
+                ResumeOnRebootServiceConnection serviceConnection,
+                RebootEscrowKeyStoreManager keyStoreManager,
+                LockSettingsStorageTestable storage,
+                MockableRebootEscrowInjected injected) {
+            super(context, storage);
+            mServiceConnection = serviceConnection;
+            mRebootEscrow = null;
+            RebootEscrowProviderServerBasedImpl.Injector injector =
+                    new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection);
+            mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(storage, injector);
+            mUserManager = userManager;
+            mKeyStoreManager = keyStoreManager;
+            mInjected = injected;
+        }
+
         @Override
         public UserManager getUserManager() {
             return mUserManager;
@@ -165,6 +186,7 @@
         mUserManager = mock(UserManager.class);
         mCallbacks = mock(RebootEscrowManager.Callbacks.class);
         mRebootEscrow = mock(IRebootEscrow.class);
+        mServiceConnection = mock(ResumeOnRebootServiceConnection.class);
         mKeyStoreManager = mock(RebootEscrowKeyStoreManager.class);
         mAesKey = new SecretKeySpec(TEST_AES_KEY, "AES");
 
@@ -186,7 +208,12 @@
         when(mCallbacks.isUserSecure(SECURE_SECONDARY_USER_ID)).thenReturn(true);
         mInjected = mock(MockableRebootEscrowInjected.class);
         mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager, mRebootEscrow,
-                mKeyStoreManager, mInjected), mCallbacks, mStorage);
+                mKeyStoreManager, mStorage, mInjected), mCallbacks, mStorage);
+    }
+
+    private void setServerBasedRebootEscrowProvider() throws Exception {
+        mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager,
+                mServiceConnection, mKeyStoreManager, mStorage, mInjected), mCallbacks, mStorage);
     }
 
     @Test
@@ -202,6 +229,19 @@
     }
 
     @Test
+    public void prepareRebootEscrowServerBased_Success() throws Exception {
+        setServerBasedRebootEscrowProvider();
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+        assertFalse(mStorage.hasRebootEscrowServerBlob());
+    }
+
+    @Test
     public void prepareRebootEscrow_ClearCredentials_Success() throws Exception {
         RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
         mService.setRebootEscrowListener(mockListener);
@@ -246,6 +286,28 @@
     }
 
     @Test
+    public void armServiceServerBased_Success() throws Exception {
+        setServerBasedRebootEscrowProvider();
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertTrue(mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+
+        assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+        assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID));
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+    }
+
+    @Test
     public void armService_HalFailure_NonFatal() throws Exception {
         RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
         mService.setRebootEscrowListener(mockListener);
@@ -346,6 +408,40 @@
     }
 
     @Test
+    public void loadRebootEscrowDataIfAvailable_ServerBased_Success() throws Exception {
+        setServerBasedRebootEscrowProvider();
+
+        when(mInjected.getBootCount()).thenReturn(0);
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        // Use x -> x for both wrap & unwrap functions.
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertTrue(mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // pretend reboot happens here
+        when(mInjected.getBootCount()).thenReturn(1);
+        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+
+        when(mServiceConnection.unwrap(any(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        mService.loadRebootEscrowDataIfAvailable();
+        verify(mServiceConnection).unwrap(any(), anyLong());
+        assertTrue(metricsSuccessCaptor.getValue());
+        verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
+    }
+
+    @Test
     public void loadRebootEscrowDataIfAvailable_TooManyBootsInBetween_NoMetrics() throws Exception {
         when(mInjected.getBootCount()).thenReturn(0);
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java
new file mode 100644
index 0000000..bc1e025
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.stubbing.Answer;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RebootEscrowProviderServerBasedImplTests {
+    private SecretKey mKeyStoreEncryptionKey;
+    private RebootEscrowKey mRebootEscrowKey;
+    private ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection mServiceConnection;
+    private LockSettingsStorageTestable mStorage;
+    private RebootEscrowProviderServerBasedImpl mRebootEscrowProvider;
+    private Answer<byte[]> mFakeEncryption;
+
+    private static final byte[] TEST_AES_KEY = new byte[] {
+            0x48, 0x19, 0x12, 0x54, 0x13, 0x13, 0x52, 0x31,
+            0x44, 0x74, 0x61, 0x54, 0x29, 0x74, 0x37, 0x61,
+            0x70, 0x70, 0x75, 0x25, 0x27, 0x31, 0x49, 0x09,
+            0x26, 0x52, 0x72, 0x63, 0x63, 0x61, 0x78, 0x23,
+    };
+
+    @Before
+    public void setUp() throws Exception {
+        mKeyStoreEncryptionKey = new SecretKeySpec(TEST_AES_KEY, "AES");
+        mRebootEscrowKey = RebootEscrowKey.generate();
+        mServiceConnection = mock(
+                ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection.class);
+
+        Context context = new ContextWrapper(InstrumentationRegistry.getContext());
+        mStorage = new LockSettingsStorageTestable(context,
+                new File(InstrumentationRegistry.getContext().getFilesDir(), "locksettings"));
+        mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mStorage,
+                new RebootEscrowProviderServerBasedImpl.Injector(mServiceConnection));
+
+        mFakeEncryption = invocation -> {
+            byte[] secret = invocation.getArgument(0);
+            for (int i = 0; i < secret.length; i++) {
+                secret[i] = (byte) (secret[i] ^ 0xf);
+            }
+            return secret;
+        };
+    }
+
+    @Test
+    public void getAndClearRebootEscrowKey_loopback_success() throws Exception {
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
+        when(mServiceConnection.unwrap(any(), anyLong())).thenAnswer(mFakeEncryption);
+
+        assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
+        mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+
+        RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
+                mKeyStoreEncryptionKey);
+        assertThat(ks.getKeyBytes(), is(mRebootEscrowKey.getKeyBytes()));
+        assertFalse(mStorage.hasRebootEscrowServerBlob());
+    }
+
+    @Test
+    public void getAndClearRebootEscrowKey_WrongDecryptionMethod_failure() throws Exception {
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
+        when(mServiceConnection.unwrap(any(), anyLong())).thenAnswer(
+                invocation -> {
+                    byte[] secret = invocation.getArgument(0);
+                    for (int i = 0; i < secret.length; i++) {
+                        secret[i] = (byte) (secret[i] ^ 0xe);
+                    }
+                    return secret;
+                }
+        );
+
+        assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
+        mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // Expect to get wrong key bytes
+        RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
+                mKeyStoreEncryptionKey);
+        assertNotEquals(ks.getKeyBytes(), mRebootEscrowKey.getKeyBytes());
+        assertFalse(mStorage.hasRebootEscrowServerBlob());
+    }
+
+    @Test
+    public void getAndClearRebootEscrowKey_ServiceConnectionException_failure() throws Exception {
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
+        doThrow(IOException.class).when(mServiceConnection).unwrap(any(), anyLong());
+
+        assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
+        mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // Expect to get null key bytes when the server service fails to unwrap the blob.
+        RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
+                mKeyStoreEncryptionKey);
+        assertNull(ks);
+        assertFalse(mStorage.hasRebootEscrowServerBlob());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
index eaf62cb..0dcd608 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
@@ -23,12 +23,11 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.ApkLite;
-import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.parsing.ApkLite;
 import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.os.FileUtils;
@@ -38,6 +37,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.servicestests.R;
+import com.android.server.pm.PackageManagerException;
 import com.android.server.pm.parsing.TestPackageParser2;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
@@ -207,28 +207,35 @@
     }
 
     @Test
-    public void testPackageSizeWithDmFile()
-            throws IOException, PackageParserException {
+    public void testPackageSizeWithDmFile() throws IOException {
         copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
-        File dm = createDexMetadataFile("install_split_base.apk");
-        ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
+        final File dm = createDexMetadataFile("install_split_base.apk");
+        final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
                 ParseTypeImpl.forDefaultParsing().reset(), mTmpDir, 0 /* flags */);
         if (result.isError()) {
             throw new IllegalStateException(result.getErrorMessage(), result.getException());
         }
-        PackageParser.PackageLite pkg = result.getResult();
+        final PackageLite pkg = result.getResult();
         Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkg));
     }
 
     // This simulates the 'adb shell pm install' flow.
     @Test
-    public void testPackageSizeWithPartialPackageLite() throws IOException, PackageParserException {
-        File base = copyApkToToTmpDir("install_split_base", R.raw.install_split_base);
-        File dm = createDexMetadataFile("install_split_base.apk");
+    public void testPackageSizeWithPartialPackageLite() throws IOException,
+            PackageManagerException {
+        final File base = copyApkToToTmpDir("install_split_base", R.raw.install_split_base);
+        final File dm = createDexMetadataFile("install_split_base.apk");
         try (FileInputStream is = new FileInputStream(base)) {
-            ApkLite baseApk = PackageParser.parseApkLite(is.getFD(), base.getAbsolutePath(), 0);
-            PackageLite pkgLite = new PackageLite(null, baseApk.codePath, baseApk, null, null, null,
-                    null, null, null);
+            final ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(
+                    ParseTypeImpl.forDefaultParsing().reset(), is.getFD(),
+                    base.getAbsolutePath(), /* flags */ 0);
+            if (result.isError()) {
+                throw new PackageManagerException(result.getErrorCode(),
+                        result.getErrorMessage(), result.getException());
+            }
+            final ApkLite baseApk = result.getResult();
+            final PackageLite pkgLite = new PackageLite(null, baseApk.getPath(), baseApk, null,
+                    null, null, null, null, null);
             Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkgLite));
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
rename to services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
index 972b3bb..4284240 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
@@ -13,18 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
-import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_DISABLED;
-import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
-import static com.android.server.location.timezone.TestSupport.USER2_CONFIG_GEO_DETECTION_ENABLED;
-import static com.android.server.location.timezone.TimeZoneProviderEvent.createPermanentFailureEvent;
-import static com.android.server.location.timezone.TimeZoneProviderEvent.createUncertainEvent;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
+import static com.android.server.timezonedetector.location.TestSupport.USER1_CONFIG_GEO_DETECTION_DISABLED;
+import static com.android.server.timezonedetector.location.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
+import static com.android.server.timezonedetector.location.TestSupport.USER2_CONFIG_GEO_DETECTION_ENABLED;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -40,10 +38,10 @@
 import android.service.timezone.TimeZoneProviderSuggestion;
 import android.util.IndentingPrintWriter;
 
-import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
 import com.android.server.timezonedetector.ConfigurationInternal;
 import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
 import com.android.server.timezonedetector.TestState;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -66,9 +64,9 @@
     private static final TimeZoneProviderEvent USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2 =
             createSuggestionEvent(asList("Europe/Paris"));
     private static final TimeZoneProviderEvent USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT =
-            createUncertainEvent();
+            TimeZoneProviderEvent.createUncertainEvent();
     private static final TimeZoneProviderEvent USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT =
-            createPermanentFailureEvent("Test");
+            TimeZoneProviderEvent.createPermanentFailureEvent("Test");
 
     private TestThreadingDomain mTestThreadingDomain;
     private TestCallback mTestCallback;
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java
rename to services/tests/servicestests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java
index 02de24d..e7dd9794 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertSame;
@@ -24,7 +24,7 @@
 import android.os.HandlerThread;
 import android.platform.test.annotations.Presubmit;
 
-import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue;
+import com.android.server.timezonedetector.location.ThreadingDomain.SingleRunnableQueue;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/LocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/location/timezone/LocationTimeZoneProviderTest.java
rename to services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
index cb292db..095c868 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/LocationTimeZoneProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
@@ -13,17 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
 import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
 
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
-import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
+import static com.android.server.timezonedetector.location.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -40,10 +40,10 @@
 import android.service.timezone.TimeZoneProviderSuggestion;
 import android.util.IndentingPrintWriter;
 
-import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderListener;
-import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
 import com.android.server.timezonedetector.ConfigurationInternal;
 import com.android.server.timezonedetector.TestState;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderListener;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
rename to services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
index 4810563..d319488 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import android.annotation.UserIdInt;
 
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java
rename to services/tests/servicestests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java
index b1a5ff9..e08fea0 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 37fb0e9..82ffa765 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -123,15 +123,6 @@
         updateDisplayFrames();
     }
 
-    void addWindowWithRawInsetsState(WindowState win) {
-        addWindow(win);
-        // Without mPerformLayout in display content, the window cannot see any insets. Override the
-        // insets state with the global one.
-        final InsetsState insetsState =
-                win.getDisplayContent().getInsetsStateController().getRawInsetsState();
-        win.mAboveInsetsState = insetsState;
-    }
-
     public void setRotation(int rotation, boolean includingWindows) {
         mRotation = rotation;
         updateDisplayFrames();
@@ -281,7 +272,7 @@
     @Test
     public void layoutWindowLw_fitStatusBars() {
         mWindow.mAttrs.setFitInsetsTypes(Type.statusBars());
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -292,7 +283,7 @@
     @Test
     public void layoutWindowLw_fitNavigationBars() {
         mWindow.mAttrs.setFitInsetsTypes(Type.navigationBars());
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -303,7 +294,7 @@
     @Test
     public void layoutWindowLw_fitAllSides() {
         mWindow.mAttrs.setFitInsetsSides(Side.all());
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -314,7 +305,7 @@
     @Test
     public void layoutWindowLw_fitTopOnly() {
         mWindow.mAttrs.setFitInsetsSides(Side.TOP);
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -324,12 +315,11 @@
 
     @Test
     public void layoutWindowLw_fitInsetsIgnoringVisibility() {
-        final InsetsState state =
-                mDisplayContent.getInsetsStateController().getRawInsetsState();
+        final InsetsState state = mWindow.getInsetsState();
         state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
         state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
         mWindow.mAttrs.setFitInsetsIgnoringVisibility(true);
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -339,12 +329,11 @@
 
     @Test
     public void layoutWindowLw_fitInsetsNotIgnoringVisibility() {
-        final InsetsState state =
-                mDisplayContent.getInsetsStateController().getRawInsetsState();
+        final InsetsState state = mWindow.getInsetsState();
         state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
         state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
         mWindow.mAttrs.setFitInsetsIgnoringVisibility(false);
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -360,7 +349,8 @@
         state.getSource(InsetsState.ITYPE_IME).setFrame(
                 0, DISPLAY_HEIGHT - IME_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT);
         mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
-        addWindowWithRawInsetsState(mWindow);
+        mWindow.mBehindIme = true;
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -374,7 +364,7 @@
 
         mWindow.mAttrs.setFitInsetsTypes(Type.displayCutout());
         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -389,7 +379,7 @@
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -405,7 +395,7 @@
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -421,7 +411,7 @@
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -437,7 +427,7 @@
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -452,7 +442,7 @@
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -467,12 +457,11 @@
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
-        mDisplayContent.getInsetsStateController().getRawInsetsState()
-                .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
+        mWindow.getInsetsState().getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
         final InsetsState requestedState = new InsetsState();
         requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
         mWindow.updateRequestedVisibility(requestedState);
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -487,13 +476,12 @@
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
-        mDisplayContent.getInsetsStateController().getRawInsetsState()
-                .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
+        mWindow.getInsetsState().getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
         final InsetsState requestedState = new InsetsState();
         requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
         mWindow.updateRequestedVisibility(requestedState);
         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -509,7 +497,7 @@
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -525,7 +513,7 @@
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -541,7 +529,7 @@
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -557,7 +545,7 @@
         mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY;
         mWindow.mAttrs.width = DISPLAY_WIDTH;
         mWindow.mAttrs.height = DISPLAY_HEIGHT;
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -574,7 +562,7 @@
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -588,7 +576,7 @@
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -604,7 +592,7 @@
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -620,7 +608,7 @@
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -636,7 +624,7 @@
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
@@ -650,7 +638,7 @@
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_NOTHING;
-        addWindowWithRawInsetsState(mWindow);
+        addWindow(mWindow);
 
         final int forwardedInsetBottom = 50;
         mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom));
@@ -788,13 +776,9 @@
     public void testFixedRotationInsetsSourceFrame() {
         doReturn((mDisplayContent.getRotation() + 1) % 4).when(mDisplayContent)
                 .rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord));
-        mWindow.mAboveInsetsState.addSource(mDisplayContent.getInsetsStateController()
-                .getRawInsetsState().peekSource(ITYPE_STATUS_BAR));
-        final Rect frame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
-                .getSource(ITYPE_STATUS_BAR).getFrame();
+        final Rect frame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame();
         mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord);
-        final Rect rotatedFrame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
-                .getSource(ITYPE_STATUS_BAR).getFrame();
+        final Rect rotatedFrame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame();
 
         assertEquals(DISPLAY_WIDTH, frame.width());
         assertEquals(DISPLAY_HEIGHT, rotatedFrame.width());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 499507e..77537a9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -300,7 +300,6 @@
         displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
         mNavBarWindow.getControllableInsetProvider().setServerVisible(true);
         final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
-        mImeWindow.mAboveInsetsState = state;
         mDisplayContent.mDisplayFrames = new DisplayFrames(mDisplayContent.getDisplayId(),
                 state, displayInfo, null /* displayCutout */);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
index 032edde..325bca4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
@@ -18,15 +18,24 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import android.platform.test.annotations.Presubmit;
+import android.view.Display.Mode;
+import android.view.DisplayInfo;
+import android.view.Surface;
+import android.view.SurfaceControl;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -40,16 +49,40 @@
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class FrameRateSelectionPriorityTests extends WindowTestsBase {
+    private static final float FLOAT_TOLERANCE = 0.01f;
+    private static final int LOW_MODE_ID = 3;
+
+    private DisplayPolicy mDisplayPolicy = mock(DisplayPolicy.class);
+    private RefreshRatePolicy mRefreshRatePolicy;
+    private HighRefreshRateDenylist mDenylist = mock(HighRefreshRateDenylist.class);
+
+    @Before
+    public void setUp() {
+        DisplayInfo di = new DisplayInfo(mDisplayInfo);
+        Mode defaultMode = di.getDefaultMode();
+        di.supportedModes = new Mode[] {
+                new Mode(1, defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 90),
+                new Mode(2, defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 70),
+                new Mode(LOW_MODE_ID,
+                        defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 60),
+        };
+        di.defaultModeId = 1;
+        mRefreshRatePolicy = new RefreshRatePolicy(mWm, di, mDenylist);
+        when(mDisplayPolicy.getRefreshRatePolicy()).thenReturn(mRefreshRatePolicy);
+    }
 
     @Test
     public void basicTest() {
         final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
         assertNotNull("Window state is created", appWindow);
+
         assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+        assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
 
         appWindow.updateFrameRateSelectionPriorityIfNeeded();
         // Priority doesn't change.
         assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+        assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
 
         // Call the function a few times.
         appWindow.updateFrameRateSelectionPriorityIfNeeded();
@@ -57,7 +90,9 @@
 
         // Since nothing changed in the priority state, the transaction should not be updating.
         verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
-                appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+                any(SurfaceControl.class), anyInt());
+        verify(appWindow.getPendingTransaction(), never()).setFrameRate(
+                any(SurfaceControl.class), anyInt(), anyInt());
     }
 
     @Test
@@ -66,10 +101,16 @@
         assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
         assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
                 .getPreferredModeId(appWindow), 0);
+        assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+        assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
+                .getPreferredRefreshRate(appWindow), 0, FLOAT_TOLERANCE);
+
+        assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
 
         appWindow.updateFrameRateSelectionPriorityIfNeeded();
         // Priority stays MAX_VALUE.
         assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+        assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
         verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
                 appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
 
@@ -78,31 +119,38 @@
         appWindow.updateFrameRateSelectionPriorityIfNeeded();
         // Priority changes to 1.
         assertEquals(appWindow.mFrameRateSelectionPriority, 1);
+        assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
         verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
                 appWindow.getSurfaceControl(), 1);
+        verify(appWindow.getPendingTransaction(), never()).setFrameRate(
+                any(SurfaceControl.class), anyInt(), anyInt());
     }
 
     @Test
     public void testApplicationInFocusWithModeId() {
         final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
         assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+        assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
 
         // Application is in focus.
         appWindow.mToken.mDisplayContent.mCurrentFocus = appWindow;
         appWindow.updateFrameRateSelectionPriorityIfNeeded();
         // Priority changes.
         assertEquals(appWindow.mFrameRateSelectionPriority, 1);
+        assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
         // Update the mode ID to a requested number.
         appWindow.mAttrs.preferredDisplayModeId = 1;
         appWindow.updateFrameRateSelectionPriorityIfNeeded();
         // Priority changes.
         assertEquals(appWindow.mFrameRateSelectionPriority, 0);
+        assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
 
         // Remove the mode ID request.
         appWindow.mAttrs.preferredDisplayModeId = 0;
         appWindow.updateFrameRateSelectionPriorityIfNeeded();
         // Priority changes.
         assertEquals(appWindow.mFrameRateSelectionPriority, 1);
+        assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
 
         // Verify we called actions on Transactions correctly.
         verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
@@ -111,12 +159,15 @@
                 appWindow.getSurfaceControl(), 0);
         verify(appWindow.getPendingTransaction(), times(2)).setFrameRateSelectionPriority(
                 appWindow.getSurfaceControl(), 1);
+        verify(appWindow.getPendingTransaction(), never()).setFrameRate(
+                any(SurfaceControl.class), anyInt(), anyInt());
     }
 
     @Test
     public void testApplicationNotInFocusWithModeId() {
         final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
         assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+        assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
 
         final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus");
         appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;
@@ -124,23 +175,28 @@
         appWindow.updateFrameRateSelectionPriorityIfNeeded();
         // The window is not in focus.
         assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+        assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
 
         // Update the mode ID to a requested number.
         appWindow.mAttrs.preferredDisplayModeId = 1;
         appWindow.updateFrameRateSelectionPriorityIfNeeded();
         // Priority changes.
         assertEquals(appWindow.mFrameRateSelectionPriority, 2);
+        assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
 
         verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
                 appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
         verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
                 appWindow.getSurfaceControl(), 2);
+        verify(appWindow.getPendingTransaction(), never()).setFrameRate(
+                any(SurfaceControl.class), anyInt(), anyInt());
     }
 
     @Test
     public void testApplicationNotInFocusWithoutModeId() {
         final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
         assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+        assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
 
         final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus");
         appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;
@@ -148,14 +204,45 @@
         appWindow.updateFrameRateSelectionPriorityIfNeeded();
         // The window is not in focus.
         assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+        assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
 
         // Make sure that the mode ID is not set.
         appWindow.mAttrs.preferredDisplayModeId = 0;
         appWindow.updateFrameRateSelectionPriorityIfNeeded();
         // Priority doesn't change.
         assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+        assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
 
         verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
                 appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+        verify(appWindow.getPendingTransaction(), never()).setFrameRate(
+                any(SurfaceControl.class), anyInt(), anyInt());
+    }
+
+    @Test
+    public void testPreferredRefreshRate() {
+        final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
+        assertNotNull("Window state is created", appWindow);
+        when(appWindow.getDisplayContent().getDisplayPolicy()).thenReturn(mDisplayPolicy);
+
+        appWindow.mAttrs.packageName = "com.android.test";
+        when(mDenylist.isDenylisted("com.android.test")).thenReturn(true);
+
+        assertEquals(0, mRefreshRatePolicy.getPreferredModeId(appWindow));
+        assertEquals(60, mRefreshRatePolicy.getPreferredRefreshRate(appWindow), FLOAT_TOLERANCE);
+
+        appWindow.updateFrameRateSelectionPriorityIfNeeded();
+        assertEquals(RefreshRatePolicy.LAYER_PRIORITY_UNSET, appWindow.mFrameRateSelectionPriority);
+        assertEquals(60, appWindow.mDenyListFrameRate, FLOAT_TOLERANCE);
+
+        // Call the function a few times.
+        appWindow.updateFrameRateSelectionPriorityIfNeeded();
+        appWindow.updateFrameRateSelectionPriorityIfNeeded();
+
+        // Since nothing changed in the priority state, the transaction should not be updating.
+        verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
+                any(SurfaceControl.class), anyInt());
+        verify(appWindow.getPendingTransaction(), times(1)).setFrameRate(
+                appWindow.getSurfaceControl(), 60, Surface.FRAME_RATE_COMPATIBILITY_EXACT);
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index bf3ed69..e0fd379 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -45,7 +45,6 @@
 
 import android.app.StatusBarManager;
 import android.platform.test.annotations.Presubmit;
-import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 
@@ -273,6 +272,7 @@
         final WindowState navBar = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar");
         navBar.setHasSurface(true);
         navBar.getControllableInsetProvider().setServerVisible(true);
+
         final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
         doNothing().when(policy).startAnimation(anyBoolean(), any());
 
@@ -337,14 +337,11 @@
     @UseTestDisplay(addWindows = W_ACTIVITY)
     @Test
     public void testAbortTransientBars_bothCanBeAborted_appGetsBothRealControls() {
-        final InsetsSource statusBarSource = addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
-                .getControllableInsetProvider().getSource();
-        final InsetsSource navBarSource = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
-                .getControllableInsetProvider().getSource();
-        statusBarSource.setVisible(false);
-        navBarSource.setVisible(false);
-        mAppWindow.mAboveInsetsState.addSource(navBarSource);
-        mAppWindow.mAboveInsetsState.addSource(statusBarSource);
+        addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
+                .getControllableInsetProvider().getSource().setVisible(false);
+        addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
+                .getControllableInsetProvider().getSource().setVisible(false);
+
         final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
         doNothing().when(policy).startAnimation(anyBoolean(), any());
         policy.updateBarControlTarget(mAppWindow);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 2107ab1e..2766438 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -59,6 +59,25 @@
 public class InsetsStateControllerTest extends WindowTestsBase {
 
     @Test
+    public void testStripForDispatch_notOwn() {
+        final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+        statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR));
+        assertNotNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
+    }
+
+    @Test
+    public void testStripForDispatch_own() {
+        final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+        mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR)
+                .setWindow(statusBar, null, null);
+        statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR));
+        final InsetsState state = getController().getInsetsForWindow(statusBar);
+        assertNull(state.peekSource(ITYPE_STATUS_BAR));
+    }
+
+    @Test
     public void testStripForDispatch_navBar() {
         final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
@@ -123,15 +142,14 @@
         getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
 
         final WindowState app1 = createWindow(null, TYPE_APPLICATION, "app1");
-        final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
+        app1.mBehindIme = true;
 
-        app1.mAboveInsetsState.addSource(getController().getRawInsetsState().getSource(ITYPE_IME));
+        final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
+        app2.mBehindIme = false;
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertFalse(getController().getInsetsForWindow(app2).getSource(ITYPE_IME)
-                .isVisible());
-        assertTrue(getController().getInsetsForWindow(app1).getSource(ITYPE_IME)
-                .isVisible());
+        assertFalse(getController().getInsetsForWindow(app2).getSource(ITYPE_IME).isVisible());
+        assertTrue(getController().getInsetsForWindow(app1).getSource(ITYPE_IME).isVisible());
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -140,8 +158,7 @@
         getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
 
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        app.mAboveInsetsState.getSource(ITYPE_IME).setVisible(true);
-        app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame());
+        app.mBehindIme = true;
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
         assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
@@ -153,10 +170,10 @@
         getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
 
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        app.mBehindIme = false;
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
-        assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME)
-                .isVisible());
+        assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -193,8 +210,7 @@
 
         // app won't get visible IME insets while above IME even when IME is visible.
         assertTrue(getController().getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
-        assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME)
-                .isVisible());
+        assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
 
         // Reset invocation counter.
         clearInvocations(app);
@@ -203,8 +219,6 @@
         app.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
         mDisplayContent.computeImeTarget(true);
         mDisplayContent.applySurfaceChangesTransaction();
-        app.mAboveInsetsState.getSource(ITYPE_IME).setVisible(true);
-        app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame());
 
         // Make sure app got notified.
         verify(app, atLeast(1)).notifyInsetsChanged();
@@ -220,8 +234,6 @@
 
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
-        app.mAboveInsetsState.set(getController().getRawInsetsState());
-        child.mAboveInsetsState.set(getController().getRawInsetsState());
         child.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
 
         mDisplayContent.computeImeTarget(true);
@@ -230,8 +242,7 @@
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
         assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
-        assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME)
-                .isVisible());
+        assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME).isVisible());
     }
 
     @UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -241,7 +252,6 @@
 
         final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
-        app.mAboveInsetsState.addSource(getController().getRawInsetsState().peekSource(ITYPE_IME));
         child.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
         child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
 
@@ -251,8 +261,7 @@
 
         getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
         assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
-        assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME)
-                .isVisible());
+        assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME).isVisible());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index 77a4b05..ef3c7ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -44,7 +44,7 @@
 @RunWith(WindowTestRunner.class)
 @FlakyTest
 public class RefreshRatePolicyTest extends WindowTestsBase {
-
+    private static final float FLOAT_TOLERANCE = 0.01f;
     private static final int LOW_MODE_ID = 3;
 
     private RefreshRatePolicy mPolicy;
@@ -70,28 +70,34 @@
                 "cameraUsingWindow");
         cameraUsingWindow.mAttrs.packageName = "com.android.test";
         assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
+        assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
         mPolicy.addNonHighRefreshRatePackage("com.android.test");
         assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow));
+        assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
         mPolicy.removeNonHighRefreshRatePackage("com.android.test");
         assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
+        assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
     }
 
     @Test
-    public void testBlacklist() {
-        final WindowState blacklistedWindow = createWindow(null, TYPE_BASE_APPLICATION,
-                "blacklistedWindow");
-        blacklistedWindow.mAttrs.packageName = "com.android.test";
+    public void testDenyList() {
+        final WindowState denylistedWindow = createWindow(null, TYPE_BASE_APPLICATION,
+                "denylistedWindow");
+        denylistedWindow.mAttrs.packageName = "com.android.test";
         when(mDenylist.isDenylisted("com.android.test")).thenReturn(true);
-        assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(blacklistedWindow));
+        assertEquals(0, mPolicy.getPreferredModeId(denylistedWindow));
+        assertEquals(60, mPolicy.getPreferredRefreshRate(denylistedWindow), FLOAT_TOLERANCE);
     }
 
     @Test
     public void testAppOverride_blacklist() {
         final WindowState overrideWindow = createWindow(null, TYPE_BASE_APPLICATION,
                 "overrideWindow");
+        overrideWindow.mAttrs.packageName = "com.android.test";
         overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID;
         when(mDenylist.isDenylisted("com.android.test")).thenReturn(true);
         assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
+        assertEquals(60, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
     }
 
     @Test
@@ -102,6 +108,7 @@
         overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID;
         mPolicy.addNonHighRefreshRatePackage("com.android.test");
         assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
+        assertEquals(0, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
     }
 
     @Test
@@ -115,6 +122,7 @@
                 false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
         mPolicy.addNonHighRefreshRatePackage("com.android.test");
         assertEquals(0, mPolicy.getPreferredModeId(overrideWindow));
+        assertEquals(0, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
     }
 
     @Test
@@ -125,10 +133,12 @@
 
         mPolicy.addNonHighRefreshRatePackage("com.android.test");
         assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow));
+        assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
 
         cameraUsingWindow.mActivityRecord.mSurfaceAnimator.startAnimation(
                 cameraUsingWindow.getPendingTransaction(), mock(AnimationAdapter.class),
                 false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
         assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
+        assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index db77324..371e680 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -109,7 +109,7 @@
         final Rect originalOverrideBounds = new Rect(mActivity.getBounds());
         resizeDisplay(mTask.mDisplayContent, 600, 1200);
         // The visible activity should recompute configuration according to the last parent bounds.
-        mAtm.restartActivityProcessIfVisible(mActivity.appToken);
+        mAtm.mActivityClientController.restartActivityProcessIfVisible(mActivity.appToken);
 
         assertEquals(Task.ActivityState.RESTARTING_PROCESS, mActivity.getState());
         assertNotEquals(originalOverrideBounds, mActivity.getBounds());
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index c308fdb..b8d44f6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -245,6 +245,12 @@
     }
 
     @Override
+    public SurfaceControl.Transaction setFrameRate(SurfaceControl sc, float frameRate,
+            int compatibility) {
+        return this;
+    }
+
+    @Override
     public SurfaceControl.Transaction unsetColor(SurfaceControl sc) {
         return this;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index a1f89ec..1607f01 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -275,7 +275,7 @@
         imeSource.setFrame(imeFrame);
         imeSource.setVisible(true);
         w.updateRequestedVisibility(state);
-        w.mAboveInsetsState.addSource(imeSource);
+        w.mBehindIme = true;
 
         // With no insets or system decor all the frames incoming from PhoneWindowManager
         // are identical.
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index b3092b9..137543a 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -64,6 +64,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
@@ -5963,23 +5964,18 @@
         callback.expectCapabilitiesThat(mMockVpn,
                 nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
                         && nc.hasTransport(TRANSPORT_WIFI));
-
-        // BUG: the VPN is no longer suspended, so a RESUMED callback should have been sent.
-        // callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
+        callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
         callback.assertNoCallback();
 
         assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
                 .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
         assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
-        assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED);  // BUG: VPN caps have NOT_SUSPENDED.
+        assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
         assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
-        // BUG: the device has connectivity, so this should return true.
-        assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+        assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
 
-        // Unsuspend cellular and then switch back to it.
-        // The same bug happens in the opposite direction: the VPN's capabilities correctly have
-        // NOT_SUSPENDED, but the VPN's NetworkInfo is in state SUSPENDED.
+        // Unsuspend cellular and then switch back to it. The VPN remains not suspended.
         mCellNetworkAgent.resume();
         callback.assertNoCallback();
         mWiFiNetworkAgent.disconnect();
@@ -5996,12 +5992,11 @@
                 .hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
-        assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED);  // BUG: VPN caps have NOT_SUSPENDED.
+        assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
-        // BUG: the device has connectivity, so this should return true.
-        assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+        assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
 
-        // Re-suspending the current network fixes the problem.
+        // Suspend cellular and expect no connectivity.
         mCellNetworkAgent.suspend();
         callback.expectCapabilitiesThat(mMockVpn,
                 nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -6017,6 +6012,7 @@
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
         assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
 
+        // Resume cellular and expect that connectivity comes back.
         mCellNetworkAgent.resume();
         callback.expectCapabilitiesThat(mMockVpn,
                 nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -6407,10 +6403,7 @@
                 && caps.hasTransport(TRANSPORT_CELLULAR)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-        // While the SUSPENDED callback should in theory be sent here, it is not. This is
-        // a bug in ConnectivityService, but as the SUSPENDED and RESUMED callbacks have never
-        // been public and are deprecated and slated for removal, there is no sense in spending
-        // resources fixing this bug now.
+        vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
         assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
 
         // Use both again.
@@ -6422,8 +6415,7 @@
                 && caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
                 && !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
                 && caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-        // As above, the RESUMED callback not being sent here is a bug, but not a bug that's
-        // worth anybody's time to fix.
+        vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
         assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
 
         // Disconnect cell. Receive update without even removing the dead network from the
@@ -7335,39 +7327,68 @@
         b2.expectBroadcast();
     }
 
+    /**
+     * Test mutable and requestable network capabilities such as
+     * {@link NetworkCapabilities#NET_CAPABILITY_TRUSTED} and
+     * {@link NetworkCapabilities#NET_CAPABILITY_NOT_VCN_MANAGED}. Verify that the
+     * {@code ConnectivityService} re-assign the networks accordingly.
+     */
     @Test
-    public final void testLoseTrusted() throws Exception {
-        final NetworkRequest trustedRequest = new NetworkRequest.Builder()
-                .addCapability(NET_CAPABILITY_TRUSTED)
-                .build();
-        final TestNetworkCallback trustedCallback = new TestNetworkCallback();
-        mCm.requestNetwork(trustedRequest, trustedCallback);
+    public final void testLoseMutableAndRequestableCaps() throws Exception {
+        final int[] testCaps = new int [] {
+                NET_CAPABILITY_TRUSTED,
+                NET_CAPABILITY_NOT_VCN_MANAGED
+        };
+        for (final int testCap : testCaps) {
+            // Create requests with and without the testing capability.
+            final TestNetworkCallback callbackWithCap = new TestNetworkCallback();
+            final TestNetworkCallback callbackWithoutCap = new TestNetworkCallback();
+            mCm.requestNetwork(new NetworkRequest.Builder().addCapability(testCap).build(),
+                    callbackWithCap);
+            mCm.requestNetwork(new NetworkRequest.Builder().removeCapability(testCap).build(),
+                    callbackWithoutCap);
 
-        mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
-        mCellNetworkAgent.connect(true);
-        trustedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
-        verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
-        reset(mMockNetd);
+            // Setup networks with testing capability and verify the default network changes.
+            mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+            mCellNetworkAgent.addCapability(testCap);
+            mCellNetworkAgent.connect(true);
+            callbackWithCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+            callbackWithoutCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+            verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+            reset(mMockNetd);
 
-        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
-        mWiFiNetworkAgent.connect(true);
-        trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
-        verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId));
-        reset(mMockNetd);
+            mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+            mWiFiNetworkAgent.addCapability(testCap);
+            mWiFiNetworkAgent.connect(true);
+            callbackWithCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+            callbackWithoutCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+            verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId));
+            reset(mMockNetd);
 
-        mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
-        trustedCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
-        verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
-        reset(mMockNetd);
+            // Remove the testing capability on wifi, verify the callback and default network
+            // changes back to cellular.
+            mWiFiNetworkAgent.removeCapability(testCap);
+            callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent);
+            callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent);
+            // TODO: Test default network changes for NOT_VCN_MANAGED once the default request has
+            //  it.
+            if (testCap == NET_CAPABILITY_TRUSTED) {
+                verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+                reset(mMockNetd);
+            }
 
-        mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
-        trustedCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
-        verify(mMockNetd).networkClearDefault();
+            mCellNetworkAgent.removeCapability(testCap);
+            callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+            callbackWithoutCap.assertNoCallback();
+            if (testCap == NET_CAPABILITY_TRUSTED) {
+                verify(mMockNetd).networkClearDefault();
+            }
 
-        mCm.unregisterNetworkCallback(trustedCallback);
+            mCm.unregisterNetworkCallback(callbackWithCap);
+            mCm.unregisterNetworkCallback(callbackWithoutCap);
+        }
     }
 
-    @Ignore // 40%+ flakiness : figure out why and re-enable.
     @Test
     public final void testBatteryStatsNetworkType() throws Exception {
         final LinkProperties cellLp = new LinkProperties();
@@ -7375,8 +7396,8 @@
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
         mCellNetworkAgent.connect(true);
         waitForIdle();
-        verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
-                TYPE_MOBILE);
+        verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+                new int[] { TRANSPORT_CELLULAR });
         reset(mBatteryStatsService);
 
         final LinkProperties wifiLp = new LinkProperties();
@@ -7384,18 +7405,20 @@
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
         mWiFiNetworkAgent.connect(true);
         waitForIdle();
-        verify(mBatteryStatsService).noteNetworkInterfaceType(wifiLp.getInterfaceName(),
-                TYPE_WIFI);
+        verify(mBatteryStatsService).noteNetworkInterfaceForTransports(wifiLp.getInterfaceName(),
+                new int[] { TRANSPORT_WIFI });
         reset(mBatteryStatsService);
 
         mCellNetworkAgent.disconnect();
+        mWiFiNetworkAgent.disconnect();
 
         cellLp.setInterfaceName("wifi0");
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
         mCellNetworkAgent.connect(true);
         waitForIdle();
-        verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
-                TYPE_MOBILE);
+        verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+                new int[] { TRANSPORT_CELLULAR });
+        mCellNetworkAgent.disconnect();
     }
 
     /**
@@ -7468,8 +7491,8 @@
         assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute);
         verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
         verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME);
-        verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
-                TYPE_MOBILE);
+        verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+                new int[] { TRANSPORT_CELLULAR });
 
         networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
         verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
@@ -7489,7 +7512,8 @@
         // Make sure BatteryStats was not told about any v4- interfaces, as none should have
         // come online yet.
         waitForIdle();
-        verify(mBatteryStatsService, never()).noteNetworkInterfaceType(startsWith("v4-"), anyInt());
+        verify(mBatteryStatsService, never()).noteNetworkInterfaceForTransports(startsWith("v4-"),
+                any());
 
         verifyNoMoreInteractions(mMockNetd);
         verifyNoMoreInteractions(mMockDnsResolver);
@@ -7542,8 +7566,8 @@
         assertTrue(ArrayUtils.contains(resolvrParams.servers, "8.8.8.8"));
 
         for (final LinkProperties stackedLp : stackedLpsAfterChange) {
-            verify(mBatteryStatsService).noteNetworkInterfaceType(stackedLp.getInterfaceName(),
-                    TYPE_MOBILE);
+            verify(mBatteryStatsService).noteNetworkInterfaceForTransports(
+                    stackedLp.getInterfaceName(), new int[] { TRANSPORT_CELLULAR });
         }
         reset(mMockNetd);
         when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME))