Merge "Delegate playSoundEffect calls to VDM for virtual context"
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index 659db9f..c9981da 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -400,6 +400,8 @@
      * Returns {@code true} if the app currently holds the
      * {@link android.Manifest.permission#RUN_LONG_JOBS} permission, allowing it to run long jobs.
      * @hide
+     * TODO(255371817): consider exposing this to apps who could call
+     * {@link #scheduleAsPackage(JobInfo, String, int, String)}
      */
     public boolean hasRunLongJobsPermission(@NonNull String packageName, @UserIdInt int userId) {
         return false;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 1147e07..c032513 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -3660,11 +3660,8 @@
             return canPersist;
         }
 
-        private void validateJob(JobInfo job, int callingUid) {
-            validateJob(job, callingUid, null);
-        }
-
-        private void validateJob(JobInfo job, int callingUid, @Nullable JobWorkItem jobWorkItem) {
+        private int validateJob(@NonNull JobInfo job, int callingUid, int sourceUserId,
+                @Nullable String sourcePkgName, @Nullable JobWorkItem jobWorkItem) {
             final boolean rejectNegativeNetworkEstimates = CompatChanges.isChangeEnabled(
                             JobInfo.REJECT_NEGATIVE_NETWORK_ESTIMATES, callingUid);
             job.enforceValidity(
@@ -3684,6 +3681,37 @@
                             + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job);
                 }
             }
+            if (job.isUserInitiated()) {
+                int sourceUid = -1;
+                if (sourceUserId != -1 && sourcePkgName != null) {
+                    try {
+                        sourceUid = AppGlobals.getPackageManager().getPackageUid(
+                                sourcePkgName, 0, sourceUserId);
+                    } catch (RemoteException ex) {
+                        // Can't happen, PackageManager runs in the same process.
+                    }
+                }
+                // We aim to check the permission of both the source and calling app so that apps
+                // don't attempt to bypass the permission by using other apps to do the work.
+                if (sourceUid != -1) {
+                    // Check the permission of the source app.
+                    final int sourceResult =
+                            validateRunLongJobsPermission(sourceUid, sourcePkgName);
+                    if (sourceResult != JobScheduler.RESULT_SUCCESS) {
+                        return sourceResult;
+                    }
+                }
+                final String callingPkgName = job.getService().getPackageName();
+                if (callingUid != sourceUid || !callingPkgName.equals(sourcePkgName)) {
+                    // Source app is different from calling app. Make sure the calling app also has
+                    // the permission.
+                    final int callingResult =
+                            validateRunLongJobsPermission(callingUid, callingPkgName);
+                    if (callingResult != JobScheduler.RESULT_SUCCESS) {
+                        return callingResult;
+                    }
+                }
+            }
             if (jobWorkItem != null) {
                 jobWorkItem.enforceValidity(rejectNegativeNetworkEstimates);
                 if (jobWorkItem.getEstimatedNetworkDownloadBytes() != JobInfo.NETWORK_BYTES_UNKNOWN
@@ -3704,6 +3732,19 @@
                     }
                 }
             }
+            return JobScheduler.RESULT_SUCCESS;
+        }
+
+        private int validateRunLongJobsPermission(int uid, String packageName) {
+            final int state = getRunLongJobsPermissionState(uid, packageName);
+            if (state == PermissionChecker.PERMISSION_HARD_DENIED) {
+                throw new SecurityException(android.Manifest.permission.RUN_LONG_JOBS
+                        + " required to schedule user-initiated jobs.");
+            }
+            if (state == PermissionChecker.PERMISSION_SOFT_DENIED) {
+                return JobScheduler.RESULT_FAILURE;
+            }
+            return JobScheduler.RESULT_SUCCESS;
         }
 
         // IJobScheduler implementation
@@ -3724,7 +3765,10 @@
                 }
             }
 
-            validateJob(job, uid);
+            final int result = validateJob(job, uid, -1, null, null);
+            if (result != JobScheduler.RESULT_SUCCESS) {
+                return result;
+            }
 
             final long ident = Binder.clearCallingIdentity();
             try {
@@ -3752,7 +3796,10 @@
                 throw new NullPointerException("work is null");
             }
 
-            validateJob(job, uid, work);
+            final int result = validateJob(job, uid, -1, null, work);
+            if (result != JobScheduler.RESULT_SUCCESS) {
+                return result;
+            }
 
             final long ident = Binder.clearCallingIdentity();
             try {
@@ -3783,7 +3830,10 @@
                         + " not permitted to schedule jobs for other apps");
             }
 
-            validateJob(job, callerUid);
+            int result = validateJob(job, callerUid, userId, packageName, null);
+            if (result != JobScheduler.RESULT_SUCCESS) {
+                return result;
+            }
 
             final long ident = Binder.clearCallingIdentity();
             try {
@@ -4257,11 +4307,16 @@
         return 0;
     }
 
+    /** Returns true if both the appop and permission are granted. */
     private boolean checkRunLongJobsPermission(int packageUid, String packageName) {
-        // Returns true if both the appop and permission are granted.
+        return getRunLongJobsPermissionState(packageUid, packageName)
+                == PermissionChecker.PERMISSION_GRANTED;
+    }
+
+    private int getRunLongJobsPermissionState(int packageUid, String packageName) {
         return PermissionChecker.checkPermissionForPreflight(getTestableContext(),
                 android.Manifest.permission.RUN_LONG_JOBS, PermissionChecker.PID_UNKNOWN,
-                packageUid, packageName) == PermissionChecker.PERMISSION_GRANTED;
+                packageUid, packageName);
     }
 
     @VisibleForTesting
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 80512f7..00d9a4b 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -603,6 +603,15 @@
     mFlingerSurface = s;
     mTargetInset = -1;
 
+    // Rotate the boot animation according to the value specified in the sysprop
+    // ro.bootanim.set_orientation_<display_id>. Four values are supported: ORIENTATION_0,
+    // ORIENTATION_90, ORIENTATION_180 and ORIENTATION_270.
+    // If the value isn't specified or is ORIENTATION_0, nothing will be changed.
+    // This is needed to support having boot animation in orientations different from the natural
+    // device orientation. For example, on tablets that may want to keep natural orientation
+    // portrait for applications compatibility and to have the boot animation in landscape.
+    rotateAwayFromNaturalOrientationIfNeeded();
+
     projectSceneToWindow();
 
     // Register a display event receiver
@@ -616,6 +625,50 @@
     return NO_ERROR;
 }
 
+void BootAnimation::rotateAwayFromNaturalOrientationIfNeeded() {
+    const auto orientation = parseOrientationProperty();
+
+    if (orientation == ui::ROTATION_0) {
+        // Do nothing if the sysprop isn't set or is set to ROTATION_0.
+        return;
+    }
+
+    if (orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270) {
+        std::swap(mWidth, mHeight);
+        std::swap(mInitWidth, mInitHeight);
+        mFlingerSurfaceControl->updateDefaultBufferSize(mWidth, mHeight);
+    }
+
+    Rect displayRect(0, 0, mWidth, mHeight);
+    Rect layerStackRect(0, 0, mWidth, mHeight);
+
+    SurfaceComposerClient::Transaction t;
+    t.setDisplayProjection(mDisplayToken, orientation, layerStackRect, displayRect);
+    t.apply();
+}
+
+ui::Rotation BootAnimation::parseOrientationProperty() {
+    const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds();
+    if (displayIds.size() == 0) {
+        return ui::ROTATION_0;
+    }
+    const auto displayId = displayIds[0];
+    const auto syspropName = [displayId] {
+        std::stringstream ss;
+        ss << "ro.bootanim.set_orientation_" << displayId.value;
+        return ss.str();
+    }();
+    const auto syspropValue = android::base::GetProperty(syspropName, "ORIENTATION_0");
+    if (syspropValue == "ORIENTATION_90") {
+        return ui::ROTATION_90;
+    } else if (syspropValue == "ORIENTATION_180") {
+        return ui::ROTATION_180;
+    } else if (syspropValue == "ORIENTATION_270") {
+        return ui::ROTATION_270;
+    }
+    return ui::ROTATION_0;
+}
+
 void BootAnimation::projectSceneToWindow() {
     glViewport(0, 0, mWidth, mHeight);
     glScissor(0, 0, mWidth, mHeight);
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 8658205..8683b71 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -30,6 +30,8 @@
 #include <utils/Thread.h>
 #include <binder/IBinder.h>
 
+#include <ui/Rotation.h>
+
 #include <EGL/egl.h>
 #include <GLES2/gl2.h>
 
@@ -200,6 +202,8 @@
     ui::Size limitSurfaceSize(int width, int height) const;
     void resizeSurface(int newWidth, int newHeight);
     void projectSceneToWindow();
+    void rotateAwayFromNaturalOrientationIfNeeded();
+    ui::Rotation parseOrientationProperty();
 
     bool shouldStopPlayingPart(const Animation::Part& part, int fadedFramesCount,
                                int lastDisplayedProgress);
diff --git a/core/api/current.txt b/core/api/current.txt
index 0c8b010..a8dddcf 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -363,6 +363,7 @@
     field public static final int allowGameFpsOverride = 16844378; // 0x101065a
     field public static final int allowNativeHeapPointerTagging = 16844306; // 0x1010612
     field public static final int allowParallelSyncs = 16843570; // 0x1010332
+    field public static final int allowSharedIsolatedProcess;
     field public static final int allowSingleTap = 16843353; // 0x1010259
     field public static final int allowTaskReparenting = 16843268; // 0x1010204
     field public static final int allowUndo = 16843999; // 0x10104df
@@ -4612,6 +4613,7 @@
     method public boolean getLockTaskMode();
     method public int getSplashScreenStyle();
     method public boolean isPendingIntentBackgroundActivityLaunchAllowed();
+    method public boolean isShareIdentityEnabled();
     method public static android.app.ActivityOptions makeBasic();
     method public static android.app.ActivityOptions makeClipRevealAnimation(android.view.View, int, int, int, int);
     method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
@@ -7498,8 +7500,8 @@
     method public boolean getCameraDisabled(@Nullable android.content.ComponentName);
     method @Deprecated @Nullable public String getCertInstallerPackage(@NonNull android.content.ComponentName) throws java.lang.SecurityException;
     method @Deprecated @Nullable public java.util.Set<java.lang.String> getCrossProfileCalendarPackages(@NonNull android.content.ComponentName);
-    method public boolean getCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName);
-    method public boolean getCrossProfileContactsSearchDisabled(@NonNull android.content.ComponentName);
+    method @Deprecated public boolean getCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName);
+    method @Deprecated public boolean getCrossProfileContactsSearchDisabled(@NonNull android.content.ComponentName);
     method @NonNull public java.util.Set<java.lang.String> getCrossProfilePackages(@NonNull android.content.ComponentName);
     method @NonNull public java.util.List<java.lang.String> getCrossProfileWidgetProviders(@NonNull android.content.ComponentName);
     method public int getCurrentFailedPasswordAttempts();
@@ -7519,6 +7521,8 @@
     method public int getLockTaskFeatures(@NonNull android.content.ComponentName);
     method @NonNull public String[] getLockTaskPackages(@NonNull android.content.ComponentName);
     method @Nullable public CharSequence getLongSupportMessage(@NonNull android.content.ComponentName);
+    method @Nullable public android.app.admin.PackagePolicy getManagedProfileCallerIdAccessPolicy();
+    method @Nullable public android.app.admin.PackagePolicy getManagedProfileContactsAccessPolicy();
     method public long getManagedProfileMaximumTimeOff(@NonNull android.content.ComponentName);
     method public int getMaximumFailedPasswordsForWipe(@Nullable android.content.ComponentName);
     method public long getMaximumTimeToLock(@Nullable android.content.ComponentName);
@@ -7647,8 +7651,8 @@
     method public void setCommonCriteriaModeEnabled(@NonNull android.content.ComponentName, boolean);
     method public void setConfiguredNetworksLockdownState(@NonNull android.content.ComponentName, boolean);
     method @Deprecated public void setCrossProfileCalendarPackages(@NonNull android.content.ComponentName, @Nullable java.util.Set<java.lang.String>);
-    method public void setCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName, boolean);
-    method public void setCrossProfileContactsSearchDisabled(@NonNull android.content.ComponentName, boolean);
+    method @Deprecated public void setCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName, boolean);
+    method @Deprecated public void setCrossProfileContactsSearchDisabled(@NonNull android.content.ComponentName, boolean);
     method public void setCrossProfilePackages(@NonNull android.content.ComponentName, @NonNull java.util.Set<java.lang.String>);
     method public void setDefaultSmsApplication(@NonNull android.content.ComponentName, @NonNull String);
     method public void setDelegatedScopes(@NonNull android.content.ComponentName, @NonNull String, @NonNull java.util.List<java.lang.String>);
@@ -7667,6 +7671,8 @@
     method public void setLockTaskPackages(@NonNull android.content.ComponentName, @NonNull String[]) throws java.lang.SecurityException;
     method public void setLogoutEnabled(@NonNull android.content.ComponentName, boolean);
     method public void setLongSupportMessage(@NonNull android.content.ComponentName, @Nullable CharSequence);
+    method public void setManagedProfileCallerIdAccessPolicy(@Nullable android.app.admin.PackagePolicy);
+    method public void setManagedProfileContactsAccessPolicy(@Nullable android.app.admin.PackagePolicy);
     method public void setManagedProfileMaximumTimeOff(@NonNull android.content.ComponentName, long);
     method public void setMasterVolumeMuted(@NonNull android.content.ComponentName, boolean);
     method public void setMaximumFailedPasswordsForWipe(@NonNull android.content.ComponentName, int);
@@ -7978,6 +7984,19 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.NetworkEvent> CREATOR;
   }
 
+  public final class PackagePolicy implements android.os.Parcelable {
+    ctor public PackagePolicy(int);
+    ctor public PackagePolicy(int, @NonNull java.util.Set<java.lang.String>);
+    method public int describeContents();
+    method @NonNull public java.util.Set<java.lang.String> getPackageNames();
+    method public int getPolicyType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.PackagePolicy> CREATOR;
+    field public static final int PACKAGE_POLICY_ALLOWLIST = 3; // 0x3
+    field public static final int PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM = 2; // 0x2
+    field public static final int PACKAGE_POLICY_BLOCKLIST = 1; // 0x1
+  }
+
   public final class PreferentialNetworkServiceConfig implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public int[] getExcludedUids();
@@ -9878,6 +9897,7 @@
     method @Deprecated public abstract int getWallpaperDesiredMinimumHeight();
     method @Deprecated public abstract int getWallpaperDesiredMinimumWidth();
     method public abstract void grantUriPermission(String, android.net.Uri, int);
+    method public boolean isDeviceContext();
     method public abstract boolean isDeviceProtectedStorage();
     method public boolean isRestricted();
     method public boolean isUiContext();
@@ -9893,6 +9913,7 @@
     method public abstract android.database.sqlite.SQLiteDatabase openOrCreateDatabase(String, int, android.database.sqlite.SQLiteDatabase.CursorFactory, @Nullable android.database.DatabaseErrorHandler);
     method @Deprecated public abstract android.graphics.drawable.Drawable peekWallpaper();
     method public void registerComponentCallbacks(android.content.ComponentCallbacks);
+    method public void registerDeviceIdChangeListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
     method @Nullable public abstract android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter);
     method @Nullable public abstract android.content.Intent registerReceiver(@Nullable android.content.BroadcastReceiver, android.content.IntentFilter, int);
     method @Nullable public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler);
@@ -9932,6 +9953,7 @@
     method public abstract boolean stopService(android.content.Intent);
     method public abstract void unbindService(@NonNull android.content.ServiceConnection);
     method public void unregisterComponentCallbacks(android.content.ComponentCallbacks);
+    method public void unregisterDeviceIdChangeListener(@NonNull java.util.function.IntConsumer);
     method public abstract void unregisterReceiver(android.content.BroadcastReceiver);
     method public void updateServiceGroup(@NonNull android.content.ServiceConnection, int, int);
     field public static final String ACCESSIBILITY_SERVICE = "accessibility";
@@ -9953,6 +9975,7 @@
     field public static final int BIND_INCLUDE_CAPABILITIES = 4096; // 0x1000
     field public static final int BIND_NOT_FOREGROUND = 4; // 0x4
     field public static final int BIND_NOT_PERCEPTIBLE = 256; // 0x100
+    field public static final int BIND_SHARED_ISOLATED_PROCESS = 8192; // 0x2000
     field public static final int BIND_WAIVE_PRIORITY = 32; // 0x20
     field public static final String BIOMETRIC_SERVICE = "biometric";
     field public static final String BLOB_STORE_SERVICE = "blob_store";
@@ -11975,6 +11998,7 @@
     method @Deprecated public abstract void addPreferredActivity(@NonNull android.content.IntentFilter, int, @Nullable android.content.ComponentName[], @NonNull android.content.ComponentName);
     method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean addWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
     method public boolean canPackageQuery(@NonNull String, @NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method @NonNull public boolean[] canPackageQuery(@NonNull String, @NonNull String[]) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract boolean canRequestPackageInstalls();
     method public abstract String[] canonicalToCurrentPackageNames(@NonNull String[]);
     method @CheckResult public abstract int checkPermission(@NonNull String, @NonNull String);
@@ -12528,6 +12552,7 @@
     method public void dump(android.util.Printer, String);
     method public int getForegroundServiceType();
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ServiceInfo> CREATOR;
+    field public static final int FLAG_ALLOW_SHARED_ISOLATED_PROCESS = 16; // 0x10
     field public static final int FLAG_EXTERNAL_SERVICE = 4; // 0x4
     field public static final int FLAG_ISOLATED_PROCESS = 2; // 0x2
     field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
@@ -17958,8 +17983,10 @@
     method @Nullable public android.util.Range<java.lang.Long> getEstimatedCaptureLatencyRangeMillis(int, @NonNull android.util.Size, int);
     method @NonNull public <T> java.util.List<android.util.Size> getExtensionSupportedSizes(int, @NonNull Class<T>);
     method @NonNull public java.util.List<android.util.Size> getExtensionSupportedSizes(int, int);
+    method @NonNull public java.util.List<android.util.Size> getPostviewSupportedSizes(int, @NonNull android.util.Size, int);
     method @NonNull public java.util.List<java.lang.Integer> getSupportedExtensions();
     method public boolean isCaptureProcessProgressAvailable(int);
+    method public boolean isPostviewAvailable(int);
     field public static final int EXTENSION_AUTOMATIC = 0; // 0x0
     field @Deprecated public static final int EXTENSION_BEAUTY = 1; // 0x1
     field public static final int EXTENSION_BOKEH = 2; // 0x2
@@ -18631,7 +18658,9 @@
     method @NonNull public java.util.concurrent.Executor getExecutor();
     method public int getExtension();
     method @NonNull public java.util.List<android.hardware.camera2.params.OutputConfiguration> getOutputConfigurations();
+    method @Nullable public android.hardware.camera2.params.OutputConfiguration getPostviewOutputConfiguration();
     method @NonNull public android.hardware.camera2.CameraExtensionSession.StateCallback getStateCallback();
+    method public void setPostviewOutputConfiguration(@Nullable android.hardware.camera2.params.OutputConfiguration);
   }
 
   public final class Face {
@@ -24096,9 +24125,10 @@
   }
 
   public final class RouteListingPreference implements android.os.Parcelable {
-    ctor public RouteListingPreference(@NonNull java.util.List<android.media.RouteListingPreference.Item>);
+    ctor public RouteListingPreference(@NonNull java.util.List<android.media.RouteListingPreference.Item>, boolean);
     method public int describeContents();
     method @NonNull public java.util.List<android.media.RouteListingPreference.Item> getItems();
+    method public boolean getUseSystemOrdering();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteListingPreference> CREATOR;
   }
@@ -41008,6 +41038,32 @@
     field public static final int RTT_MODE_VCO = 3; // 0x3
   }
 
+  public final class CallAttributes implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.net.Uri getAddress();
+    method public int getCallCapabilities();
+    method public int getCallType();
+    method public int getDirection();
+    method @NonNull public CharSequence getDisplayName();
+    method @NonNull public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
+    method public void writeToParcel(@Nullable android.os.Parcel, int);
+    field public static final int AUDIO_CALL = 1; // 0x1
+    field @NonNull public static final android.os.Parcelable.Creator<android.telecom.CallAttributes> CREATOR;
+    field public static final int DIRECTION_INCOMING = 1; // 0x1
+    field public static final int DIRECTION_OUTGOING = 2; // 0x2
+    field public static final int SUPPORTS_SET_INACTIVE = 2; // 0x2
+    field public static final int SUPPORTS_STREAM = 4; // 0x4
+    field public static final int SUPPORTS_TRANSFER = 8; // 0x8
+    field public static final int VIDEO_CALL = 2; // 0x2
+  }
+
+  public static final class CallAttributes.Builder {
+    ctor public CallAttributes.Builder(@NonNull android.telecom.PhoneAccountHandle, int, @NonNull CharSequence, @NonNull android.net.Uri);
+    method @NonNull public android.telecom.CallAttributes build();
+    method @NonNull public android.telecom.CallAttributes.Builder setCallCapabilities(int);
+    method @NonNull public android.telecom.CallAttributes.Builder setCallType(int);
+  }
+
   public final class CallAudioState implements android.os.Parcelable {
     ctor public CallAudioState(boolean, int, int);
     method public static String audioRouteToString(int);
@@ -41026,6 +41082,15 @@
     field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
   }
 
+  public final class CallControl implements java.lang.AutoCloseable {
+    method public void close();
+    method public void disconnect(@NonNull android.telecom.DisconnectCause, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>);
+    method @NonNull public android.os.ParcelUuid getCallId();
+    method public void rejectCall(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>);
+    method public void setActive(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>);
+    method public void setInactive(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>);
+  }
+
   public final class CallEndpoint implements android.os.Parcelable {
     ctor public CallEndpoint(@NonNull CharSequence, int, @NonNull android.os.ParcelUuid);
     method public int describeContents();
@@ -41056,6 +41121,30 @@
     field public static final int ERROR_UNSPECIFIED = 4; // 0x4
   }
 
+  public interface CallEventCallback {
+    method public void onAnswer(int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method public void onCallAudioStateChanged(@NonNull android.telecom.CallAudioState);
+    method public void onDisconnect(@NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method public void onReject(@NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method public void onSetActive(@NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method public void onSetInactive(@NonNull java.util.function.Consumer<java.lang.Boolean>);
+  }
+
+  public final class CallException extends java.lang.RuntimeException implements android.os.Parcelable {
+    ctor public CallException(@Nullable String);
+    ctor public CallException(@Nullable String, int);
+    method public int describeContents();
+    method public int getCode();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CODE_CALL_CANNOT_BE_SET_TO_ACTIVE = 4; // 0x4
+    field public static final int CODE_CALL_IS_NOT_BEING_TRACKED = 3; // 0x3
+    field public static final int CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME = 5; // 0x5
+    field public static final int CODE_CANNOT_HOLD_CURRENT_ACTIVE_CALL = 2; // 0x2
+    field public static final int CODE_ERROR_UNKNOWN = 1; // 0x1
+    field public static final int CODE_OPERATION_TIMED_OUT = 6; // 0x6
+    field @NonNull public static final android.os.Parcelable.Creator<android.telecom.CallException> CREATOR;
+  }
+
   public abstract class CallRedirectionService extends android.app.Service {
     ctor public CallRedirectionService();
     method public final void cancelCall();
@@ -41565,6 +41654,7 @@
     field public static final int CAPABILITY_RTT = 4096; // 0x1000
     field public static final int CAPABILITY_SELF_MANAGED = 2048; // 0x800
     field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+    field public static final int CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS = 262144; // 0x40000
     field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
     field public static final int CAPABILITY_SUPPORTS_VOICE_CALLING_INDICATIONS = 65536; // 0x10000
     field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
@@ -41757,6 +41847,7 @@
     method public void acceptHandover(android.net.Uri, int, android.telecom.PhoneAccountHandle);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall(int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_OWN_CALLS) public void addCall(@NonNull android.telecom.CallAttributes, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telecom.CallControl,android.telecom.CallException>, @NonNull android.telecom.CallEventCallback);
     method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
     method public void addNewIncomingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void cancelMissedCallsNotification();
@@ -42584,9 +42675,16 @@
   public static final class CarrierConfigManager.ImsSms {
     field public static final String KEY_PREFIX = "imssms.";
     field public static final String KEY_SMS_CSFB_RETRY_ON_FAILURE_BOOL = "imssms.sms_csfb_retry_on_failure_bool";
+    field public static final String KEY_SMS_MAX_RETRY_COUNT_INT = "imssms.sms_max_retry_count_int";
+    field public static final String KEY_SMS_MAX_RETRY_COUNT_OVER_IMS_INT = "imssms.sms_max_retry_count_over_ims_int";
     field public static final String KEY_SMS_OVER_IMS_FORMAT_INT = "imssms.sms_over_ims_format_int";
+    field public static final String KEY_SMS_OVER_IMS_SEND_RETRY_DELAY_MILLIS_INT = "imssms.sms_rover_ims_send_retry_delay_millis_int";
     field public static final String KEY_SMS_OVER_IMS_SUPPORTED_BOOL = "imssms.sms_over_ims_supported_bool";
     field public static final String KEY_SMS_OVER_IMS_SUPPORTED_RATS_INT_ARRAY = "imssms.sms_over_ims_supported_rats_int_array";
+    field public static final String KEY_SMS_RP_CAUSE_VALUES_TO_FALLBACK_INT_ARRAY = "imssms.sms_rp_cause_values_to_fallback_int_array";
+    field public static final String KEY_SMS_RP_CAUSE_VALUES_TO_RETRY_OVER_IMS_INT_ARRAY = "imssms.sms_rp_cause_values_to_retry_over_ims_int_array";
+    field public static final String KEY_SMS_TR1_TIMER_MILLIS_INT = "imssms.sms_tr1_timer_millis_int";
+    field public static final String KEY_SMS_TR2_TIMER_MILLIS_INT = "imssms.sms_tr2_timer_millis_int";
     field public static final int SMS_FORMAT_3GPP = 0; // 0x0
     field public static final int SMS_FORMAT_3GPP2 = 1; // 0x1
   }
@@ -44073,6 +44171,29 @@
     field public static final int RESULT_SMS_SEND_RETRY_FAILED = 30; // 0x1e
     field public static final int RESULT_SYSTEM_ERROR = 15; // 0xf
     field public static final int RESULT_UNEXPECTED_EVENT_STOP_SENDING = 28; // 0x1c
+    field public static final int SMS_RP_CAUSE_CALL_BARRING = 10; // 0xa
+    field public static final int SMS_RP_CAUSE_CONGESTION = 42; // 0x2a
+    field public static final int SMS_RP_CAUSE_DESTINATION_OUT_OF_ORDER = 27; // 0x1b
+    field public static final int SMS_RP_CAUSE_FACILITY_NOT_IMPLEMENTED = 69; // 0x45
+    field public static final int SMS_RP_CAUSE_FACILITY_NOT_SUBSCRIBED = 50; // 0x32
+    field public static final int SMS_RP_CAUSE_FACILITY_REJECTED = 29; // 0x1d
+    field public static final int SMS_RP_CAUSE_INFORMATION_ELEMENT_NON_EXISTENT = 99; // 0x63
+    field public static final int SMS_RP_CAUSE_INTERWORKING_UNSPECIFIED = 127; // 0x7f
+    field public static final int SMS_RP_CAUSE_INVALID_MANDATORY_INFORMATION = 96; // 0x60
+    field public static final int SMS_RP_CAUSE_INVALID_MESSAGE_REFERENCE_VALUE = 81; // 0x51
+    field public static final int SMS_RP_CAUSE_MESSAGE_INCOMPATIBLE_WITH_PROTOCOL_STATE = 98; // 0x62
+    field public static final int SMS_RP_CAUSE_MESSAGE_TYPE_NON_EXISTENT = 97; // 0x61
+    field public static final int SMS_RP_CAUSE_NETWORK_OUT_OF_ORDER = 38; // 0x26
+    field public static final int SMS_RP_CAUSE_OPERATOR_DETERMINED_BARRING = 8; // 0x8
+    field public static final int SMS_RP_CAUSE_PROTOCOL_ERROR = 111; // 0x6f
+    field public static final int SMS_RP_CAUSE_RESERVED = 11; // 0xb
+    field public static final int SMS_RP_CAUSE_RESOURCES_UNAVAILABLE = 47; // 0x2f
+    field public static final int SMS_RP_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE = 95; // 0x5f
+    field public static final int SMS_RP_CAUSE_SHORT_MESSAGE_TRANSFER_REJECTED = 21; // 0x15
+    field public static final int SMS_RP_CAUSE_TEMPORARY_FAILURE = 41; // 0x29
+    field public static final int SMS_RP_CAUSE_UNALLOCATED_NUMBER = 1; // 0x1
+    field public static final int SMS_RP_CAUSE_UNIDENTIFIED_SUBSCRIBER = 28; // 0x1c
+    field public static final int SMS_RP_CAUSE_UNKNOWN_SUBSCRIBER = 30; // 0x1e
     field public static final int STATUS_ON_ICC_FREE = 0; // 0x0
     field public static final int STATUS_ON_ICC_READ = 1; // 0x1
     field public static final int STATUS_ON_ICC_SENT = 5; // 0x5
@@ -53747,6 +53868,7 @@
 package android.view.autofill {
 
   public final class AutofillId implements android.os.Parcelable {
+    method @NonNull public static android.view.autofill.AutofillId create(@NonNull android.view.View, int);
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.view.autofill.AutofillId> CREATOR;
@@ -54198,7 +54320,7 @@
   }
 
   public abstract class HandwritingGesture {
-    method @Nullable public String getFallbackText();
+    method @Nullable public final String getFallbackText();
     field public static final int GESTURE_TYPE_DELETE = 4; // 0x4
     field public static final int GESTURE_TYPE_DELETE_RANGE = 64; // 0x40
     field public static final int GESTURE_TYPE_INSERT = 2; // 0x2
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 314fd03..adbbe61 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -63,6 +63,8 @@
   public class DevicePolicyManager {
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void acknowledgeNewUserDisclaimer();
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getLogoutUser();
+    method public boolean hasManagedProfileCallerIdAccess(@NonNull android.os.UserHandle, @NonNull String);
+    method public boolean hasManagedProfileContactsAccess(@NonNull android.os.UserHandle, @NonNull String);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public int logoutUser();
     field public static final String ACTION_SHOW_NEW_USER_DISCLAIMER = "android.app.action.SHOW_NEW_USER_DISCLAIMER";
   }
@@ -430,6 +432,7 @@
   }
 
   public static final class Settings.Config extends android.provider.Settings.NameValueTable {
+    method @RequiresPermission(android.Manifest.permission.MONITOR_DEVICE_CONFIG_ACCESS) public static void clearMonitorCallback(@NonNull android.content.ContentResolver);
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean deleteString(@NonNull String, @NonNull String);
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getString(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static java.util.Map<java.lang.String,java.lang.String> getStrings(@NonNull String, @NonNull java.util.List<java.lang.String>);
@@ -437,6 +440,7 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean putString(@NonNull String, @NonNull String, @Nullable String, boolean);
     method public static void registerContentObserver(@Nullable String, boolean, @NonNull android.database.ContentObserver);
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String);
+    method @RequiresPermission(android.Manifest.permission.MONITOR_DEVICE_CONFIG_ACCESS) public static void setMonitorCallback(@NonNull android.content.ContentResolver, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.MonitorCallback);
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setStrings(@NonNull String, @NonNull java.util.Map<java.lang.String,java.lang.String>) throws android.provider.DeviceConfig.BadConfigException;
     method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void setSyncDisabledMode(int);
     method public static void unregisterContentObserver(@NonNull android.database.ContentObserver);
@@ -474,6 +478,10 @@
 
 package android.telephony {
 
+  public class CarrierConfigManager {
+    field public static final String KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT = "min_udp_port_4500_nat_timeout_sec_int";
+  }
+
   public abstract class CellSignalStrength {
     method public static int getNumSignalStrengthLevels();
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 46f51c7..99aa01f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -212,6 +212,7 @@
     field public static final String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS";
     field public static final String MODIFY_QUIET_MODE = "android.permission.MODIFY_QUIET_MODE";
     field public static final String MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE = "android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE";
+    field public static final String MONITOR_DEVICE_CONFIG_ACCESS = "android.permission.MONITOR_DEVICE_CONFIG_ACCESS";
     field public static final String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE";
     field public static final String NETWORK_AIRPLANE_MODE = "android.permission.NETWORK_AIRPLANE_MODE";
     field public static final String NETWORK_CARRIER_PROVISIONING = "android.permission.NETWORK_CARRIER_PROVISIONING";
@@ -352,7 +353,6 @@
     field public static final String USE_COLORIZED_NOTIFICATIONS = "android.permission.USE_COLORIZED_NOTIFICATIONS";
     field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
     field public static final String UWB_PRIVILEGED = "android.permission.UWB_PRIVILEGED";
-    field public static final String WAKEUP_SURFACE_FLINGER = "android.permission.WAKEUP_SURFACE_FLINGER";
     field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS";
     field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS";
     field public static final String WIFI_ACCESS_COEX_UNSAFE_CHANNELS = "android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS";
@@ -457,6 +457,7 @@
     field public static final int config_systemUi = 17039418; // 0x104003a
     field public static final int config_systemUiIntelligence = 17039410; // 0x1040032
     field public static final int config_systemVisualIntelligence = 17039415; // 0x1040037
+    field public static final int config_systemWearHealthService;
     field public static final int config_systemWellbeing = 17039408; // 0x1040030
     field public static final int config_systemWifiCoexManager = 17039407; // 0x104002f
     field public static final int safety_protection_display_text = 17039425; // 0x1040041
@@ -3623,7 +3624,6 @@
   public abstract class PackageManager {
     method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void addOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
     method public abstract boolean arePermissionsIndividuallyControlled();
-    method @NonNull public boolean[] canPackageQuery(@NonNull String, @NonNull String[]) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(@NonNull String);
     method @NonNull @RequiresPermission("android.permission.GET_APP_METADATA") public android.os.PersistableBundle getAppMetadata(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -5817,6 +5817,8 @@
     field public static final int DATA_STATUS_DISABLED_CONTAMINANT = 4; // 0x4
     field public static final int DATA_STATUS_DISABLED_DEBUG = 32; // 0x20
     field public static final int DATA_STATUS_DISABLED_DOCK = 8; // 0x8
+    field public static final int DATA_STATUS_DISABLED_DOCK_DEVICE_MODE = 128; // 0x80
+    field public static final int DATA_STATUS_DISABLED_DOCK_HOST_MODE = 64; // 0x40
     field public static final int DATA_STATUS_DISABLED_FORCE = 16; // 0x10
     field public static final int DATA_STATUS_DISABLED_OVERHEAT = 2; // 0x2
     field public static final int DATA_STATUS_ENABLED = 1; // 0x1
@@ -6592,6 +6594,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages();
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method public boolean isAudioServerRunning();
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean isBluetoothVariableLatencyEnabled();
     method public boolean isHdmiSystemAudioSupported();
     method @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public boolean isPstnCallAudioInterceptable();
     method @RequiresPermission(android.Manifest.permission.ACCESS_ULTRASOUND) public boolean isUltrasoundSupported();
@@ -6610,6 +6613,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setActiveAssistantServiceUids(@NonNull int[]);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) long);
     method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setBluetoothVariableLatencyEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForCapturePreset(int, @NonNull android.media.AudioDeviceAttributes);
@@ -6617,6 +6621,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDevicesForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull java.util.List<android.media.AudioDeviceAttributes>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean supportsBluetoothVariableLatency();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterMuteAwaitConnectionCallback(@NonNull android.media.AudioManager.MuteAwaitConnectionCallback);
@@ -9499,7 +9504,8 @@
     method @Deprecated @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.SignalPollResult signalPoll(@NonNull String);
     method public boolean startPnoScan(@NonNull String, @NonNull android.net.wifi.nl80211.PnoSettings, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.PnoScanRequestCallback);
     method @Deprecated public boolean startScan(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>);
-    method public boolean startScan(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>, @Nullable android.os.Bundle);
+    method @Deprecated public boolean startScan(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>, @Nullable android.os.Bundle);
+    method public int startScan2(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>, @Nullable android.os.Bundle);
     method public boolean stopPnoScan(@NonNull String);
     method public boolean tearDownClientInterface(@NonNull String);
     method public boolean tearDownInterfaces();
@@ -9533,7 +9539,8 @@
   }
 
   public static interface WifiNl80211Manager.ScanEventCallback {
-    method public void onScanFailed();
+    method @Deprecated public void onScanFailed();
+    method public default void onScanFailed(int);
     method public void onScanResultReady();
   }
 
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 82cc3fd..5be98bf 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3232,7 +3232,7 @@
 package android.view.inputmethod {
 
   public abstract class HandwritingGesture {
-    method public int getGestureType();
+    method public final int getGestureType();
   }
 
   public final class InlineSuggestion implements android.os.Parcelable {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d1772e37..da88f4b 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1015,6 +1015,8 @@
 
     private ComponentCallbacksController mCallbacksController;
 
+    @Nullable private IVoiceInteractionManagerService mVoiceInteractionManagerService;
+
     private final WindowControllerCallback mWindowControllerCallback =
             new WindowControllerCallback() {
         /**
@@ -1624,18 +1626,17 @@
 
     private void notifyVoiceInteractionManagerServiceActivityEvent(
             @VoiceInteractionSession.VoiceInteractionActivityEventType int type) {
-
-        final IVoiceInteractionManagerService service =
-                IVoiceInteractionManagerService.Stub.asInterface(
-                        ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
-        if (service == null) {
-            Log.w(TAG, "notifyVoiceInteractionManagerServiceActivityEvent: Can not get "
-                    + "VoiceInteractionManagerService");
-            return;
+        if (mVoiceInteractionManagerService == null) {
+            mVoiceInteractionManagerService = IVoiceInteractionManagerService.Stub.asInterface(
+                    ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
+            if (mVoiceInteractionManagerService == null) {
+                Log.w(TAG, "notifyVoiceInteractionManagerServiceActivityEvent: Can not get "
+                        + "VoiceInteractionManagerService");
+                return;
+            }
         }
-
         try {
-            service.notifyActivityEventChanged(mToken, type);
+            mVoiceInteractionManagerService.notifyActivityEventChanged(mToken, type);
         } catch (RemoteException e) {
             // Empty
         }
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index c51e8ae..6826b67 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -907,4 +907,19 @@
      * Return all client package names of a service.
      */
     public abstract ArraySet<String> getClientPackages(String servicePackageName);
+
+    /**
+     * Retrieve an IUnsafeIntentStrictModeCallback matching the given callingUid.
+     * Returns null no match is found.
+     * @param callingPid The PID mapped with the callback.
+     * @return The callback, if it exists.
+     */
+    public abstract IUnsafeIntentStrictModeCallback getRegisteredStrictModeCallback(
+            int callingPid);
+
+    /**
+     * Unregisters an IUnsafeIntentStrictModeCallback matching the given callingUid.
+     * @param callingPid The PID mapped with the callback.
+     */
+    public abstract void unregisterStrictModeCallback(int callingPid);
 }
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 1b92312..2214c8e 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1499,13 +1499,13 @@
      * Returns whether the launching app has opted-in to sharing its identity with the launched
      * activity.
      *
+     * @return {@code true} if the launching app has opted-in to sharing its identity
+     *
      * @see #setShareIdentityEnabled(boolean)
      * @see Activity#getLaunchedFromUid()
      * @see Activity#getLaunchedFromPackage()
-     *
-     * @hide
      */
-    public boolean getShareIdentity() {
+    public boolean isShareIdentityEnabled() {
         return mShareIdentity;
     }
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 39f7153..a832b9a 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -21,12 +21,14 @@
 import static android.os.StrictMode.vmIncorrectContextUseEnabled;
 import static android.view.WindowManager.LayoutParams.WindowType;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UiContext;
 import android.companion.virtual.VirtualDevice;
 import android.companion.virtual.VirtualDeviceManager;
+import android.companion.virtual.VirtualDeviceParams;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.AttributionSource;
 import android.content.AutofillOptions;
@@ -121,6 +123,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.Executor;
+import java.util.function.IntConsumer;
 
 class ReceiverRestrictedContext extends ContextWrapper {
     @UnsupportedAppUsage
@@ -257,6 +260,13 @@
     /** @see Context#isConfigurationContext() */
     private boolean mIsConfigurationBasedContext;
 
+    /**
+     *  Indicates that this context was created with an explicit device ID association via
+     *  Context#createDeviceContext and under no circumstances will it ever change, even if
+     *  this context is not associated with a display id, or if the associated display id changes.
+     */
+    private boolean mIsExplicitDeviceId = false;
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final int mFlags;
 
@@ -372,6 +382,24 @@
     @ServiceInitializationState
     final int[] mServiceInitializationStateArray = new int[mServiceCache.length];
 
+    private final Object mDeviceIdListenerLock = new Object();
+    /**
+     * List of listeners for deviceId changes and their associated Executor.
+     * List is lazy-initialized on first registration
+     */
+    @GuardedBy("mDeviceIdListenerLock")
+    @Nullable
+    private ArrayList<DeviceIdChangeListenerDelegate> mDeviceIdChangeListeners;
+
+    private static class DeviceIdChangeListenerDelegate {
+        final @NonNull IntConsumer mListener;
+        final @NonNull Executor mExecutor;
+        DeviceIdChangeListenerDelegate(IntConsumer listener, Executor executor) {
+            mListener = listener;
+            mExecutor = executor;
+        }
+    }
+
     @UnsupportedAppUsage
     static ContextImpl getImpl(Context context) {
         Context nextContext;
@@ -573,7 +601,8 @@
                             && !getSystemService(UserManager.class)
                                     .isUserUnlockingOrUnlocked(UserHandle.myUserId())) {
                         throw new IllegalStateException("SharedPreferences in credential encrypted "
-                                + "storage are not available until after user is unlocked");
+                                + "storage are not available until after user (id "
+                                + UserHandle.myUserId() + ") is unlocked");
                     }
                 }
                 sp = new SharedPreferencesImpl(file, mode);
@@ -2699,15 +2728,7 @@
 
     @Override
     public @NonNull Context createDeviceContext(int deviceId) {
-        boolean validDeviceId = deviceId == VirtualDeviceManager.DEVICE_ID_DEFAULT;
-        if (deviceId > VirtualDeviceManager.DEVICE_ID_DEFAULT) {
-            VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class);
-            if (vdm != null) {
-                List<VirtualDevice> virtualDevices = vdm.getVirtualDevices();
-                validDeviceId = virtualDevices.stream().anyMatch(d -> d.getDeviceId() == deviceId);
-            }
-        }
-        if (!validDeviceId) {
+        if (!isValidDeviceId(deviceId)) {
             throw new IllegalArgumentException(
                     "Not a valid ID of the default device or any virtual device: " + deviceId);
         }
@@ -2718,9 +2739,35 @@
                 mSplitName, mToken, mUser, mFlags, mClassLoader, null);
 
         context.mDeviceId = deviceId;
+        context.mIsExplicitDeviceId = true;
         return context;
     }
 
+    /**
+     * Checks whether the passed {@code deviceId} is valid or not.
+     * {@link VirtualDeviceManager#DEVICE_ID_DEFAULT} is valid as it is the ID of the default
+     * device when no additional virtual devices exist. If {@code deviceId} is the id of
+     * a virtual device, it should correspond to a virtual device created by
+     * {@link VirtualDeviceManager#createVirtualDevice(int, VirtualDeviceParams)}.
+     */
+    private boolean isValidDeviceId(int deviceId) {
+        if (deviceId == VirtualDeviceManager.DEVICE_ID_DEFAULT) {
+            return true;
+        }
+        if (deviceId > VirtualDeviceManager.DEVICE_ID_DEFAULT) {
+            VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class);
+            if (vdm != null) {
+                List<VirtualDevice> virtualDevices = vdm.getVirtualDevices();
+                for (int i = 0; i < virtualDevices.size(); i++) {
+                    if (virtualDevices.get(i).getDeviceId() == deviceId) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
     @NonNull
     @Override
     public WindowContext createWindowContext(@WindowType int type,
@@ -2965,6 +3012,21 @@
         if (mContextType == CONTEXT_TYPE_NON_UI) {
             mContextType = CONTEXT_TYPE_DISPLAY_CONTEXT;
         }
+        // TODO(b/253201821): Update deviceId when display is updated.
+    }
+
+    @Override
+    public void updateDeviceId(int updatedDeviceId) {
+        if (!isValidDeviceId(updatedDeviceId)) {
+            throw new IllegalArgumentException(
+                    "Not a valid ID of the default device or any virtual device: " + mDeviceId);
+        }
+        if (mIsExplicitDeviceId) {
+            throw new UnsupportedOperationException(
+                    "Cannot update device ID on a Context created with createDeviceContext()");
+        }
+        mDeviceId = updatedDeviceId;
+        notifyOnDeviceChangedListeners(updatedDeviceId);
     }
 
     @Override
@@ -2973,6 +3035,69 @@
     }
 
     @Override
+    public boolean isDeviceContext() {
+        return mIsExplicitDeviceId || isAssociatedWithDisplay();
+    }
+
+    @Override
+    public void registerDeviceIdChangeListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull IntConsumer listener) {
+        Objects.requireNonNull(executor, "executor cannot be null");
+        Objects.requireNonNull(listener, "listener cannot be null");
+
+        synchronized (mDeviceIdListenerLock) {
+            if (getDeviceIdListener(listener) != null) {
+                throw new IllegalArgumentException(
+                        "attempt to call registerDeviceIdChangeListener() "
+                                + "on a previously registered listener");
+            }
+            // lazy initialization
+            if (mDeviceIdChangeListeners == null) {
+                mDeviceIdChangeListeners = new ArrayList<>();
+            }
+            mDeviceIdChangeListeners.add(new DeviceIdChangeListenerDelegate(listener, executor));
+        }
+    }
+
+    @Override
+    public void unregisterDeviceIdChangeListener(@NonNull IntConsumer listener) {
+        Objects.requireNonNull(listener, "listener cannot be null");
+        synchronized (mDeviceIdListenerLock) {
+            DeviceIdChangeListenerDelegate listenerToRemove = getDeviceIdListener(listener);
+            if (listenerToRemove != null) {
+                mDeviceIdChangeListeners.remove(listenerToRemove);
+            }
+        }
+    }
+
+    @GuardedBy("mDeviceIdListenerLock")
+    @Nullable
+    private DeviceIdChangeListenerDelegate getDeviceIdListener(
+            @Nullable IntConsumer listener) {
+        if (mDeviceIdChangeListeners == null) {
+            return null;
+        }
+        for (int i = 0; i < mDeviceIdChangeListeners.size(); i++) {
+            DeviceIdChangeListenerDelegate delegate = mDeviceIdChangeListeners.get(i);
+            if (delegate.mListener == listener) {
+                return delegate;
+            }
+        }
+        return null;
+    }
+
+    private void notifyOnDeviceChangedListeners(int deviceId) {
+        synchronized (mDeviceIdListenerLock) {
+            if (mDeviceIdChangeListeners != null) {
+                for (DeviceIdChangeListenerDelegate delegate : mDeviceIdChangeListeners) {
+                    delegate.mExecutor.execute(() ->
+                            delegate.mListener.accept(deviceId));
+                }
+            }
+        }
+    }
+
+    @Override
     public DisplayAdjustments getDisplayAdjustments(int displayId) {
         return mResources.getDisplayAdjustments();
     }
@@ -3193,7 +3318,6 @@
             @Nullable String splitName, @Nullable IBinder token, @Nullable UserHandle user,
             int flags, @Nullable ClassLoader classLoader, @Nullable String overrideOpPackageName) {
         mOuterContext = this;
-
         // If creator didn't specify which storage to use, use the default
         // location for application.
         if ((flags & (Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE
@@ -3227,6 +3351,8 @@
             opPackageName = container.mOpPackageName;
             setResources(container.mResources);
             mDisplay = container.mDisplay;
+            mDeviceId = container.mDeviceId;
+            mIsExplicitDeviceId = container.mIsExplicitDeviceId;
             mForceDisplayOverrideInResources = container.mForceDisplayOverrideInResources;
             mIsConfigurationBasedContext = container.mIsConfigurationBasedContext;
             mContextType = container.mContextType;
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index b120ea7..6b6b820 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -331,6 +331,7 @@
     @UnsupportedAppUsage
     void handleApplicationStrictModeViolation(in IBinder app, int penaltyMask,
             in StrictMode.ViolationInfo crashInfo);
+    void registerStrictModeCallback(in IBinder binder);
     boolean isTopActivityImmersive();
     void crashApplicationWithType(int uid, int initialPid, in String packageName, int userId,
             in String message, boolean force, int exceptionTypeId);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 91add27..f20503c 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -249,11 +249,6 @@
     /** Returns an interface enabling the management of window organizers. */
     IWindowOrganizerController getWindowOrganizerController();
 
-    /**
-     * Sets whether we are currently in an interactive split screen resize operation where we
-     * are changing the docked stack size.
-     */
-    void setSplitScreenResizing(boolean resizing);
     boolean supportsLocalVoiceInteraction();
 
     // Get device configuration
diff --git a/core/java/android/app/IUnsafeIntentStrictModeCallback.aidl b/core/java/android/app/IUnsafeIntentStrictModeCallback.aidl
new file mode 100644
index 0000000..e2b3bb1
--- /dev/null
+++ b/core/java/android/app/IUnsafeIntentStrictModeCallback.aidl
@@ -0,0 +1,28 @@
+/*
+* Copyright 2022, The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package android.app;
+
+import android.content.Intent;
+
+/**
+ * Callback to find out when a strict mode violation occurs.
+ * {@hide}
+ */
+oneway interface IUnsafeIntentStrictModeCallback
+{
+    void onImplicitIntentMatchedInternalComponent(in Intent intent);
+}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 84b404d..e655209 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1109,7 +1109,7 @@
     }
 
     /**
-     * Like {@link #getFastDrawable(int)}, but the returned Drawable has a number
+     * Like {@link #getDrawable(int)}, but the returned Drawable has a number
      * of limitations to reduce its overhead as much as possible. It will
      * never scale the wallpaper (only centering it if the requested bounds
      * do match the bitmap bounds, which should not be typical), doesn't
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 8ebd329..e729e7d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9878,11 +9878,20 @@
      * <p>
      * The calling device admin must be a profile owner. If it is not, a security exception will be
      * thrown.
+     * <p>
+     * Starting with {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, calling this function
+     * is similar to calling {@link #setManagedProfileCallerIdAccessPolicy(PackagePolicy)}
+     * with a {@link PackagePolicy#PACKAGE_POLICY_BLOCKLIST} policy type when {@code disabled} is
+     * false or a {@link PackagePolicy#PACKAGE_POLICY_ALLOWLIST} policy type when
+     * {@code disabled} is true.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param disabled If true caller-Id information in the managed profile is not displayed.
      * @throws SecurityException if {@code admin} is not a profile owner.
+     * @deprecated starting with {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, use
+     * {@link #setManagedProfileCallerIdAccessPolicy(PackagePolicy)} instead
      */
+    @Deprecated
     public void setCrossProfileCallerIdDisabled(@NonNull ComponentName admin, boolean disabled) {
         throwIfParentInstance("setCrossProfileCallerIdDisabled");
         if (mService != null) {
@@ -9900,10 +9909,19 @@
      * <p>
      * The calling device admin must be a profile owner. If it is not, a security exception will be
      * thrown.
+     * <p>
+     * Starting with {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+     * this will return true when
+     * {@link #setManagedProfileCallerIdAccessPolicy(PackagePolicy)}
+     * has been set with a non-null policy whose policy type is NOT
+     * {@link PackagePolicy#PACKAGE_POLICY_BLOCKLIST}
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @throws SecurityException if {@code admin} is not a profile owner.
+     * @deprecated starting with {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, use
+     * {@link #getManagedProfileCallerIdAccessPolicy()} instead
      */
+    @Deprecated
     public boolean getCrossProfileCallerIdDisabled(@NonNull ComponentName admin) {
         throwIfParentInstance("getCrossProfileCallerIdDisabled");
         if (mService != null) {
@@ -9917,11 +9935,20 @@
     }
 
     /**
-     * Determine whether or not caller-Id information has been disabled.
+     * Called by the system to determine whether or not caller-Id information has been disabled.
+     * <p>
+     * Starting with {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+     * this will return true when
+     * {@link #setManagedProfileCallerIdAccessPolicy(PackagePolicy)}
+     * has been set with a non-null policy whose policy type is NOT
+     * {@link PackagePolicy#PACKAGE_POLICY_BLOCKLIST}
      *
      * @param userHandle The user for whom to check the caller-id permission
+     * @deprecated use {@link #hasManagedProfileCallerIdAccess(UserHandle, String)} and provide the
+     * package name requesting access
      * @hide
      */
+    @Deprecated
     public boolean getCrossProfileCallerIdDisabled(UserHandle userHandle) {
         if (mService != null) {
             try {
@@ -9934,16 +9961,174 @@
     }
 
     /**
+     * Called by a profile owner of a managed profile to set the packages that are allowed to
+     * lookup contacts in the managed profile based on caller id information.
+     * <p>
+     * For example, the policy determines if a dialer app in the parent profile resolving
+     * an incoming call can search the caller id data, such as phone number,
+     * of managed contacts and return managed contacts that match.
+     * <p>
+     * The calling device admin must be a profile owner of a managed profile.
+     * If it is not, a {@link SecurityException} will be thrown.
+     * <p>
+     * A {@link PackagePolicy#PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM} policy type
+     * allows access from the OEM default packages for the Sms, Dialer and Contact roles,
+     * in addition to the packages specified in {@link PackagePolicy#getPackageNames()}
+     *
+     * @param policy the policy to set, setting this value to {@code null} will allow
+     *               all packages
+     * @throws SecurityException if caller is not a profile owner of a managed profile
+     */
+    public void setManagedProfileCallerIdAccessPolicy(@Nullable PackagePolicy policy) {
+        throwIfParentInstance("setManagedProfileCallerIdAccessPolicy");
+        if (mService != null) {
+            try {
+                mService.setManagedProfileCallerIdAccessPolicy(policy);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Called by a profile owner of a managed profile to retrieve the caller id policy.
+     * <p>
+     * The calling device admin must be a profile owner of a managed profile.
+     * If it is not, a {@link SecurityException} will be thrown.
+     *
+     * @throws SecurityException if caller is not a profile owner of a managed profile.
+     * @return the current caller id policy
+     */
+    public @Nullable PackagePolicy getManagedProfileCallerIdAccessPolicy() {
+        throwIfParentInstance("getManagedProfileCallerIdAccessPolicy");
+        if (mService != null) {
+            try {
+                return mService.getManagedProfileCallerIdAccessPolicy();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Determine whether the given package is allowed to query the requested user to
+     * populate caller id information
+     *
+     * @param userHandle The user for whom to check the contacts search permission
+     * @param packageName the name of the package requesting access
+     * @return true if package should be granted access, false otherwise
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public boolean hasManagedProfileCallerIdAccess(@NonNull UserHandle userHandle,
+            @NonNull String packageName) {
+        if (mService == null) {
+            return true;
+        }
+        try {
+            return mService.hasManagedProfileCallerIdAccess(userHandle.getIdentifier(),
+                    packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Called by a profile owner of a managed profile to set the packages that are allowed
+     * access to the managed profile contacts from the parent user.
+     * <p>
+     * For example, the system will enforce the provided policy and determine
+     * if contacts in the managed profile are shown when queried by an application
+     * in the parent user.
+     * <p>
+     * The calling device admin must be a profile owner of a managed profile.
+     * If it is not, a {@link SecurityException} will be thrown.
+     * <p>
+     * A {@link PackagePolicy#PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM} policy type
+     * allows access from the OEM default packages for the Sms, Dialer and Contact roles,
+     * in addition to the packages specified in {@link PackagePolicy#getPackageNames()}
+     *
+     * @param policy the policy to set, setting this value to {@code null} will allow
+     *               all packages
+     * @throws SecurityException if caller is not a profile owner of a managed profile
+     */
+    public void setManagedProfileContactsAccessPolicy(@Nullable PackagePolicy policy) {
+        throwIfParentInstance("setManagedProfileContactsAccessPolicy");
+        if (mService != null) {
+            try {
+                mService.setManagedProfileContactsAccessPolicy(policy);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Called by a profile owner of a managed profile to determine the current policy applied
+     * to managed profile contacts.
+     * <p>
+     * The calling device admin must be a profile owner of a managed profile.
+     * If it is not, a {@link SecurityException} will be thrown.
+     *
+     * @throws SecurityException if caller is not a profile owner of a managed profile.
+     * @return the current contacts search policy
+     */
+    public @Nullable PackagePolicy getManagedProfileContactsAccessPolicy() {
+        throwIfParentInstance("getManagedProfileContactsAccessPolicy");
+        if (mService == null) {
+            return null;
+        }
+        try {
+            return mService.getManagedProfileContactsAccessPolicy();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Determine whether requesting package has ability to access contacts of the requested user
+     *
+     * @param userHandle The user for whom to check the contacts search permission
+     * @param packageName packageName requesting access to contact search
+     * @return true when package is allowed access, false otherwise
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public boolean hasManagedProfileContactsAccess(@NonNull UserHandle userHandle,
+            @NonNull String packageName) {
+        if (mService != null) {
+            try {
+                return mService.hasManagedProfileContactsAccess(userHandle.getIdentifier(),
+                        packageName);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
      * Called by a profile owner of a managed profile to set whether contacts search from the
      * managed profile will be shown in the parent profile, for incoming calls.
      * <p>
      * The calling device admin must be a profile owner. If it is not, a security exception will be
      * thrown.
      *
+     * Starting with {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, calling this function
+     * is similar to calling {@link #setManagedProfileContactsAccessPolicy(PackagePolicy)} with a
+     * {@link PackagePolicy#PACKAGE_POLICY_BLOCKLIST} policy type when {@code disabled} is false
+     * or a {@link PackagePolicy#PACKAGE_POLICY_ALLOWLIST} policy type when {@code disabled}
+     * is true.
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param disabled If true contacts search in the managed profile is not displayed.
      * @throws SecurityException if {@code admin} is not a profile owner.
+     *
+     * @deprecated From {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} use
+     * {@link #setManagedProfileContactsAccessPolicy(PackagePolicy)}
      */
+    @Deprecated
     public void setCrossProfileContactsSearchDisabled(@NonNull ComponentName admin,
             boolean disabled) {
         throwIfParentInstance("setCrossProfileContactsSearchDisabled");
@@ -9962,10 +10147,19 @@
      * <p>
      * The calling device admin must be a profile owner. If it is not, a security exception will be
      * thrown.
+     * <p>
+     * Starting with {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+     * this will return true when
+     * {@link #setManagedProfileContactsAccessPolicy(PackagePolicy)}
+     * has been set with a non-null policy whose policy type is NOT
+     * {@link PackagePolicy#PACKAGE_POLICY_BLOCKLIST}
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @throws SecurityException if {@code admin} is not a profile owner.
+     * @deprecated From {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} use
+     * {@link #getManagedProfileContactsAccessPolicy()}
      */
+    @Deprecated
     public boolean getCrossProfileContactsSearchDisabled(@NonNull ComponentName admin) {
         throwIfParentInstance("getCrossProfileContactsSearchDisabled");
         if (mService != null) {
@@ -9978,13 +10172,20 @@
         return false;
     }
 
-
     /**
      * Determine whether or not contacts search has been disabled.
-     *
+     * <p>
+     * Starting with {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
+     * this will return true when
+     * {@link #setManagedProfileContactsAccessPolicy(PackagePolicy)}
+     * has been set with a non-null policy whose policy type is NOT
+     * {@link PackagePolicy#PACKAGE_POLICY_BLOCKLIST}
      * @param userHandle The user for whom to check the contacts search permission
+     * @deprecated use {@link #hasManagedProfileContactsAccess(UserHandle, String)} and provide the
+     * package name requesting access
      * @hide
      */
+    @Deprecated
     public boolean getCrossProfileContactsSearchDisabled(@NonNull UserHandle userHandle) {
         if (mService != null) {
             try {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 5383dca..26d7667 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -29,6 +29,7 @@
 import android.app.admin.StartInstallingUpdateCallback;
 import android.app.admin.SystemUpdateInfo;
 import android.app.admin.SystemUpdatePolicy;
+import android.app.admin.PackagePolicy;
 import android.app.admin.PasswordMetrics;
 import android.app.admin.FactoryResetProtectionPolicy;
 import android.app.admin.ManagedProfileProvisioningParams;
@@ -330,6 +331,14 @@
     boolean getCrossProfileContactsSearchDisabledForUser(int userId);
     void startManagedQuickContact(String lookupKey, long contactId, boolean isContactIdIgnored, long directoryId, in Intent originalIntent);
 
+    void setManagedProfileCallerIdAccessPolicy(in PackagePolicy policy);
+    PackagePolicy getManagedProfileCallerIdAccessPolicy();
+    boolean hasManagedProfileCallerIdAccess(int userId, String packageName);
+
+    void setManagedProfileContactsAccessPolicy(in PackagePolicy policy);
+    PackagePolicy getManagedProfileContactsAccessPolicy();
+    boolean hasManagedProfileContactsAccess(int userId, String packageName);
+
     void setBluetoothContactSharingDisabled(in ComponentName who, boolean disabled);
     boolean getBluetoothContactSharingDisabled(in ComponentName who);
     boolean getBluetoothContactSharingDisabledForUser(int userId);
diff --git a/core/java/android/app/admin/PackagePolicy.aidl b/core/java/android/app/admin/PackagePolicy.aidl
new file mode 100644
index 0000000..33a67de
--- /dev/null
+++ b/core/java/android/app/admin/PackagePolicy.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+parcelable PackagePolicy;
\ No newline at end of file
diff --git a/core/java/android/app/admin/PackagePolicy.java b/core/java/android/app/admin/PackagePolicy.java
new file mode 100644
index 0000000..0d457ba
--- /dev/null
+++ b/core/java/android/app/admin/PackagePolicy.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+
+/**
+ * A generic class that defines which APK packages are in scope for some device policy.
+ * <p>
+ * The packages can be defined using either an allowlist or a blocklist.
+ * In allowlist mode, it could optionally include all system packages
+ * that meet the specific criteria of the device policy in question.
+ */
+public final class PackagePolicy implements Parcelable {
+
+    /**
+     * PackagePolicy type indicator for {@link PackagePolicy}
+     * <p>
+     * This constant indicates that all packages are allowed except for the packages returned by
+     * {@link PackagePolicy#getPackageNames()}, which acts as a denylist.
+     * @see #PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM
+     * @see #PACKAGE_POLICY_ALLOWLIST
+     */
+    public static final int PACKAGE_POLICY_BLOCKLIST = 1;
+
+    /**
+     * PackagePolicy type indicator for {@link PackagePolicy}
+     * <p>
+     * This constant indicates system packages are allowed in addition to the packages returned by
+     * {@link PackagePolicy#getPackageNames()}, which acts as an allowlist.
+     *
+     * <p>Functions that accept {@link PackagePolicy} will further clarify
+     * how this policy is interpreted.
+     *
+     * @see #PACKAGE_POLICY_BLOCKLIST
+     * @see #PACKAGE_POLICY_ALLOWLIST
+     */
+    public static final int PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM = 2;
+
+    /**
+     * PackagePolicy type indicator for {@link PackagePolicy}
+     * <p>
+     * This constant indicates that all packages are denied except for the packages returned by
+     * {@link PackagePolicy#getPackageNames()}, which acts as an allowlist.
+     *
+     * @see #PACKAGE_POLICY_BLOCKLIST
+     * @see #PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM
+     */
+    public static final int PACKAGE_POLICY_ALLOWLIST = 3;
+
+    /**
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"PACKAGE_POLICY_"}, value = {
+            PACKAGE_POLICY_BLOCKLIST,
+            PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM,
+            PACKAGE_POLICY_ALLOWLIST
+    })
+    public @interface PackagePolicyType {}
+
+    private @PackagePolicyType int mPolicyType;
+
+    private ArraySet<String> mPackageNames;
+
+    /**
+     * Create the package policy
+     * @param policyType indicates how to interpret this policy
+
+     * @see PackagePolicy#PackagePolicy(int, Set)
+     */
+    public PackagePolicy(@PackagePolicyType int policyType) {
+        this(policyType, Collections.emptySet());
+    }
+
+    /**
+     * Create the package policy
+     * @param policyType indicates how to interpret this policy
+     * @param packageNames allowlist or a denylist, based on policyType
+     */
+    public PackagePolicy(@PackagePolicyType int policyType, @NonNull Set<String> packageNames) {
+        if (policyType != PACKAGE_POLICY_BLOCKLIST
+                && policyType != PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM
+                && policyType != PACKAGE_POLICY_ALLOWLIST) {
+            throw new IllegalArgumentException("Invalid policy type");
+        }
+        mPolicyType = policyType;
+        mPackageNames = new ArraySet<>(packageNames);
+    }
+
+    private PackagePolicy(Parcel in) {
+        mPolicyType = in.readInt();
+        mPackageNames = (ArraySet<String>) in.readArraySet(null);
+    }
+
+    /**
+     * Returns the current policy type
+     */
+    public @PackagePolicyType int getPolicyType() {
+        return mPolicyType;
+    }
+
+    /**
+     * Returns the list of packages to use as an allow/deny list based on policy type
+     */
+    @NonNull
+    public Set<String> getPackageNames() {
+        return Collections.unmodifiableSet(mPackageNames);
+    }
+
+    /**
+     * Evaluates the packageName provided against this policy to determine if the package should be
+     * allowed.
+     *
+     * If the policy type is {@link #PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM},
+     * the systemPackage will be used in addition to package names of this policy's
+     * {@link #getPackageNames()}
+     *
+     * @param packageName  the name of the package to test
+     * @param systemPackages list of packages identified as system packages
+     * @return true if the package is allowed, false if the package is denied
+     * @hide
+     */
+    public boolean isPackageAllowed(@NonNull String packageName,
+            @NonNull Set<String> systemPackages) {
+        if (mPolicyType == PACKAGE_POLICY_BLOCKLIST) {
+            return !mPackageNames.contains(packageName);
+        }
+        return mPackageNames.contains(packageName)
+                || (mPolicyType == PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM
+                      && systemPackages.contains(packageName));
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    @NonNull
+    public static final Creator<PackagePolicy> CREATOR = new Creator<PackagePolicy>() {
+        @Override
+        public PackagePolicy createFromParcel(Parcel in) {
+            return new PackagePolicy(in);
+        }
+
+        @Override
+        public PackagePolicy[] newArray(int size) {
+            return new PackagePolicy[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mPolicyType);
+        dest.writeArraySet(mPackageNames);
+    }
+
+    @Override
+    public boolean equals(Object thatObject) {
+        if (this == thatObject) {
+            return true;
+        }
+        if (!(thatObject instanceof PackagePolicy)) {
+            return false;
+        }
+        PackagePolicy that = (PackagePolicy) thatObject;
+        return mPolicyType == that.mPolicyType && mPackageNames.equals(that.mPackageNames);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPolicyType, mPackageNames);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index bb3bf5c..708a02d 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -109,6 +109,7 @@
 import java.util.List;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
+import java.util.function.IntConsumer;
 
 /**
  * Interface to global information about an application environment.  This is
@@ -276,7 +277,8 @@
             BIND_IMPORTANT,
             BIND_ADJUST_WITH_ACTIVITY,
             BIND_NOT_PERCEPTIBLE,
-            BIND_INCLUDE_CAPABILITIES
+            BIND_INCLUDE_CAPABILITIES,
+            BIND_SHARED_ISOLATED_PROCESS
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface BindServiceFlags {}
@@ -393,6 +395,22 @@
      */
     public static final int BIND_INCLUDE_CAPABILITIES = 0x000001000;
 
+    /**
+     * Flag for {@link #bindIsolatedService}: Bind the service into a shared isolated process.
+     * Specifying this flag allows multiple isolated services to be running in a single shared
+     * isolated process.
+     *
+     * The shared isolated process instance is identified by the <var>instanceName</var>
+     * parameter in {@link #bindIsolatedService(Intent, int, String, Executor, ServiceConnection)}.
+     *
+     * Subsequent calls to {@link #bindIsolatedService} with the same <var>instanceName</var>
+     * will cause the isolated service to be co-located in the same shared isolated process.
+     *
+     * Note that the shared isolated process is scoped to the calling app; once created, only
+     * the calling app can bind additional isolated services into the shared process.
+     */
+    public static final int BIND_SHARED_ISOLATED_PROCESS = 0x00002000;
+
     /***********    Public flags above this line ***********/
     /***********    Hidden flags below this line ***********/
 
@@ -6921,6 +6939,10 @@
      * {@link android.companion.virtual.VirtualDeviceManager#DEVICE_ID_DEFAULT}. Similarly,
      * applications running on the default device may access the functionality of virtual devices.
      * </p>
+     * <p>
+     * Note that the newly created instance will be associated with the same display as the parent
+     * Context, regardless of the device ID passed here.
+     * </p>
      * @param deviceId The ID of the device to associate with this context.
      * @return A context associated with the given device ID.
      *
@@ -7256,20 +7278,116 @@
     public abstract void updateDisplay(int displayId);
 
     /**
-     * Get the device ID this context is associated with. Applications can use this method to
+     * Updates the device ID association of this Context. Since a Context created with
+     * {@link #createDeviceContext} cannot change its device association, this method must
+     * not be called for instances created with {@link #createDeviceContext}.
+     *
+     * @param deviceId The new device ID to assign to this Context.
+     * @throws UnsupportedOperationException if the method is called on an instance that was
+     *         created with {@link Context#createDeviceContext(int)}
+     * @throws IllegalArgumentException if the given device ID is not a valid ID of the default
+     *         device or a virtual device.
+     *
+     * @see #isDeviceContext()
+     * @see #createDeviceContext(int)
+     * @hide
+     */
+    public void updateDeviceId(int deviceId) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Gets the device ID this context is associated with. Applications can use this method to
      * determine whether they are running on a virtual device and identify that device.
      *
      * The device ID of the host device is
      * {@link android.companion.virtual.VirtualDeviceManager#DEVICE_ID_DEFAULT}
      *
+     * <p>
+     * If the underlying device ID is changed by the system, for example, when an
+     * {@link Activity} is moved to a different virtual device, applications can register to listen
+     * to changes by calling
+     * {@link Context#registerDeviceIdChangeListener(Executor, IntConsumer)}.
+     * </p>
+     *
+     * <p>
+     * This method will only return a reliable value for this instance if
+     * {@link Context#isDeviceContext()} is {@code true}. The system can assign an arbitrary device
+     * id value for Contexts not logically associated with a device.
+     * </p>
+     *
      * @return the ID of the device this context is associated with.
+     * @see #isDeviceContext()
      * @see #createDeviceContext(int)
+     * @see #registerDeviceIdChangeListener(Executor, IntConsumer)
      */
     public int getDeviceId() {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
 
     /**
+     * Indicates whether the value of {@link Context#getDeviceId()} can be relied upon for
+     * this instance. It will return {@code true} for Contexts created by
+     * {@link Context#createDeviceContext(int)}, as well as for UI and Display Contexts.
+     * <p>
+     * Contexts created with {@link Context#createDeviceContext(int)} will have an explicit
+     * device association, which will never change. UI Contexts and Display Contexts are
+     * already associated with a display, so if the device association is not explicitly
+     * given, {@link Context#getDeviceId()} will return the ID of the device associated with
+     * the associated display. The system can assign an arbitrary device id value for Contexts not
+     * logically associated with a device.
+     * </p>
+     *
+     * @return {@code true} if {@link Context#getDeviceId()} is reliable, {@code false} otherwise.
+     *
+     * @see #createDeviceContext(int)
+     * @see #getDeviceId()}
+     * @see #createDisplayContext(Display)
+     * @see #isUiContext()
+     */
+
+    public boolean isDeviceContext() {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Adds a new device ID changed listener to the {@code Context}, which will be called when
+     * the device association is changed by the system.
+     * <p>
+     * The callback can be called when an app is moved to a different device and the {@code Context}
+     * is not explicily associated with a specific device.
+     * </p>
+     * <p> When an application receives a device id update callback, this Context is guaranteed to
+     * also have an updated display ID(if any) and {@link Configuration}.
+     * <p/>
+     * @param executor The Executor on whose thread to execute the callbacks of the {@code listener}
+     *                 object.
+     * @param listener The listener {@code IntConsumer} to call which will receive the updated
+     *                 device ID.
+     *
+     * @see Context#isDeviceContext()
+     * @see Context#getDeviceId()
+     * @see Context#createDeviceContext(int)
+     */
+    public void registerDeviceIdChangeListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull IntConsumer listener) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
+     * Removes a device ID changed listener from the Context. It's a no-op if
+     * the listener is not already registered.
+     *
+     * @param listener The {@code Consumer} to remove.
+     *
+     * @see #getDeviceId()
+     * @see #registerDeviceIdChangeListener(Executor, IntConsumer)
+     */
+    public void unregisterDeviceIdChangeListener(@NonNull IntConsumer listener) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
      * Indicates whether this Context is restricted.
      *
      * @return {@code true} if this Context is restricted, {@code false} otherwise.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index a1646a1..0a32dd78 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -61,6 +62,7 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.Executor;
+import java.util.function.IntConsumer;
 
 /**
  * Proxying implementation of Context that simply delegates all of its calls to
@@ -1171,12 +1173,36 @@
         mBase.updateDisplay(displayId);
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public void updateDeviceId(int deviceId) {
+        mBase.updateDeviceId(deviceId);
+    }
+
     @Override
     public int getDeviceId() {
         return mBase.getDeviceId();
     }
 
     @Override
+    public boolean isDeviceContext() {
+        return mBase.isDeviceContext();
+    }
+
+    @Override
+    public void registerDeviceIdChangeListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull IntConsumer listener) {
+        mBase.registerDeviceIdChangeListener(executor, listener);
+    }
+
+    @Override
+    public void unregisterDeviceIdChangeListener(@NonNull IntConsumer listener) {
+        mBase.unregisterDeviceIdChangeListener(listener);
+    }
+
+    @Override
     public Context createDeviceProtectedStorageContext() {
         return mBase.createDeviceProtectedStorageContext();
     }
diff --git a/core/java/android/content/om/FabricatedOverlay.java b/core/java/android/content/om/FabricatedOverlay.java
index 99fc5a3..e4936bc 100644
--- a/core/java/android/content/om/FabricatedOverlay.java
+++ b/core/java/android/content/om/FabricatedOverlay.java
@@ -35,29 +35,84 @@
 import java.util.Objects;
 
 /**
- * Fabricated Runtime Resource Overlays (FRROs) are overlays generated ar runtime.
+ * FabricatedOverlay describes the content of Fabricated Runtime Resource Overlay (FRRO) that is
+ * used to overlay the app's resources. The app should register the {@link FabricatedOverlay}
+ * instance in an {@link OverlayManagerTransaction} by calling {@link
+ * OverlayManagerTransaction#registerFabricatedOverlay(FabricatedOverlay)}. The FRRO is
+ * created once the transaction is committed successfully.
  *
- * Fabricated overlays are enabled, disabled, and reordered just like normal overlays. The
- * overlayable policies a fabricated overlay fulfills are the same policies the creator of the
- * overlay fulfill. For example, a fabricated overlay created by a platform signed package on the
- * system partition would fulfil the {@code system} and {@code signature} policies.
+ * <p>The app creates a FabricatedOverlay to describe the how to overlay string, integer, and file
+ * type resources. Before creating any frro, please define a target overlayable in {@code
+ * res/values/overlayable.xml} that describes what kind of resources can be overlaid, what kind of
+ * roles or applications can overlay the resources. Here is an example.
  *
- * The owner of a fabricated overlay is the UID that created it. Overlays commit to the overlay
- * manager persist across reboots. When the UID is uninstalled, its fabricated overlays are wiped.
+ * <pre>{@code
+ * <overlayable name="SignatureOverlayable" actor="overlay://theme">
+ *     <!-- The app with the same signature can overlay the below resources -->
+ *     <policy type="signature">
+ *         <item type="color" name="mycolor" />
+ *         <item type="string" name="mystring" />
+ *     </policy>
+ * </overlayable>
+ * }</pre>
  *
- * Processes with {@link Android.Manifest.permission.CHANGE_OVERLAY_PACKAGES} can manage normal
- * overlays and fabricated overlays.
+ * <p>The overlay must assign the target overlayable name just like the above example by calling
+ * {@link #setTargetOverlayable(String)}. Here is an example:
+ *
+ * <pre>{@code
+ * FabricatedOverlay fabricatedOverlay = new FabricatedOverlay("overlay_name",
+ *                                                             context.getPackageName());
+ * fabricatedOverlay.setTargetOverlayable("SignatureOverlayable")
+ * fabricatedOverlay.setResourceValue("mycolor", TypedValue.TYPE_INT_COLOR_ARGB8, Color.White)
+ * fabricatedOverlay.setResourceValue("mystring", TypedValue.TYPE_STRING, "Hello")
+ * }</pre>
+ *
+ * <p>The app can create any {@link FabricatedOverlay} instance by calling the following APIs.
+ *
+ * <ul>
+ *   <li>{@link #setTargetOverlayable(String)}
+ *   <li>{@link #setResourceValue(String, int, int, String)}
+ *   <li>{@link #setResourceValue(String, int, String, String)}
+ *   <li>{@link #setResourceValue(String, ParcelFileDescriptor, String)}
+ * </ul>
+ *
+ * @see OverlayManager
+ * @see OverlayManagerTransaction
  * @hide
  */
 public class FabricatedOverlay {
 
-    /** Retrieves the identifier for this fabricated overlay. */
+    /**
+     * Retrieves the identifier for this fabricated overlay.
+     * @return the overlay identifier
+     *
+     * @hide
+     */
     public OverlayIdentifier getIdentifier() {
         return new OverlayIdentifier(
                 mOverlay.packageName, TextUtils.nullIfEmpty(mOverlay.overlayName));
     }
 
-    public static class Builder {
+    /**
+     * The builder of Fabricated Runtime Resource Overlays(FRROs).
+     *
+     * Fabricated overlays are enabled, disabled, and reordered just like normal overlays. The
+     * overlayable policies a fabricated overlay fulfills are the same policies the creator of the
+     * overlay fulfill. For example, a fabricated overlay created by a platform signed package on
+     * the system partition would fulfil the {@code system} and {@code signature} policies.
+     *
+     * The owner of a fabricated overlay is the UID that created it. Overlays commit to the overlay
+     * manager persist across reboots. When the UID is uninstalled, its fabricated overlays are
+     * wiped.
+     *
+     * Processes with {@code android.Manifest.permission#CHANGE_OVERLAY_PACKAGES} can manage normal
+     * overlays and fabricated overlays.
+     *
+     * @see FabricatedOverlay
+     * @see OverlayManagerTransaction.Builder#registerFabricatedOverlay(FabricatedOverlay)
+     * @hide
+     */
+    public static final class Builder {
         private final String mOwningPackage;
         private final String mName;
         private final String mTargetPackage;
@@ -88,22 +143,16 @@
         }
 
         /**
-         * Constructs a builder for building a fabricated overlay.
+         * Sets the name of the target overlayable to be overlaid.
          *
-         * @param name a name used to uniquely identify the fabricated overlay owned by the caller
-         *             itself.
-         * @param targetPackage the name of the package to overlay
-         */
-        public Builder(@NonNull String name, @NonNull String targetPackage) {
-            mName = OverlayManagerImpl.checkOverlayNameValid(name);
-            mTargetPackage =
-                    Preconditions.checkStringNotEmpty(
-                            targetPackage, "'targetPackage' must not be empty nor null");
-            mOwningPackage = ""; // The package name is filled in OverlayManager.commit
-        }
-
-        /**
-         * Sets the name of the overlayable resources to overlay (can be null).
+         * <p>The target package defines may define several overlayables. The
+         * {@link FabricatedOverlay} should specify which overlayable to be overlaid.
+         *
+         * <p>The target overlayable should be defined in {@code <overlayable>} and pass the value
+         * of its {@code name} attribute as the parameter.
+         *
+         * @param targetOverlayable is a name of the overlayable resources set
+         * @hide
          */
         @NonNull
         public Builder setTargetOverlayable(@Nullable String targetOverlayable) {
@@ -112,27 +161,6 @@
         }
 
         /**
-         * Ensure the resource name is in the form [package]:type/entry.
-         *
-         * @param name name of the target resource to overlay (in the form [package]:type/entry)
-         * @return the valid name
-         */
-        private static String ensureValidResourceName(@NonNull String name) {
-            Objects.requireNonNull(name);
-            final int slashIndex = name.indexOf('/'); /* must contain '/' */
-            final int colonIndex = name.indexOf(':'); /* ':' should before '/' if ':' exist */
-
-            // The minimum length of resource type is "id".
-            Preconditions.checkArgument(
-                    slashIndex >= 0 /* It must contain the type name */
-                    && colonIndex != 0 /* 0 means the package name is empty */
-                    && (slashIndex - colonIndex) > 2 /* The shortest length of type is "id" */,
-                    "\"%s\" is invalid resource name",
-                    name);
-            return name;
-        }
-
-        /**
          * Sets the value of the fabricated overlay for the integer-like types.
          *
          * @param resourceName name of the target resource to overlay (in the form
@@ -141,8 +169,12 @@
          * @param value the unsigned 32 bit integer representing the new value
          * @return the builder itself
          * @see #setResourceValue(String, int, int, String)
-         * @see android.util.TypedValue#type
+         * @see android.util.TypedValue#TYPE_INT_COLOR_ARGB8 android.util.TypedValue#type
+         * @deprecated Framework should use {@link FabricatedOverlay#setResourceValue(String, int,
+                       int, String)} instead.
+         * @hide
          */
+        @Deprecated(since = "Please use FabricatedOverlay#setResourceValue instead")
         @NonNull
         public Builder setResourceValue(
                 @NonNull String resourceName,
@@ -161,8 +193,13 @@
          * @param dataType the data type of the new value
          * @param value the unsigned 32 bit integer representing the new value
          * @param configuration The string representation of the config this overlay is enabled for
-         * @see android.util.TypedValue#type
+         * @return the builder itself
+         * @see android.util.TypedValue#TYPE_INT_COLOR_ARGB8 android.util.TypedValue#type
+         * @deprecated Framework should use {@link FabricatedOverlay#setResourceValue(String, int,
+                       int, String)} instead.
+         * @hide
          */
+        @Deprecated(since = "Please use FabricatedOverlay#setResourceValue instead")
         @NonNull
         public Builder setResourceValue(
                 @NonNull String resourceName,
@@ -171,30 +208,11 @@
                 int value,
                 @Nullable String configuration) {
             ensureValidResourceName(resourceName);
-
-            final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
-            entry.resourceName = resourceName;
-            entry.dataType =
-                    Preconditions.checkArgumentInRange(
-                            dataType,
-                            TypedValue.TYPE_FIRST_INT,
-                            TypedValue.TYPE_LAST_INT,
-                            "dataType");
-            entry.data = value;
-            entry.configuration = configuration;
-            mEntries.add(entry);
+            mEntries.add(generateFabricatedOverlayInternalEntry(resourceName, dataType, value,
+                    configuration));
             return this;
         }
 
-        /** @hide */
-        @IntDef(
-                prefix = {"OVERLAY_TYPE"},
-                value = {
-                    TypedValue.TYPE_STRING,
-                })
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface StringTypeOverlayResource {}
-
         /**
          * Sets the value of the fabricated overlay for the string-like type.
          *
@@ -203,8 +221,12 @@
          * @param dataType the data type of the new value
          * @param value the string representing the new value
          * @return the builder itself
-         * @see android.util.TypedValue#type
+         * @see android.util.TypedValue#TYPE_STRING android.util.TypedValue#type
+         * @deprecated Framework should use {@link FabricatedOverlay#setResourceValue(String, int,
+                       String, String)} instead.
+         * @hide
          */
+        @Deprecated(since = "Please use FabricatedOverlay#setResourceValue instead")
         @NonNull
         public Builder setResourceValue(
                 @NonNull String resourceName,
@@ -221,8 +243,13 @@
          * @param dataType the data type of the new value
          * @param value the string representing the new value
          * @param configuration The string representation of the config this overlay is enabled for
-         * @see android.util.TypedValue#type
+         * @return the builder itself
+         * @see android.util.TypedValue#TYPE_STRING android.util.TypedValue#type
+         * @deprecated Framework should use {@link FabricatedOverlay#setResourceValue(String, int,
+                       String, String)} instead.
+         * @hide
          */
+        @Deprecated(since = "Please use FabricatedOverlay#setResourceValue instead")
         @NonNull
         public Builder setResourceValue(
                 @NonNull String resourceName,
@@ -230,39 +257,32 @@
                 @NonNull String value,
                 @Nullable String configuration) {
             ensureValidResourceName(resourceName);
-
-            final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
-            entry.resourceName = resourceName;
-            entry.dataType =
-                    Preconditions.checkArgumentInRange(
-                            dataType, TypedValue.TYPE_STRING, TypedValue.TYPE_FRACTION, "dataType");
-            entry.stringData = Objects.requireNonNull(value);
-            entry.configuration = configuration;
-            mEntries.add(entry);
+            mEntries.add(generateFabricatedOverlayInternalEntry(resourceName, dataType, value,
+                    configuration));
             return this;
         }
 
         /**
-         * Sets the value of the fabricated overlay
+         * Sets the value of the fabricated overlay for the file descriptor type.
          *
          * @param resourceName name of the target resource to overlay (in the form
          *     [package]:type/entry)
          * @param value the file descriptor whose contents are the value of the frro
          * @param configuration The string representation of the config this overlay is enabled for
          * @return the builder itself
+         * @deprecated Framework should use {@link FabricatedOverlay#setResourceValue(String,
+                       ParcelFileDescriptor, String)} instead.
+         * @hide
          */
+        @Deprecated(since = "Please use FabricatedOverlay#setResourceValue instead")
         @NonNull
         public Builder setResourceValue(
                 @NonNull String resourceName,
                 @NonNull ParcelFileDescriptor value,
                 @Nullable String configuration) {
             ensureValidResourceName(resourceName);
-
-            final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
-            entry.resourceName = resourceName;
-            entry.binaryData = Objects.requireNonNull(value);
-            entry.configuration = configuration;
-            mEntries.add(entry);
+            mEntries.add(
+                    generateFabricatedOverlayInternalEntry(resourceName, value, configuration));
             return this;
         }
 
@@ -270,22 +290,216 @@
          * Builds an immutable fabricated overlay.
          *
          * @return the fabricated overlay
+         * @hide
          */
         @NonNull
         public FabricatedOverlay build() {
-            final FabricatedOverlayInternal overlay = new FabricatedOverlayInternal();
-            overlay.packageName = mOwningPackage;
-            overlay.overlayName = mName;
-            overlay.targetPackageName = mTargetPackage;
-            overlay.targetOverlayable = mTargetOverlayable;
-            overlay.entries = new ArrayList<>();
-            overlay.entries.addAll(mEntries);
-            return new FabricatedOverlay(overlay);
+            return new FabricatedOverlay(
+                    generateFabricatedOverlayInternal(mOwningPackage, mName, mTargetPackage,
+                            mTargetOverlayable, mEntries));
         }
     }
 
+    private static FabricatedOverlayInternal generateFabricatedOverlayInternal(
+            @NonNull String owningPackage, @NonNull String overlayName,
+            @NonNull String targetPackageName, @Nullable String targetOverlayable,
+            @NonNull ArrayList<FabricatedOverlayInternalEntry> entries) {
+        final FabricatedOverlayInternal overlay = new FabricatedOverlayInternal();
+        overlay.packageName = owningPackage;
+        overlay.overlayName = overlayName;
+        overlay.targetPackageName = targetPackageName;
+        overlay.targetOverlayable = TextUtils.emptyIfNull(targetOverlayable);
+        overlay.entries = new ArrayList<>();
+        overlay.entries.addAll(entries);
+        return overlay;
+    }
+
     final FabricatedOverlayInternal mOverlay;
     private FabricatedOverlay(FabricatedOverlayInternal overlay) {
         mOverlay = overlay;
     }
+
+    /**
+     * Create a fabricated overlay to overlay on the specified package.
+     *
+     * @param overlayName a name used to uniquely identify the fabricated overlay owned by the
+     *                   caller itself.
+     * @param targetPackage the name of the package to be overlaid
+     * @hide
+     */
+    public FabricatedOverlay(@NonNull String overlayName, @NonNull String targetPackage) {
+        this(generateFabricatedOverlayInternal(
+                "" /* owningPackage, The package name is filled commitment */,
+                OverlayManagerImpl.checkOverlayNameValid(overlayName),
+                Preconditions.checkStringNotEmpty(targetPackage,
+                        "'targetPackage' must not be empty nor null"),
+                null /* targetOverlayable */,
+                new ArrayList<>()));
+    }
+
+    /**
+     * Set the target overlayable name of the overlay
+     *
+     * The target package defines may define several overlayables. The {@link FabricatedOverlay}
+     * should specify which overlayable to be overlaid.
+     *
+     * @param targetOverlayable the overlayable name defined in target package.
+     * @hide
+     */
+    public void setTargetOverlayable(@Nullable String targetOverlayable) {
+        mOverlay.targetOverlayable = TextUtils.emptyIfNull(targetOverlayable);
+    }
+
+    /**
+     * Return the target overlayable name of the overlay
+     *
+     * The target package defines may define several overlayables. The {@link FabricatedOverlay}
+     * should specify which overlayable to be overlaid.
+     *
+     * @return the target overlayable name.
+     * @hide
+     */
+    @Nullable
+    public String getTargetOverlayable() {
+        return mOverlay.targetOverlayable;
+    }
+
+    /**
+     * Ensure the resource name is in the form [package]:type/entry.
+     *
+     * @param name name of the target resource to overlay (in the form [package]:type/entry)
+     * @return the valid name
+     */
+    private static String ensureValidResourceName(@NonNull String name) {
+        Objects.requireNonNull(name);
+        final int slashIndex = name.indexOf('/'); /* must contain '/' */
+        final int colonIndex = name.indexOf(':'); /* ':' should before '/' if ':' exist */
+
+        // The minimum length of resource type is "id".
+        Preconditions.checkArgument(
+                slashIndex >= 0 /* It must contain the type name */
+                        && colonIndex != 0 /* 0 means the package name is empty */
+                        && (slashIndex - colonIndex) > 2 /* The shortest length of type is "id" */,
+                "\"%s\" is invalid resource name",
+                name);
+        return name;
+    }
+
+    @NonNull
+    private static FabricatedOverlayInternalEntry generateFabricatedOverlayInternalEntry(
+            @NonNull String resourceName,
+            @IntRange(from = TypedValue.TYPE_FIRST_INT, to = TypedValue.TYPE_LAST_INT) int dataType,
+            int value, @Nullable String configuration) {
+        final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
+        entry.resourceName = resourceName;
+        entry.dataType =
+                Preconditions.checkArgumentInRange(
+                        dataType,
+                        TypedValue.TYPE_FIRST_INT,
+                        TypedValue.TYPE_LAST_INT,
+                        "dataType");
+        entry.data = value;
+        entry.configuration = configuration;
+        return entry;
+    }
+
+    @NonNull
+    private static FabricatedOverlayInternalEntry generateFabricatedOverlayInternalEntry(
+            @NonNull String resourceName, @StringTypeOverlayResource int dataType,
+            @NonNull String value, @Nullable String configuration) {
+        final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
+        entry.resourceName = resourceName;
+        entry.dataType =
+                Preconditions.checkArgumentInRange(
+                        dataType, TypedValue.TYPE_STRING, TypedValue.TYPE_FRACTION, "dataType");
+        entry.stringData = Objects.requireNonNull(value);
+        entry.configuration = configuration;
+        return entry;
+    }
+
+    @NonNull
+    private static FabricatedOverlayInternalEntry generateFabricatedOverlayInternalEntry(
+            @NonNull String resourceName, @NonNull ParcelFileDescriptor parcelFileDescriptor,
+            @Nullable String configuration) {
+        final FabricatedOverlayInternalEntry entry = new FabricatedOverlayInternalEntry();
+        entry.resourceName = resourceName;
+        entry.binaryData = Objects.requireNonNull(parcelFileDescriptor);
+        entry.configuration = configuration;
+        return entry;
+    }
+
+    /**
+     * Sets the resource value in the fabricated overlay for the integer-like types with the
+     * configuration.
+     *
+     * @param resourceName name of the target resource to overlay (in the form
+     *     [package]:type/entry)
+     * @param dataType the data type of the new value
+     * @param value the integer representing the new value
+     * @param configuration The string representation of the config this overlay is enabled for
+     * @see android.util.TypedValue#TYPE_INT_COLOR_ARGB8 android.util.TypedValue#type
+     * @hide
+     */
+    @NonNull
+    public void setResourceValue(
+            @NonNull String resourceName,
+            @IntRange(from = TypedValue.TYPE_FIRST_INT, to = TypedValue.TYPE_LAST_INT) int dataType,
+            int value,
+            @Nullable String configuration) {
+        ensureValidResourceName(resourceName);
+        mOverlay.entries.add(generateFabricatedOverlayInternalEntry(resourceName, dataType, value,
+                configuration));
+    }
+
+    /** @hide */
+    @IntDef(
+            prefix = {"OVERLAY_TYPE"},
+            value = {
+                    TypedValue.TYPE_STRING,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StringTypeOverlayResource {}
+
+    /**
+     * Sets the resource value in the fabricated overlay for the string-like type with the
+     * configuration.
+     *
+     * @param resourceName name of the target resource to overlay (in the form
+     *     [package]:type/entry)
+     * @param dataType the data type of the new value
+     * @param value the string representing the new value
+     * @param configuration The string representation of the config this overlay is enabled for
+     * @see android.util.TypedValue#TYPE_STRING android.util.TypedValue#type
+     * @hide
+     */
+    @NonNull
+    public void setResourceValue(
+            @NonNull String resourceName,
+            @StringTypeOverlayResource int dataType,
+            @NonNull String value,
+            @Nullable String configuration) {
+        ensureValidResourceName(resourceName);
+        mOverlay.entries.add(generateFabricatedOverlayInternalEntry(resourceName, dataType, value,
+                configuration));
+    }
+
+    /**
+     * Sets the resource value in the fabricated overlay for the file descriptor type with the
+     * configuration.
+     *
+     * @param resourceName name of the target resource to overlay (in the form
+     *     [package]:type/entry)
+     * @param value the file descriptor whose contents are the value of the frro
+     * @param configuration The string representation of the config this overlay is enabled for
+     * @hide
+     */
+    @NonNull
+    public void setResourceValue(
+            @NonNull String resourceName,
+            @NonNull ParcelFileDescriptor value,
+            @Nullable String configuration) {
+        ensureValidResourceName(resourceName);
+        mOverlay.entries.add(
+                generateFabricatedOverlayInternalEntry(resourceName, value, configuration));
+    }
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index be19203..cbdcc02 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -10427,9 +10427,7 @@
      * @throws NameNotFoundException if either a given package can not be found on the
      * system, or if the caller is not able to query for details about the source or
      * target packages.
-     * @hide
      */
-    @SystemApi
     @NonNull
     public boolean[] canPackageQuery(@NonNull String sourcePackageName,
             @NonNull String[] targetPackageNames) throws NameNotFoundException {
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 6453ab0..4ade8a8 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -79,6 +79,20 @@
     public static final int FLAG_USE_APP_ZYGOTE = 0x0008;
 
     /**
+     * Bit in {@link #flags}: If set, and this is an {@link android.R.attr#isolatedProcess}
+     * service, the service is allowed to be bound in a shared isolated process with other
+     * isolated services. Note that these other isolated services can also belong to other
+     * apps from different vendors.
+     *
+     * Shared isolated processes are created when using the
+     * {@link android.content.Context#BIND_SHARED_ISOLATED_PROCESS) during service binding.
+     *
+     * Note that when this flag is used, the {@link android.R.attr#process} attribute is
+     * ignored when the process is bound into a shared isolated process by a client.
+     */
+    public static final int FLAG_ALLOW_SHARED_ISOLATED_PROCESS = 0x0010;
+
+    /**
      * Bit in {@link #flags} indicating if the service is visible to ephemeral applications.
      * @hide
      */
diff --git a/core/java/android/credentials/ui/RequestInfo.java b/core/java/android/credentials/ui/RequestInfo.java
index c3937b6..5eaf2a0 100644
--- a/core/java/android/credentials/ui/RequestInfo.java
+++ b/core/java/android/credentials/ui/RequestInfo.java
@@ -44,6 +44,8 @@
     public static final @NonNull String EXTRA_REQUEST_INFO =
             "android.credentials.ui.extra.REQUEST_INFO";
 
+    /** Type value for any request that does not require UI. */
+    public static final @NonNull String TYPE_UNDEFINED = "android.credentials.ui.TYPE_UNDEFINED";
     /** Type value for an executeGetCredential request. */
     public static final @NonNull String TYPE_GET = "android.credentials.ui.TYPE_GET";
     /** Type value for an executeCreateCredential request. */
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index bbdb626..cca900a 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -574,6 +574,147 @@
     }
 
     /**
+     * Checks for postview support of still capture.
+     *
+     * <p>A postview is a preview version of the still capture that is available before the final
+     * image. For example, it can be used as a temporary placeholder for the requested capture
+     * while the final image is being processed. The supported sizes for a still capture's postview
+     * can be retrieved using
+     * {@link CameraExtensionCharacteristics#getPostviewSupportedSizes(int, Size, int)}.
+     * The formats of the still capture and postview should be equivalent upon capture request.</p>
+     *
+     * @param extension the extension type
+     * @return {@code true} in case postview is supported, {@code false} otherwise
+     *
+     * @throws IllegalArgumentException in case the extension type is not a
+     * supported device-specific extension
+     */
+    public boolean isPostviewAvailable(@Extension int extension) {
+        long clientId = registerClient(mContext);
+        if (clientId < 0) {
+            throw new IllegalArgumentException("Unsupported extensions");
+        }
+
+        try {
+            if (!isExtensionSupported(mCameraId, extension, mChars)) {
+                throw new IllegalArgumentException("Unsupported extension");
+            }
+
+            if (areAdvancedExtensionsSupported()) {
+                IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+                extender.init(mCameraId);
+                return extender.isPostviewAvailable();
+            } else {
+                Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+                        initializeExtension(extension);
+                extenders.second.init(mCameraId, mChars.getNativeMetadata());
+                return extenders.second.isPostviewAvailable();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to query the extension for postview availability! Extension "
+                    + "service does not respond!");
+        } finally {
+            unregisterClient(clientId);
+        }
+
+        return false;
+    }
+
+    /**
+     * Get a list of the postview sizes supported for a still capture, using its
+     * capture size {@code captureSize}, to use as an output for the postview request.
+     *
+     * <p>Available postview sizes will always be either equal to or less than the still
+     * capture size. When choosing the most applicable postview size for a usecase, it should
+     * be noted that lower resolution postviews will generally be available more quickly
+     * than larger resolution postviews. For example, when choosing a size for an optimized
+     * postview that will be displayed as a placeholder while the final image is processed,
+     * the resolution closest to the preview size may be most suitable.</p>
+     *
+     * <p>Note that device-specific extensions are allowed to support only a subset
+     * of the camera resolutions advertised by
+     * {@link StreamConfigurationMap#getOutputSizes}.</p>
+     *
+     * @param extension the extension type
+     * @param captureSize size of the still capture for which the postview is requested
+     * @param format device-specific extension output format of the still capture and
+     * postview
+     * @return non-modifiable list of available sizes or an empty list if the format and
+     * size is not supported.
+     * @throws IllegalArgumentException in case of unsupported extension or if postview
+     * feature is not supported by extension.
+     */
+    @NonNull
+    public List<Size> getPostviewSupportedSizes(@Extension int extension,
+            @NonNull Size captureSize, int format) {
+
+        long clientId = registerClient(mContext);
+        if (clientId < 0) {
+            throw new IllegalArgumentException("Unsupported extensions");
+        }
+
+        try {
+            if (!isExtensionSupported(mCameraId, extension, mChars)) {
+                throw new IllegalArgumentException("Unsupported extension");
+            }
+
+            android.hardware.camera2.extension.Size sz =
+                    new android.hardware.camera2.extension.Size();
+            sz.width = captureSize.getWidth();
+            sz.height = captureSize.getHeight();
+
+            StreamConfigurationMap streamMap = mChars.get(
+                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+            if (areAdvancedExtensionsSupported()) {
+                switch(format) {
+                    case ImageFormat.YUV_420_888:
+                    case ImageFormat.JPEG:
+                        break;
+                    default:
+                        throw new IllegalArgumentException("Unsupported format: " + format);
+                }
+                IAdvancedExtenderImpl extender = initializeAdvancedExtension(extension);
+                extender.init(mCameraId);
+                return generateSupportedSizes(extender.getSupportedPostviewResolutions(
+                    sz), format, streamMap);
+            } else {
+                Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+                        initializeExtension(extension);
+                extenders.second.init(mCameraId, mChars.getNativeMetadata());
+                if ((extenders.second.getCaptureProcessor() == null) ||
+                        !isPostviewAvailable(extension)) {
+                    // Extensions that don't implement any capture processor
+                    // and have processing occur in the HAL don't currently support the
+                    // postview feature
+                    throw new IllegalArgumentException("Extension does not support "
+                            + "postview feature");
+                }
+
+                if (format == ImageFormat.YUV_420_888) {
+                    return generateSupportedSizes(
+                            extenders.second.getSupportedPostviewResolutions(sz),
+                            format, streamMap);
+                } else if (format == ImageFormat.JPEG) {
+                    // The framework will perform the additional encoding pass on the
+                    // processed YUV_420 buffers.
+                    return generateJpegSupportedSizes(
+                            extenders.second.getSupportedPostviewResolutions(sz),
+                                    streamMap);
+                } else {
+                    throw new IllegalArgumentException("Unsupported format: " + format);
+                }
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to query the extension postview supported sizes! Extension "
+                    + "service does not respond!");
+            return Collections.emptyList();
+        } finally {
+            unregisterClient(clientId);
+        }
+    }
+
+    /**
      * Get a list of sizes compatible with {@code klass} to use as an output for the
      * repeating request
      * {@link CameraExtensionSession#setRepeatingRequest}.
diff --git a/core/java/android/hardware/camera2/CameraExtensionSession.java b/core/java/android/hardware/camera2/CameraExtensionSession.java
index b0fafea..1542d61 100644
--- a/core/java/android/hardware/camera2/CameraExtensionSession.java
+++ b/core/java/android/hardware/camera2/CameraExtensionSession.java
@@ -311,10 +311,15 @@
      * The rest of the settings included in the request will be entirely overridden by
      * the device-specific extension. </p>
      *
-     * <p>The {@link CaptureRequest.Builder#addTarget} supports only one
+     * <p> If {@link CameraExtensionCharacteristics#isPostviewAvailable} returns
+     * false, the {@link CaptureRequest.Builder#addTarget} will support only one
      * ImageFormat.YUV_420_888 or ImageFormat.JPEG target surface. {@link CaptureRequest}
-     * arguments that include further targets will cause
-     * IllegalArgumentException to be thrown. </p>
+     * arguments that include further targets will cause IllegalArgumentException to be thrown.
+     * If postview is available, {@link CaptureRequest.Builder#addTarget} will support up to two
+     * ImageFormat.YUV_420_888 or ImageFormat.JPEG target surfaces for the still capture and
+     * postview. IllegalArgumentException will be thrown if a postview target is added without
+     * a still capture target, if more than two target surfaces are added, or if the surface
+     * formats for postview and capture are not equivalent.
      *
      * <p>Starting with Android {@link android.os.Build.VERSION_CODES#TIRAMISU} single capture
      * requests will also support the preview {@link android.graphics.ImageFormat#PRIVATE} target
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index ce191e8..ed1e9e5 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -114,7 +114,7 @@
      */
     @ChangeId
     @Overridable
-    @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.TIRAMISU)
+    @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.BASE)
     @TestApi
     public static final long OVERRIDE_FRONT_CAMERA_APP_COMPAT = 250678880L;
 
diff --git a/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
index fa2cbe7..c9b7ea1 100644
--- a/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IAdvancedExtenderImpl.aidl
@@ -30,8 +30,10 @@
             int format);
     @nullable List<SizeList> getSupportedPreviewOutputResolutions(in String cameraId);
     @nullable List<SizeList> getSupportedCaptureOutputResolutions(in String cameraId);
+    @nullable List<SizeList> getSupportedPostviewResolutions(in Size captureSize);
     ISessionProcessorImpl getSessionProcessor();
     CameraMetadataNative getAvailableCaptureRequestKeys(in String cameraId);
     CameraMetadataNative getAvailableCaptureResultKeys(in String cameraId);
     boolean isCaptureProcessProgressAvailable();
+    boolean isPostviewAvailable();
 }
diff --git a/core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl
index 3c5f5ff..f83d407 100644
--- a/core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl
@@ -24,7 +24,9 @@
 interface ICaptureProcessorImpl
 {
     void onOutputSurface(in Surface surface, int imageFormat);
-    void onResolutionUpdate(in Size size);
+    void onPostviewOutputSurface(in Surface surface);
+    void onResolutionUpdate(in Size size, in Size postviewSize);
     void onImageFormatUpdate(int imageFormat);
-    void process(in List<CaptureBundle> capturelist, in IProcessResultImpl resultCallback);
+    void process(in List<CaptureBundle> capturelist,
+            in IProcessResultImpl resultCallback, boolean isPostviewRequested);
 }
diff --git a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
index 360f809..754f8f6 100644
--- a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
@@ -40,9 +40,11 @@
     @nullable List<CaptureStageImpl> getCaptureStages();
     int getMaxCaptureStage();
     @nullable List<SizeList> getSupportedResolutions();
+    @nullable List<SizeList> getSupportedPostviewResolutions(in Size captureSize);
     LatencyRange getEstimatedCaptureLatencyRange(in Size outputSize);
     CameraMetadataNative getAvailableCaptureRequestKeys();
     CameraMetadataNative getAvailableCaptureResultKeys();
     boolean isCaptureProcessProgressAvailable();
     @nullable LatencyPair getRealtimeCaptureLatency();
+    boolean isPostviewAvailable();
 }
diff --git a/core/java/android/hardware/camera2/extension/IOutputSurfaceConfiguration.aidl b/core/java/android/hardware/camera2/extension/IOutputSurfaceConfiguration.aidl
index 70b096f..ab650c7 100644
--- a/core/java/android/hardware/camera2/extension/IOutputSurfaceConfiguration.aidl
+++ b/core/java/android/hardware/camera2/extension/IOutputSurfaceConfiguration.aidl
@@ -23,4 +23,5 @@
     OutputSurface getPreviewOutputSurface();
     OutputSurface getImageCaptureOutputSurface();
     OutputSurface getImageAnalysisOutputSurface();
+    OutputSurface getPostviewOutputSurface();
 }
diff --git a/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
index e0f1b64..2af1df9 100644
--- a/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
@@ -27,13 +27,13 @@
 interface ISessionProcessorImpl
 {
     CameraSessionConfig initSession(in String cameraId, in OutputSurface previewSurface,
-            in OutputSurface imageCaptureSurface);
+            in OutputSurface imageCaptureSurface, in OutputSurface postviewSurface);
     void deInitSession();
     void onCaptureSessionStart(IRequestProcessorImpl requestProcessor);
     void onCaptureSessionEnd();
     int startRepeating(in ICaptureCallback callback);
     void stopRepeating();
-    int startCapture(in ICaptureCallback callback);
+    int startCapture(in ICaptureCallback callback, in boolean isPostviewRequested);
     void setParameters(in CaptureRequest captureRequest);
     int startTrigger(in CaptureRequest captureRequest, in ICaptureCallback callback);
     @nullable LatencyPair getRealtimeCaptureLatency();
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index 42c4411..2d591c3 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -69,6 +69,7 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -93,6 +94,7 @@
 
     private Surface mClientRepeatingRequestSurface;
     private Surface mClientCaptureSurface;
+    private Surface mClientPostviewSurface;
     private CameraCaptureSession mCaptureSession = null;
     private ISessionProcessorImpl mSessionProcessor = null;
     private final InitializeSessionHandler mInitializeHandler;
@@ -173,13 +175,37 @@
             throw new IllegalArgumentException("One or more unsupported output surfaces found!");
         }
 
+        Surface postviewSurface = null;
+        if (burstCaptureSurface != null) {
+            CameraExtensionUtils.SurfaceInfo burstCaptureSurfaceInfo =
+                    CameraExtensionUtils.querySurface(burstCaptureSurface);
+            Size burstCaptureSurfaceSize =
+                    new Size(burstCaptureSurfaceInfo.mWidth, burstCaptureSurfaceInfo.mHeight);
+            HashMap<Integer, List<Size>> supportedPostviewSizes = new HashMap<>();
+            for (int format : CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
+                List<Size> supportedSizesPostview = extensionChars.getPostviewSupportedSizes(
+                        config.getExtension(), burstCaptureSurfaceSize, format);
+                if (supportedSizesPostview != null) {
+                    supportedPostviewSizes.put(format, supportedSizesPostview);
+                }
+            }
+
+            postviewSurface = CameraExtensionUtils.getPostviewSurface(
+                        config.getPostviewOutputConfiguration(), supportedPostviewSizes,
+                        burstCaptureSurfaceInfo.mFormat);
+
+            if ((config.getPostviewOutputConfiguration() != null) && (postviewSurface == null)) {
+                throw new IllegalArgumentException("Unsupported output surface for postview!");
+            }
+        }
+
         IAdvancedExtenderImpl extender = CameraExtensionCharacteristics.initializeAdvancedExtension(
                 config.getExtension());
         extender.init(cameraId);
 
         CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(clientId,
                 extender, cameraDevice, repeatingRequestSurface, burstCaptureSurface,
-                config.getStateCallback(), config.getExecutor(), sessionId);
+                postviewSurface, config.getStateCallback(), config.getExecutor(), sessionId);
         ret.initialize();
 
         return ret;
@@ -188,6 +214,7 @@
     private CameraAdvancedExtensionSessionImpl(long extensionClientId,
             @NonNull IAdvancedExtenderImpl extender, @NonNull CameraDevice cameraDevice,
             @Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface,
+            @Nullable Surface postviewSurface,
             @NonNull CameraExtensionSession.StateCallback callback, @NonNull Executor executor,
             int sessionId) {
         mExtensionClientId = extensionClientId;
@@ -197,6 +224,7 @@
         mExecutor = executor;
         mClientRepeatingRequestSurface = repeatingRequestSurface;
         mClientCaptureSurface = burstCaptureSurface;
+        mClientPostviewSurface = postviewSurface;
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
@@ -216,9 +244,11 @@
 
         OutputSurface previewSurface = initializeParcelable(mClientRepeatingRequestSurface);
         OutputSurface captureSurface = initializeParcelable(mClientCaptureSurface);
+        OutputSurface postviewSurface = initializeParcelable(mClientPostviewSurface);
+
         mSessionProcessor = mAdvancedExtender.getSessionProcessor();
         CameraSessionConfig sessionConfig = mSessionProcessor.initSession(mCameraDevice.getId(),
-                previewSurface, captureSurface);
+                previewSurface, captureSurface, postviewSurface);
         List<CameraOutputConfig> outputConfigs = sessionConfig.outputConfigs;
         ArrayList<OutputConfiguration> outputList = new ArrayList<>();
         for (CameraOutputConfig output : outputConfigs) {
@@ -395,17 +425,15 @@
                 throw new IllegalStateException("Uninitialized component");
             }
 
-            if (request.getTargets().size() != 1) {
-                throw new IllegalArgumentException("Single capture to both preview & still"  +
-                        " capture outputs target is not supported!");
-            }
+            validateCaptureRequestTargets(request);
 
             if ((mClientCaptureSurface != null)  && request.containsTarget(mClientCaptureSurface)) {
                 try {
+                    boolean isPostviewRequested = request.containsTarget(mClientPostviewSurface);
                     mSessionProcessor.setParameters(request);
 
                     seqId = mSessionProcessor.startCapture(new RequestCallbackHandler(request,
-                            executor, listener));
+                            executor, listener), isPostviewRequested);
                 } catch (RemoteException e) {
                     throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, "Failed " +
                             " to submit capture request, extension service failed to respond!");
@@ -427,6 +455,27 @@
         return seqId;
     }
 
+    private void validateCaptureRequestTargets(@NonNull CaptureRequest request) {
+        if ((request.getTargets().size() == 1) &&
+                (!request.containsTarget(mClientRepeatingRequestSurface) ||
+                !request.containsTarget(mClientCaptureSurface))) {
+            throw new IllegalArgumentException("Target output combination requested is " +
+                    "not supported!");
+        }
+
+        if ((request.getTargets().size() == 2) &&
+                (!request.getTargets().containsAll(Arrays.asList(mClientCaptureSurface,
+                mClientPostviewSurface)))) {
+            throw new IllegalArgumentException("Target output combination requested is " +
+                    "not supported!");
+        }
+
+        if (request.getTargets().size() > 2) {
+            throw new IllegalArgumentException("Target output combination requested is " +
+                    "not supported!");
+        }
+    }
+
     @Override
     public void stopRepeating() throws CameraAccessException {
         synchronized (mInterfaceLock) {
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
index aee20db..7701125 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
@@ -52,10 +52,14 @@
     private final ICaptureProcessorImpl mProcessor;
 
     private ImageReader mYuvReader = null;
+    private ImageReader mPostviewYuvReader = null;
     private android.hardware.camera2.extension.Size mResolution = null;
+    private android.hardware.camera2.extension.Size mPostviewResolution = null;
     private int mFormat = -1;
     private Surface mOutputSurface = null;
     private ImageWriter mOutputWriter = null;
+    private Surface mPostviewOutputSurface = null;
+    private ImageWriter mPostviewOutputWriter = null;
 
     private static final class JpegParameters {
         public HashSet<Long> mTimeStamps = new HashSet<>();
@@ -185,12 +189,13 @@
             int rot90);
 
     @Override
-    public void process(List<CaptureBundle> captureBundle, IProcessResultImpl captureCallback)
+    public void process(List<CaptureBundle> captureBundle, IProcessResultImpl captureCallback,
+            boolean isPostviewRequested)
             throws RemoteException {
         JpegParameters jpegParams = getJpegParameters(captureBundle);
         try {
             mJpegParameters.add(jpegParams);
-            mProcessor.process(captureBundle, captureCallback);
+            mProcessor.process(captureBundle, captureCallback, isPostviewRequested);
         } catch (Exception e) {
             mJpegParameters.remove(jpegParams);
             throw e;
@@ -206,10 +211,23 @@
         initializePipeline();
     }
 
+    public void onPostviewOutputSurface(Surface surface) throws RemoteException {
+        CameraExtensionUtils.SurfaceInfo postviewSurfaceInfo =
+                CameraExtensionUtils.querySurface(surface);
+        if (postviewSurfaceInfo.mFormat != ImageFormat.JPEG) {
+            Log.e(TAG, "Unsupported output format: " + postviewSurfaceInfo.mFormat);
+            return;
+        }
+        mPostviewOutputSurface = surface;
+        initializePostviewPipeline();
+    }
+
     @Override
-    public void onResolutionUpdate(android.hardware.camera2.extension.Size size)
+    public void onResolutionUpdate(android.hardware.camera2.extension.Size size,
+            android.hardware.camera2.extension.Size postviewSize)
             throws RemoteException {
         mResolution = size;
+        mPostviewResolution = postviewSize;
         initializePipeline();
     }
 
@@ -230,9 +248,26 @@
                     ImageFormat.JPEG, mResolution.width * mResolution.height, 1);
             mYuvReader = ImageReader.newInstance(mResolution.width, mResolution.height, mFormat,
                     JPEG_QUEUE_SIZE);
-            mYuvReader.setOnImageAvailableListener(new YuvCallback(), mHandler);
+            mYuvReader.setOnImageAvailableListener(
+                    new YuvCallback(mYuvReader, mOutputWriter), mHandler);
             mProcessor.onOutputSurface(mYuvReader.getSurface(), mFormat);
-            mProcessor.onResolutionUpdate(mResolution);
+            mProcessor.onResolutionUpdate(mResolution, mPostviewResolution);
+            mProcessor.onImageFormatUpdate(mFormat);
+        }
+    }
+
+    private void initializePostviewPipeline() throws RemoteException {
+        if ((mFormat != -1) && (mPostviewOutputSurface != null) && (mPostviewResolution != null)
+                && (mPostviewYuvReader == null)) {
+            // Jpeg/blobs are expected to be configured with (w*h)x1
+            mPostviewOutputWriter = ImageWriter.newInstance(mPostviewOutputSurface, 1/*maxImages*/,
+                    ImageFormat.JPEG, mPostviewResolution.width * mPostviewResolution.height, 1);
+            mPostviewYuvReader = ImageReader.newInstance(mPostviewResolution.width,
+                    mPostviewResolution.height, mFormat, JPEG_QUEUE_SIZE);
+            mPostviewYuvReader.setOnImageAvailableListener(
+                    new YuvCallback(mPostviewYuvReader, mPostviewOutputWriter), mHandler);
+            mProcessor.onPostviewOutputSurface(mPostviewYuvReader.getSurface());
+            mProcessor.onResolutionUpdate(mResolution, mPostviewResolution);
             mProcessor.onImageFormatUpdate(mFormat);
         }
     }
@@ -243,13 +278,21 @@
     }
 
     private class YuvCallback implements ImageReader.OnImageAvailableListener {
+        private ImageReader mImageReader;
+        private ImageWriter mImageWriter;
+
+        public YuvCallback(ImageReader imageReader, ImageWriter imageWriter) {
+            mImageReader = imageReader;
+            mImageWriter = imageWriter;
+        }
+
         @Override
         public void onImageAvailable(ImageReader reader) {
             Image yuvImage = null;
             Image jpegImage = null;
             try {
-                yuvImage = mYuvReader.acquireNextImage();
-                jpegImage = mOutputWriter.dequeueInputImage();
+                yuvImage = mImageReader.acquireNextImage();
+                jpegImage = mImageWriter.dequeueInputImage();
             } catch (IllegalStateException e) {
                 if (yuvImage != null) {
                     yuvImage.close();
@@ -270,7 +313,9 @@
             Plane crPlane = yuvImage.getPlanes()[1];
             Plane cbPlane = yuvImage.getPlanes()[2];
 
-            Iterator<JpegParameters> jpegIter = mJpegParameters.iterator();
+            ConcurrentLinkedQueue<JpegParameters> jpegParameters =
+                    new ConcurrentLinkedQueue(mJpegParameters);
+            Iterator<JpegParameters> jpegIter = jpegParameters.iterator();
             JpegParameters jpegParams = null;
             while(jpegIter.hasNext()) {
                 JpegParameters currentParams = jpegIter.next();
@@ -281,7 +326,7 @@
                 }
             }
             if (jpegParams == null) {
-                if (mJpegParameters.isEmpty()) {
+                if (jpegParameters.isEmpty()) {
                     Log.w(TAG, "Empty jpeg settings queue! Using default jpeg orientation"
                             + " and quality!");
                     jpegParams = new JpegParameters();
@@ -291,7 +336,7 @@
                     Log.w(TAG, "No jpeg settings found with matching timestamp for current"
                             + " processed input!");
                     Log.w(TAG, "Using values from the top of the queue!");
-                    jpegParams = mJpegParameters.poll();
+                    jpegParams = jpegParameters.poll();
                 }
             }
 
@@ -307,7 +352,7 @@
             yuvImage.close();
 
             try {
-                mOutputWriter.queueInputImage(jpegImage);
+                mImageWriter.queueInputImage(jpegImage);
             } catch (IllegalStateException e) {
                 Log.e(TAG, "Failed to queue encoded result!");
             } finally {
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 259bd7b..5b84ec0 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -67,6 +67,7 @@
 import java.io.Closeable;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -95,6 +96,7 @@
     private CameraCaptureSession mCaptureSession = null;
     private Surface mCameraRepeatingSurface, mClientRepeatingRequestSurface;
     private Surface mCameraBurstSurface, mClientCaptureSurface;
+    private Surface mClientPostviewSurface;
     private ImageReader mRepeatingRequestImageReader = null;
     private ImageReader mBurstCaptureImageReader = null;
     private ImageReader mStubCaptureImageReader = null;
@@ -197,6 +199,30 @@
             throw new IllegalArgumentException("One or more unsupported output surfaces found!");
         }
 
+        Surface postviewSurface = null;
+        if (burstCaptureSurface != null) {
+            CameraExtensionUtils.SurfaceInfo burstCaptureSurfaceInfo =
+                    CameraExtensionUtils.querySurface(burstCaptureSurface);
+            Size burstCaptureSurfaceSize =
+                    new Size(burstCaptureSurfaceInfo.mWidth, burstCaptureSurfaceInfo.mHeight);
+            HashMap<Integer, List<Size>> supportedPostviewSizes = new HashMap<>();
+            for (int format : CameraExtensionUtils.SUPPORTED_CAPTURE_OUTPUT_FORMATS) {
+                List<Size> supportedSizesPostview = extensionChars.getPostviewSupportedSizes(
+                        config.getExtension(), burstCaptureSurfaceSize, format);
+                if (supportedSizesPostview != null) {
+                    supportedPostviewSizes.put(format, supportedSizesPostview);
+                }
+            }
+
+            postviewSurface = CameraExtensionUtils.getPostviewSurface(
+                        config.getPostviewOutputConfiguration(), supportedPostviewSizes,
+                        burstCaptureSurfaceInfo.mFormat);
+
+            if ((config.getPostviewOutputConfiguration() != null) && (postviewSurface == null)) {
+                throw new IllegalArgumentException("Unsupported output surface for postview!");
+            }
+        }
+
         extenders.first.init(cameraId, chars.getNativeMetadata());
         extenders.first.onInit(cameraId, chars.getNativeMetadata());
         extenders.second.init(cameraId, chars.getNativeMetadata());
@@ -210,6 +236,7 @@
                 cameraDevice,
                 repeatingRequestSurface,
                 burstCaptureSurface,
+                postviewSurface,
                 config.getStateCallback(),
                 config.getExecutor(),
                 sessionId,
@@ -228,6 +255,7 @@
             @NonNull CameraDevice cameraDevice,
             @Nullable Surface repeatingRequestSurface,
             @Nullable Surface burstCaptureSurface,
+            @Nullable Surface postviewSurface,
             @NonNull StateCallback callback,
             @NonNull Executor executor,
             int sessionId,
@@ -241,6 +269,7 @@
         mExecutor = executor;
         mClientRepeatingRequestSurface = repeatingRequestSurface;
         mClientCaptureSurface = burstCaptureSurface;
+        mClientPostviewSurface = postviewSurface;
         mSupportedPreviewSizes = previewSizes;
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
@@ -365,7 +394,19 @@
                     new android.hardware.camera2.extension.Size();
             sz.width = mBurstCaptureImageReader.getWidth();
             sz.height = mBurstCaptureImageReader.getHeight();
-            mImageProcessor.onResolutionUpdate(sz);
+
+            if (mClientPostviewSurface != null) {
+                CameraExtensionUtils.SurfaceInfo postviewSurfaceInfo =
+                        CameraExtensionUtils.querySurface(mClientPostviewSurface);
+                android.hardware.camera2.extension.Size postviewSize =
+                        new android.hardware.camera2.extension.Size();
+                postviewSize.width = postviewSurfaceInfo.mWidth;
+                postviewSize.height = postviewSurfaceInfo.mHeight;
+                mImageProcessor.onResolutionUpdate(sz, postviewSize);
+            } else {
+                mImageProcessor.onResolutionUpdate(sz, null);
+            }
+
             mImageProcessor.onImageFormatUpdate(mBurstCaptureImageReader.getImageFormat());
         } else {
             if (mClientCaptureSurface != null) {
@@ -402,6 +443,10 @@
             }
         }
         if ((mImageProcessor != null) && (mClientCaptureSurface != null)) {
+            if (mClientPostviewSurface != null) {
+                mImageProcessor.onPostviewOutputSurface(mClientPostviewSurface);
+            }
+
             CameraExtensionUtils.SurfaceInfo surfaceInfo = CameraExtensionUtils.querySurface(
                     mClientCaptureSurface);
             mImageProcessor.onOutputSurface(mClientCaptureSurface, surfaceInfo.mFormat);
@@ -621,10 +666,7 @@
             throw new IllegalStateException("Uninitialized component");
         }
 
-        if (request.getTargets().size() != 1) {
-            throw new IllegalArgumentException("Single capture to both preview & still capture " +
-                    "outputs target is not supported!");
-        }
+        validateCaptureRequestTargets(request);
 
         int seqId = -1;
         if ((mClientCaptureSurface != null) && request.containsTarget(mClientCaptureSurface)) {
@@ -674,6 +716,27 @@
         return seqId;
     }
 
+    private void validateCaptureRequestTargets(@NonNull CaptureRequest request) {
+        if ((request.getTargets().size() == 1) &&
+                (!request.containsTarget(mClientRepeatingRequestSurface) ||
+                !request.containsTarget(mClientCaptureSurface))) {
+            throw new IllegalArgumentException("Target output combination requested is " +
+                    "not supported!");
+        }
+
+        if ((request.getTargets().size() == 2) &&
+                (!request.getTargets().containsAll(Arrays.asList(mClientCaptureSurface,
+                mClientPostviewSurface)))) {
+            throw new IllegalArgumentException("Target output combination requested is " +
+                    "not supported!");
+        }
+
+        if (request.getTargets().size() > 2) {
+            throw new IllegalArgumentException("Target output combination requested is " +
+                    "not supported!");
+        }
+    }
+
     @Override
     public void stopRepeating() throws CameraAccessException {
         synchronized (mInterfaceLock) {
@@ -814,6 +877,7 @@
             mImageProcessor = null;
             mCameraRepeatingSurface = mClientRepeatingRequestSurface = null;
             mCameraBurstSurface = mClientCaptureSurface = null;
+            mClientPostviewSurface = null;
         }
 
         if (notifyClose && !skipCloseNotification) {
@@ -1123,7 +1187,10 @@
                 List<CaptureBundle> captureList = initializeParcelable(mCaptureStageMap,
                         jpegOrientation, jpegQuality);
                 try {
-                    mImageProcessor.process(captureList, mCaptureResultHandler);
+                    boolean isPostviewRequested =
+                            mClientRequest.containsTarget(mClientPostviewSurface);
+                    mImageProcessor.process(captureList, mCaptureResultHandler,
+                            isPostviewRequested);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Failed to process multi-frame request! Extension service "
                             + "does not respond!");
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
index afefcbe..5222408 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionUtils.java
@@ -94,6 +94,31 @@
         return surfaceInfo;
     }
 
+    public static @Nullable Surface getPostviewSurface(
+            @Nullable OutputConfiguration outputConfig,
+            @NonNull HashMap<Integer, List<Size>> supportedPostviewSizes,
+            @NonNull int captureFormat) {
+        if (outputConfig == null) return null;
+
+        SurfaceInfo surfaceInfo = querySurface(outputConfig.getSurface());
+        if (surfaceInfo.mFormat == captureFormat) {
+            if (supportedPostviewSizes.containsKey(captureFormat)) {
+                Size postviewSize = new Size(surfaceInfo.mWidth, surfaceInfo.mHeight);
+                if (supportedPostviewSizes.get(surfaceInfo.mFormat)
+                        .contains(postviewSize)) {
+                    return outputConfig.getSurface();
+                } else {
+                    throw new IllegalArgumentException("Postview size not supported!");
+                }
+            }
+        } else {
+            throw new IllegalArgumentException("Postview format should be equivalent to " +
+                    " the capture format!");
+        }
+
+        return null;
+    }
+
     public static Surface getBurstCaptureSurface(
             @NonNull List<OutputConfiguration> outputConfigs,
             @NonNull HashMap<Integer, List<Size>> supportedCaptureSizes) {
diff --git a/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java b/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java
index c81d339..0e6c1b3 100644
--- a/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/ExtensionSessionConfiguration.java
@@ -16,6 +16,7 @@
 package android.hardware.camera2.params;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 
 import android.hardware.camera2.CameraExtensionCharacteristics.Extension;
 import android.hardware.camera2.CameraExtensionSession;
@@ -32,6 +33,7 @@
 
     private int mExtensionType;
     private List<OutputConfiguration> mOutputs;
+    private OutputConfiguration mPostviewOutput = null;
     private Executor mExecutor = null;
     private CameraExtensionSession.StateCallback mCallback = null;
 
@@ -64,6 +66,28 @@
     }
 
     /**
+     * Set the postview for still capture output configuration.
+     *
+     * @param postviewOutput output configuration for postview
+     * @see android.hardware.camera2.CameraExtensionCharacteristics#isPostviewAvailable
+     */
+    public
+    void setPostviewOutputConfiguration(@Nullable OutputConfiguration postviewOutput) {
+        mPostviewOutput = postviewOutput;
+    }
+
+    /**
+     * Get the postview for still capture output configuration.
+     *
+     * @return output configuration for postview
+     * @see android.hardware.camera2.CameraExtensionCharacteristics#isPostviewAvailable
+     */
+    public @Nullable // Postview output is optional
+    OutputConfiguration getPostviewOutputConfiguration() {
+        return mPostviewOutput;
+    }
+
+    /**
      * Retrieve the {@link OutputConfiguration} list for the capture
      * session.
      *
diff --git a/core/java/android/hardware/input/VirtualKeyboardConfig.java b/core/java/android/hardware/input/VirtualKeyboardConfig.java
index 28d8a0f..d788df4 100644
--- a/core/java/android/hardware/input/VirtualKeyboardConfig.java
+++ b/core/java/android/hardware/input/VirtualKeyboardConfig.java
@@ -124,6 +124,10 @@
          * Note that the preferred layout is not guaranteed. If the specified language is
          * well-formed but not supported, the keyboard will be using English US QWERTY layout.
          *
+         * In case where the owning Virtual Device has created multiple virtual keyboards, only the
+         * {@code languageTag} of the most recent virtual keyboard will be kept to hint the locale
+         * of the Virtual Device.
+         *
          *  @throws IllegalArgumentException if either of the language or country is not present in
          *  the language tag.
          */
diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
index e0f9cad..cdd67b7 100644
--- a/core/java/android/hardware/usb/UsbPort.java
+++ b/core/java/android/hardware/usb/UsbPort.java
@@ -46,6 +46,8 @@
 import static android.hardware.usb.UsbPortStatus.DATA_STATUS_DISABLED_DOCK;
 import static android.hardware.usb.UsbPortStatus.DATA_STATUS_DISABLED_FORCE;
 import static android.hardware.usb.UsbPortStatus.DATA_STATUS_DISABLED_DEBUG;
+import static android.hardware.usb.UsbPortStatus.DATA_STATUS_DISABLED_DOCK_HOST_MODE;
+import static android.hardware.usb.UsbPortStatus.DATA_STATUS_DISABLED_DOCK_DEVICE_MODE;
 import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_DEBUG_ACCESSORY;
 import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_BC_1_2;
 import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_MISSING_RP;
@@ -676,6 +678,15 @@
             statusString.append("disabled-debug, ");
         }
 
+        if ((usbDataStatus & DATA_STATUS_DISABLED_DOCK_HOST_MODE) ==
+            DATA_STATUS_DISABLED_DOCK_HOST_MODE) {
+            statusString.append("disabled-host-dock, ");
+        }
+
+        if ((usbDataStatus & DATA_STATUS_DISABLED_DOCK_DEVICE_MODE) ==
+            DATA_STATUS_DISABLED_DOCK_DEVICE_MODE) {
+            statusString.append("disabled-device-dock, ");
+        }
         return statusString.toString().replaceAll(", $", "");
     }
 
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index ed3e40d..e61703d 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -219,7 +219,11 @@
     public static final int DATA_STATUS_DISABLED_CONTAMINANT = 1 << 2;
 
     /**
-     * USB data is disabled due to docking event.
+     * This flag indicates that some or all data modes are disabled
+     * due to docking event, and the specific sub-statuses viz.,
+     * {@link #DATA_STATUS_DISABLED_DOCK_HOST_MODE},
+     * {@link #DATA_STATUS_DISABLED_DOCK_DEVICE_MODE}
+     * can be checked for individual modes.
      */
     public static final int DATA_STATUS_DISABLED_DOCK = 1 << 3;
 
@@ -235,6 +239,18 @@
     public static final int DATA_STATUS_DISABLED_DEBUG = 1 << 5;
 
     /**
+     * USB host mode is disabled due to docking event.
+     * {@link #DATA_STATUS_DISABLED_DOCK} will be set as well.
+     */
+    public static final int DATA_STATUS_DISABLED_DOCK_HOST_MODE = 1 << 6;
+
+    /**
+     * USB device mode is disabled due to docking event.
+     * {@link #DATA_STATUS_DISABLED_DOCK} will be set as well.
+     */
+    public static final int DATA_STATUS_DISABLED_DOCK_DEVICE_MODE = 1 << 7;
+
+    /**
      * Unknown whether a power brick is connected.
      */
     public static final int POWER_BRICK_STATUS_UNKNOWN = 0;
@@ -329,6 +345,8 @@
             DATA_STATUS_DISABLED_OVERHEAT,
             DATA_STATUS_DISABLED_CONTAMINANT,
             DATA_STATUS_DISABLED_DOCK,
+            DATA_STATUS_DISABLED_DOCK_HOST_MODE,
+            DATA_STATUS_DISABLED_DOCK_DEVICE_MODE,
             DATA_STATUS_DISABLED_FORCE,
             DATA_STATUS_DISABLED_DEBUG
     })
@@ -357,6 +375,20 @@
         mSupportedRoleCombinations = supportedRoleCombinations;
         mContaminantProtectionStatus = contaminantProtectionStatus;
         mContaminantDetectionStatus = contaminantDetectionStatus;
+
+        // Older implementations that only set the DISABLED_DOCK_MODE will have the other two
+        // set at the HAL interface level, so the "dock mode only" state shouldn't be visible here.
+        // But the semantics are ensured here.
+        int disabledDockModes = (usbDataStatus &
+            (DATA_STATUS_DISABLED_DOCK_HOST_MODE | DATA_STATUS_DISABLED_DOCK_DEVICE_MODE));
+        if (disabledDockModes != 0) {
+            // Set DATA_STATUS_DISABLED_DOCK when one of DATA_STATUS_DISABLED_DOCK_*_MODE is set
+            usbDataStatus |= DATA_STATUS_DISABLED_DOCK;
+        } else {
+            // Clear DATA_STATUS_DISABLED_DOCK when none of DATA_STATUS_DISABLED_DOCK_*_MODE is set
+            usbDataStatus &= ~DATA_STATUS_DISABLED_DOCK;
+        }
+
         mUsbDataStatus = usbDataStatus;
         mPowerTransferLimited = powerTransferLimited;
         mPowerBrickConnectionStatus = powerBrickConnectionStatus;
@@ -472,7 +504,8 @@
      *         {@link #DATA_STATUS_UNKNOWN}, {@link #DATA_STATUS_ENABLED},
      *         {@link #DATA_STATUS_DISABLED_OVERHEAT}, {@link #DATA_STATUS_DISABLED_CONTAMINANT},
      *         {@link #DATA_STATUS_DISABLED_DOCK}, {@link #DATA_STATUS_DISABLED_FORCE},
-     *         {@link #DATA_STATUS_DISABLED_DEBUG}
+     *         {@link #DATA_STATUS_DISABLED_DEBUG}, {@link #DATA_STATUS_DISABLED_DOCK_HOST_MODE},
+     *         {@link #DATA_STATUS_DISABLED_DOCK_DEVICE_MODE}
      */
     public @UsbDataStatus int getUsbDataStatus() {
         return mUsbDataStatus;
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index de2dec7..0ba8d51 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -132,7 +132,7 @@
     @IntDef(prefix = {"POWER_MODEL_"}, value = {
             POWER_MODEL_UNDEFINED,
             POWER_MODEL_POWER_PROFILE,
-            POWER_MODEL_MEASURED_ENERGY,
+            POWER_MODEL_ENERGY_CONSUMPTION,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PowerModel {
@@ -150,9 +150,9 @@
     public static final int POWER_MODEL_POWER_PROFILE = 1;
 
     /**
-     * Power model that is based on energy consumption measured by on-device power monitors.
+     * Power model that is based on energy consumption stats provided by PowerStats HAL.
      */
-    public static final int POWER_MODEL_MEASURED_ENERGY = 2;
+    public static final int POWER_MODEL_ENERGY_CONSUMPTION = 2;
 
     /**
      * Identifiers of consumed power aggregations.
@@ -469,8 +469,8 @@
      */
     public static String powerModelToString(@BatteryConsumer.PowerModel int powerModel) {
         switch (powerModel) {
-            case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY:
-                return "measured energy";
+            case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION:
+                return "energy consumption";
             case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
                 return "power profile";
             default:
@@ -484,7 +484,7 @@
      */
     public static int powerModelToProtoEnum(@BatteryConsumer.PowerModel int powerModel) {
         switch (powerModel) {
-            case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY:
+            case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION:
                 return BatteryUsageStatsAtomsProto.PowerComponentModel.MEASURED_ENERGY;
             case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
                 return BatteryUsageStatsAtomsProto.PowerComponentModel.POWER_PROFILE;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 2a4c861..34aa7ef 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1043,12 +1043,13 @@
 
         /**
          * Returns the battery consumption (in microcoulombs) of bluetooth for this uid,
-         * derived from on device power measurement data.
+         * derived from {@link android.hardware.power.stats.EnergyConsumerType#BLUETOOTH} bucket
+         * provided by the PowerStats service.
          * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
          *
          * {@hide}
          */
-        public abstract long getBluetoothMeasuredBatteryConsumptionUC();
+        public abstract long getBluetoothEnergyConsumptionUC();
 
         /**
          * Returns the battery consumption (in microcoulombs) of the uid's bluetooth usage
@@ -1057,17 +1058,18 @@
          *
          * {@hide}
          */
-        public abstract long getBluetoothMeasuredBatteryConsumptionUC(
+        public abstract long getBluetoothEnergyConsumptionUC(
                 @BatteryConsumer.ProcessState int processState);
 
         /**
          * Returns the battery consumption (in microcoulombs) of the uid's cpu usage, derived from
-         * on device power measurement data.
+         * derived from {@link android.hardware.power.stats.EnergyConsumerType#CPU} bucket
+         * provided by the PowerStats service.
          * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
          *
          * {@hide}
          */
-        public abstract long getCpuMeasuredBatteryConsumptionUC();
+        public abstract long getCpuEnergyConsumptionUC();
 
         /**
          * Returns the battery consumption (in microcoulombs) of the uid's cpu usage when in the
@@ -1076,26 +1078,28 @@
          *
          * {@hide}
          */
-        public abstract long getCpuMeasuredBatteryConsumptionUC(
+        public abstract long getCpuEnergyConsumptionUC(
                 @BatteryConsumer.ProcessState int processState);
 
         /**
          * Returns the battery consumption (in microcoulombs) of the uid's GNSS usage, derived from
-         * on device power measurement data.
+         * derived from {@link android.hardware.power.stats.EnergyConsumerType#GNSS} bucket
+         * provided by the PowerStats service.
          * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
          *
          * {@hide}
          */
-        public abstract long getGnssMeasuredBatteryConsumptionUC();
+        public abstract long getGnssEnergyConsumptionUC();
 
         /**
          * Returns the battery consumption (in microcoulombs) of the uid's radio usage, derived from
-         * on device power measurement data.
+         * derived from {@link android.hardware.power.stats.EnergyConsumerType#MOBILE_RADIO}
+         * bucket provided by the PowerStats service.
          * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
          *
          * {@hide}
          */
-        public abstract long getMobileRadioMeasuredBatteryConsumptionUC();
+        public abstract long getMobileRadioEnergyConsumptionUC();
 
         /**
          * Returns the battery consumption (in microcoulombs) of the uid's radio usage when in the
@@ -1104,26 +1108,28 @@
          *
          * {@hide}
          */
-        public abstract long getMobileRadioMeasuredBatteryConsumptionUC(
+        public abstract long getMobileRadioEnergyConsumptionUC(
                 @BatteryConsumer.ProcessState int processState);
 
         /**
          * Returns the battery consumption (in microcoulombs) of the screen while on and uid active,
-         * derived from on device power measurement data.
+         * derived from {@link android.hardware.power.stats.EnergyConsumerType#DISPLAY} bucket
+         * provided by the PowerStats service.
          * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
          *
          * {@hide}
          */
-        public abstract long getScreenOnMeasuredBatteryConsumptionUC();
+        public abstract long getScreenOnEnergyConsumptionUC();
 
         /**
          * Returns the battery consumption (in microcoulombs) of wifi for this uid,
-         * derived from on device power measurement data.
+         * derived from {@link android.hardware.power.stats.EnergyConsumerType#WIFI} bucket
+         * provided by the PowerStats service.
          * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
          *
          * {@hide}
          */
-        public abstract long getWifiMeasuredBatteryConsumptionUC();
+        public abstract long getWifiEnergyConsumptionUC();
 
         /**
          * Returns the battery consumption (in microcoulombs) of the uid's wifi usage when in the
@@ -1132,7 +1138,7 @@
          *
          * {@hide}
          */
-        public abstract long getWifiMeasuredBatteryConsumptionUC(
+        public abstract long getWifiEnergyConsumptionUC(
                 @BatteryConsumer.ProcessState int processState);
 
 
@@ -1147,7 +1153,7 @@
          *
          * {@hide}
          */
-        public abstract @Nullable long[] getCustomConsumerMeasuredBatteryConsumptionUC();
+        public abstract @Nullable long[] getCustomEnergyConsumerBatteryConsumptionUC();
 
         public static abstract class Sensor {
 
@@ -1776,7 +1782,7 @@
     /**
      * Measured energy delta from the previous reading.
      */
-    public static final class MeasuredEnergyDetails {
+    public static final class EnergyConsumerDetails {
         /**
          * Description of the energy consumer, such as CPU, DISPLAY etc
          */
@@ -1986,8 +1992,8 @@
         // Non-null when there is more detailed information at this step.
         public HistoryStepDetails stepDetails;
 
-        // Non-null when there is measured energy information
-        public MeasuredEnergyDetails measuredEnergyDetails;
+        // Non-null when there is energy consumer information
+        public EnergyConsumerDetails energyConsumerDetails;
 
         // Non-null when there is CPU usage information
         public CpuUsageDetails cpuUsageDetails;
@@ -2200,7 +2206,7 @@
             eventCode = EVENT_NONE;
             eventTag = null;
             tagsFirstOccurrence = false;
-            measuredEnergyDetails = null;
+            energyConsumerDetails = null;
             cpuUsageDetails = null;
         }
 
@@ -2251,7 +2257,7 @@
             }
             tagsFirstOccurrence = o.tagsFirstOccurrence;
             currentTime = o.currentTime;
-            measuredEnergyDetails = o.measuredEnergyDetails;
+            energyConsumerDetails = o.energyConsumerDetails;
             cpuUsageDetails = o.cpuUsageDetails;
         }
 
@@ -2858,7 +2864,7 @@
      *
      * {@hide}
      */
-    public abstract long getBluetoothMeasuredBatteryConsumptionUC();
+    public abstract long getBluetoothEnergyConsumptionUC();
 
     /**
      * Returns the battery consumption (in microcoulombs) of the cpu, derived from on device power
@@ -2867,7 +2873,7 @@
      *
      * {@hide}
      */
-    public abstract long getCpuMeasuredBatteryConsumptionUC();
+    public abstract long getCpuEnergyConsumptionUC();
 
     /**
      * Returns the battery consumption (in microcoulombs) of the GNSS, derived from on device power
@@ -2876,7 +2882,7 @@
      *
      * {@hide}
      */
-    public abstract long getGnssMeasuredBatteryConsumptionUC();
+    public abstract long getGnssEnergyConsumptionUC();
 
     /**
      * Returns the battery consumption (in microcoulombs) of the radio, derived from on device power
@@ -2885,7 +2891,7 @@
      *
      * {@hide}
      */
-    public abstract long getMobileRadioMeasuredBatteryConsumptionUC();
+    public abstract long getMobileRadioEnergyConsumptionUC();
 
     /**
      * Returns the battery consumption (in microcoulombs) of the screen while on, derived from on
@@ -2894,7 +2900,7 @@
      *
      * {@hide}
      */
-    public abstract long getScreenOnMeasuredBatteryConsumptionUC();
+    public abstract long getScreenOnEnergyConsumptionUC();
 
     /**
      * Returns the battery consumption (in microcoulombs) of the screen in doze, derived from on
@@ -2903,7 +2909,7 @@
      *
      * {@hide}
      */
-    public abstract long getScreenDozeMeasuredBatteryConsumptionUC();
+    public abstract long getScreenDozeEnergyConsumptionUC();
 
     /**
      * Returns the battery consumption (in microcoulombs) of wifi, derived from on
@@ -2912,7 +2918,7 @@
      *
      * {@hide}
      */
-    public abstract long getWifiMeasuredBatteryConsumptionUC();
+    public abstract long getWifiEnergyConsumptionUC();
 
     /**
      * Returns the battery consumption (in microcoulombs) that each
@@ -2924,7 +2930,7 @@
      *
      * {@hide}
      */
-    public abstract @Nullable long[] getCustomConsumerMeasuredBatteryConsumptionUC();
+    public abstract @Nullable long[] getCustomEnergyConsumerBatteryConsumptionUC();
 
     /**
      * Returns the names of all {@link android.hardware.power.stats.EnergyConsumer}'s
@@ -7088,19 +7094,19 @@
                     }
                 }
                 boolean firstExtension = true;
-                if (rec.measuredEnergyDetails != null) {
+                if (rec.energyConsumerDetails != null) {
                     firstExtension = false;
                     if (!checkin) {
                         item.append(" ext=energy:");
-                        item.append(rec.measuredEnergyDetails);
+                        item.append(rec.energyConsumerDetails);
                     } else {
                         item.append(",XE");
-                        for (int i = 0; i < rec.measuredEnergyDetails.consumers.length; i++) {
-                            if (rec.measuredEnergyDetails.chargeUC[i] != POWER_DATA_UNAVAILABLE) {
+                        for (int i = 0; i < rec.energyConsumerDetails.consumers.length; i++) {
+                            if (rec.energyConsumerDetails.chargeUC[i] != POWER_DATA_UNAVAILABLE) {
                                 item.append(',');
-                                item.append(rec.measuredEnergyDetails.consumers[i].name);
+                                item.append(rec.energyConsumerDetails.consumers[i].name);
                                 item.append('=');
-                                item.append(rec.measuredEnergyDetails.chargeUC[i]);
+                                item.append(rec.energyConsumerDetails.chargeUC[i]);
                             }
                         }
                     }
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 25a2852..90502af 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -591,7 +591,10 @@
             WAKE_REASON_DISPLAY_GROUP_TURNED_ON,
             WAKE_REASON_UNFOLD_DEVICE,
             WAKE_REASON_DREAM_FINISHED,
-            WAKE_REASON_TILT
+            WAKE_REASON_TILT,
+            WAKE_REASON_TAP,
+            WAKE_REASON_LIFT,
+            WAKE_REASON_BIOMETRIC,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface WakeReason{}
@@ -643,8 +646,9 @@
     public static final int WAKE_REASON_PLUGGED_IN = 3;
 
     /**
-     * Wake up reason code: Waking up due to a user performed gesture (e.g. double tapping on the
-     * screen).
+     * Wake up reason code: Waking up due to a user performed gesture. This includes user
+     * interactions with UI on the screen such as the notification shade. This does not include
+     * {@link WAKE_REASON_TAP} or {@link WAKE_REASON_LIFT}.
      * @hide
      */
     public static final int WAKE_REASON_GESTURE = 4;
@@ -710,6 +714,25 @@
      * @hide
      */
     public static final int WAKE_REASON_TILT = 14;
+    /**
+     * Wake up reason code: Waking up due to the user single or double tapping on the screen. This
+     * wake reason is used when the user is not tapping on a specific UI element; rather, the device
+     * wakes up due to a generic tap on the screen.
+     * @hide
+     */
+    public static final int WAKE_REASON_TAP = 15;
+
+    /**
+     * Wake up reason code: Waking up due to a user performed lift gesture.
+     * @hide
+     */
+    public static final int WAKE_REASON_LIFT = 16;
+
+    /**
+     * Wake up reason code: Waking up due to a user interacting with a biometric.
+     * @hide
+     */
+    public static final int WAKE_REASON_BIOMETRIC = 17;
 
     /**
      * Convert the wake reason to a string for debugging purposes.
@@ -732,6 +755,9 @@
             case WAKE_REASON_UNFOLD_DEVICE: return "WAKE_REASON_UNFOLD_DEVICE";
             case WAKE_REASON_DREAM_FINISHED: return "WAKE_REASON_DREAM_FINISHED";
             case WAKE_REASON_TILT: return "WAKE_REASON_TILT";
+            case WAKE_REASON_TAP: return "WAKE_REASON_TAP";
+            case WAKE_REASON_LIFT: return "WAKE_REASON_LIFT";
+            case WAKE_REASON_BIOMETRIC: return "WAKE_REASON_BIOMETRIC";
             default: return Integer.toString(wakeReason);
         }
     }
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index a6b62b0..180735b 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -26,6 +26,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.IActivityManager;
+import android.app.IUnsafeIntentStrictModeCallback;
 import android.app.compat.CompatChanges;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
@@ -1079,8 +1080,7 @@
             }
 
             /**
-             * Detect when your app launches an {@link Intent} which originated
-             * from outside your app.
+             * Detect when your app sends an unsafe {@link Intent}.
              * <p>
              * Violations may indicate security vulnerabilities in the design of
              * your app, where a malicious app could trick you into granting
@@ -1088,10 +1088,14 @@
              * are some typical design patterns that can be used to safely
              * resolve these violations:
              * <ul>
-             * <li>The ideal approach is to migrate to using a
-             * {@link android.app.PendingIntent}, which ensures that your launch is
-             * performed using the identity of the original creator, completely
-             * avoiding the security issues described above.
+             * <li> If you are sending an implicit intent to an unexported component, you should
+             * make it an explicit intent by using {@link Intent#setPackage},
+             * {@link Intent#setClassName} or {@link Intent#setComponent}.
+             * </li>
+             * <li> If you are unparceling and sending an intent from the intent delivered, The
+             * ideal approach is to migrate to using a {@link android.app.PendingIntent}, which
+             * ensures that your launch is performed using the identity of the original creator,
+             * completely avoiding the security issues described above.
              * <li>If using a {@link android.app.PendingIntent} isn't feasible, an
              * alternative approach is to create a brand new {@link Intent} and
              * carefully copy only specific values from the original
@@ -2106,10 +2110,39 @@
                 VMRuntime.setDedupeHiddenApiWarnings(true);
             }
 
+            if ((sVmPolicy.mask & DETECT_VM_UNSAFE_INTENT_LAUNCH) != 0) {
+                registerIntentMatchingRestrictionCallback();
+            }
+
             setBlockGuardVmPolicy(sVmPolicy.mask);
         }
     }
 
+    private static void registerIntentMatchingRestrictionCallback() {
+        try {
+            ActivityManager.getService().registerStrictModeCallback(
+                    new UnsafeIntentStrictModeCallback());
+        } catch (RemoteException e) {
+            /*
+            If exception is DeadObjectException it means system process is dead, so we can ignore
+             */
+            if (!(e instanceof DeadObjectException)) {
+                Log.e(TAG, "RemoteException handling StrictMode violation", e);
+            }
+        }
+    }
+
+    private static final class UnsafeIntentStrictModeCallback
+            extends IUnsafeIntentStrictModeCallback.Stub {
+        @Override
+        public void onImplicitIntentMatchedInternalComponent(Intent intent) {
+            if (StrictMode.vmUnsafeIntentLaunchEnabled()) {
+                StrictMode.onUnsafeIntentLaunch(intent,
+                        "Launch of unsafe implicit intent: " + intent);
+            }
+        }
+    }
+
     /** Gets the current VM policy. */
     public static VmPolicy getVmPolicy() {
         synchronized (StrictMode.class) {
@@ -2333,6 +2366,11 @@
         onVmPolicyViolation(new UnsafeIntentLaunchViolation(intent));
     }
 
+    /** @hide */
+    public static void onUnsafeIntentLaunch(Intent intent, String message) {
+        onVmPolicyViolation(new UnsafeIntentLaunchViolation(intent, message));
+    }
+
     /** Assume locked until we hear otherwise */
     private static volatile boolean sUserKeyUnlocked = false;
 
diff --git a/core/java/android/os/ThreadLocalWorkSource.java b/core/java/android/os/ThreadLocalWorkSource.java
index 894b1cc4..e9adb20 100644
--- a/core/java/android/os/ThreadLocalWorkSource.java
+++ b/core/java/android/os/ThreadLocalWorkSource.java
@@ -39,8 +39,8 @@
  */
 public final class ThreadLocalWorkSource {
     public static final int UID_NONE = Message.UID_NONE;
-    private static final ThreadLocal<Integer> sWorkSourceUid =
-            ThreadLocal.withInitial(() -> UID_NONE);
+    private static final ThreadLocal<int []> sWorkSourceUid =
+            ThreadLocal.withInitial(() -> new int[] {UID_NONE});
 
     /**
      * Returns the UID to blame for the code currently executed on this thread.
@@ -50,7 +50,7 @@
      * <p>It can also be set manually using {@link #setUid(int)}.
      */
     public static int getUid() {
-        return sWorkSourceUid.get();
+        return sWorkSourceUid.get()[0];
     }
 
     /**
@@ -65,7 +65,7 @@
      */
     public static long setUid(int uid) {
         final long token = getToken();
-        sWorkSourceUid.set(uid);
+        sWorkSourceUid.get()[0] = uid;
         return token;
     }
 
@@ -73,7 +73,7 @@
      * Restores the state using the provided token.
      */
     public static void restore(long token) {
-        sWorkSourceUid.set(parseUidFromToken(token));
+        sWorkSourceUid.get()[0] = parseUidFromToken(token);
     }
 
     /**
@@ -88,7 +88,7 @@
      * </pre>
      *
      * @return a token that can be used to restore the state.
-     **/
+     */
     public static long clear() {
         return setUid(UID_NONE);
     }
@@ -98,7 +98,7 @@
     }
 
     private static long getToken() {
-        return sWorkSourceUid.get();
+        return sWorkSourceUid.get()[0];
     }
 
     private ThreadLocalWorkSource() {
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 0b6a99b..3cb5c60 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -73,8 +73,6 @@
 
     private static final int ZYGOTE_CONNECT_TIMEOUT_MS = 20000;
 
-    private static final int APPLICATION_ZYGOTE_READ_TIMEOUT_MS = 5000;
-
     /**
      * Use a relatively short delay, because for app zygote, this is in the critical path of
      * service launch.
@@ -1111,9 +1109,6 @@
 
             state.mZygoteOutputWriter.flush();
 
-            // The system_server should not be blocked by a defective or bad application zygote.
-            state.mZygoteSessionSocket.setSoTimeout(APPLICATION_ZYGOTE_READ_TIMEOUT_MS);
-
             return (state.mZygoteInputStream.readInt() == 0);
         }
     }
diff --git a/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java b/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java
index f0f3cef..4abdc1b 100644
--- a/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java
+++ b/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java
@@ -56,6 +56,12 @@
         mIntent = Objects.requireNonNull(intent);
     }
 
+    /** @hide */
+    public UnsafeIntentLaunchViolation(@NonNull Intent intent, @NonNull String message) {
+        super(message);
+        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
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d01c33d..efe8238 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -17,6 +17,7 @@
 package android.provider;
 
 import android.Manifest;
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -87,6 +88,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.MemoryIntArray;
+import android.util.Slog;
 import android.view.Display;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
@@ -112,6 +114,8 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.Executor;
+
 /**
  * The Settings provider contains global system-level device preferences.
  */
@@ -2719,6 +2723,10 @@
     public static final String CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG =
             "REGISTER_MONITOR_CALLBACK_config";
 
+    /** @hide - Private call() method to unregister monitor callback for 'configuration' table */
+    public static final String CALL_METHOD_UNREGISTER_MONITOR_CALLBACK_CONFIG =
+            "UNREGISTER_MONITOR_CALLBACK_config";
+
     /** @hide - String argument extra to the config monitor callback */
     public static final String EXTRA_MONITOR_CALLBACK_TYPE = "monitor_callback_type";
 
@@ -18370,16 +18378,41 @@
         }
 
         /**
-         * Register callback for monitoring Config table.
+         * Setter callback for monitoring Config table.
          *
-         * @param callback callback to register
+         * @param executor the {@link Executor} on which to invoke the callback
+         * @param callback callback to set
          *
          * @hide
          */
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
         @RequiresPermission(Manifest.permission.MONITOR_DEVICE_CONFIG_ACCESS)
-        public static void registerMonitorCallback(@NonNull ContentResolver resolver,
-                @NonNull RemoteCallback callback) {
-            registerMonitorCallbackAsUser(resolver, resolver.getUserId(), callback);
+        public static void setMonitorCallback(
+                @NonNull ContentResolver resolver,
+                @NonNull @CallbackExecutor Executor executor,
+                @NonNull DeviceConfig.MonitorCallback callback) {
+            setMonitorCallbackAsUser(executor, resolver, resolver.getUserId(), callback);
+        }
+
+        /**
+         * Clear callback for monitoring Config table.
+         * this may only be used to clear callback function registered by
+         * {@link Config#setMonitorCallback}
+         * @hide
+         */
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @RequiresPermission(Manifest.permission.MONITOR_DEVICE_CONFIG_ACCESS)
+        public static void clearMonitorCallback(@NonNull ContentResolver resolver) {
+            try {
+                Bundle arg = new Bundle();
+                arg.putInt(CALL_METHOD_USER_KEY, resolver.getUserId());
+                IContentProvider cp = sProviderHolder.getProvider(resolver);
+                cp.call(resolver.getAttributionSource(),
+                        sProviderHolder.mUri.getAuthority(),
+                        CALL_METHOD_UNREGISTER_MONITOR_CALLBACK_CONFIG, null, arg);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Can't clear config monitor callback", e);
+            }
         }
 
 
@@ -18446,19 +18479,23 @@
             }
         }
 
-        private static void registerMonitorCallbackAsUser(
+        private static void setMonitorCallbackAsUser(
+                @NonNull @CallbackExecutor Executor executor,
                 @NonNull ContentResolver resolver, @UserIdInt int userHandle,
-                @NonNull RemoteCallback callback) {
+                @NonNull DeviceConfig.MonitorCallback callback) {
             try {
                 Bundle arg = new Bundle();
                 arg.putInt(CALL_METHOD_USER_KEY, userHandle);
-                arg.putParcelable(CALL_METHOD_MONITOR_CALLBACK_KEY, callback);
+                arg.putParcelable(CALL_METHOD_MONITOR_CALLBACK_KEY,
+                        new RemoteCallback(result -> {
+                            handleMonitorCallback(result, executor, callback);
+                        }));
                 IContentProvider cp = sProviderHolder.getProvider(resolver);
                 cp.call(resolver.getAttributionSource(),
                         sProviderHolder.mUri.getAuthority(),
                         CALL_METHOD_REGISTER_MONITOR_CALLBACK_CONFIG, null, arg);
             } catch (RemoteException e) {
-                Log.w(TAG, "Can't register config monitor callback", e);
+                Log.w(TAG, "Can't set config monitor callback", e);
             }
         }
 
@@ -18468,6 +18505,32 @@
             sNameValueCache.clearGenerationTrackerForTest();
         }
 
+        private static void handleMonitorCallback(
+                Bundle result,
+                @NonNull @CallbackExecutor Executor executor,
+                DeviceConfig.MonitorCallback monitorCallback) {
+            String callbackType = result.getString(EXTRA_MONITOR_CALLBACK_TYPE, "");
+            switch (callbackType) {
+                case EXTRA_NAMESPACE_UPDATED_CALLBACK:
+                    String updatedNamespace = result.getString(EXTRA_NAMESPACE);
+                    if (updatedNamespace != null) {
+                        executor.execute(() -> monitorCallback.onNamespaceUpdate(updatedNamespace));
+                    }
+                    break;
+                case EXTRA_ACCESS_CALLBACK:
+                    String callingPackage = result.getString(EXTRA_CALLING_PACKAGE, null);
+                    String namespace = result.getString(EXTRA_NAMESPACE, null);
+                    if (namespace != null && callingPackage != null) {
+                        executor.execute(() ->
+                                monitorCallback.onDeviceConfigAccess(callingPackage, namespace));
+                    }
+                    break;
+                default:
+                    Slog.w(TAG, "Unrecognized DeviceConfig callback");
+                    break;
+            }
+        }
+
         private static String createCompositeName(@NonNull String namespace, @NonNull String name) {
             Preconditions.checkNotNull(namespace);
             Preconditions.checkNotNull(name);
diff --git a/core/java/android/service/wallpaper/OWNERS b/core/java/android/service/wallpaper/OWNERS
index 756eef8..71bd190 100644
--- a/core/java/android/service/wallpaper/OWNERS
+++ b/core/java/android/service/wallpaper/OWNERS
@@ -3,3 +3,6 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
[email protected]
[email protected]
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index de5d395..0eab81c 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -394,7 +394,7 @@
             public void resized(ClientWindowFrames frames, boolean reportDraw,
                     MergedConfiguration mergedConfiguration, InsetsState insetsState,
                     boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId,
-                    int syncSeqId, int resizeMode) {
+                    int syncSeqId, boolean dragResizing) {
                 Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED,
                         reportDraw ? 1 : 0,
                         mergedConfiguration);
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 8e16f24..d554514 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -57,7 +57,7 @@
     void resized(in ClientWindowFrames frames, boolean reportDraw,
             in MergedConfiguration newMergedConfiguration, in InsetsState insetsState,
             boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId,
-            int syncSeqId, int resizeMode);
+            int syncSeqId, boolean dragResizing);
 
     /**
      * Called when this window retrieved control over a specified set of insets sources.
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 6d9f99f..0aba80d 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -457,12 +457,6 @@
     int getDockedStackSide();
 
     /**
-     * Sets the region the user can touch the divider. This region will be excluded from the region
-     * which is used to cause a focus switch when dispatching touch.
-     */
-    void setDockedTaskDividerTouchRegion(in Rect touchableRegion);
-
-    /**
      * Registers a listener that will be called when the pinned task state changes.
      */
     void registerPinnedTaskListener(int displayId, IPinnedTaskListener listener);
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index e34aef9..b8cd7b9 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -20,7 +20,6 @@
 import static android.view.ImeInsetsSourceConsumerProto.IS_HIDE_ANIMATION_RUNNING;
 import static android.view.ImeInsetsSourceConsumerProto.IS_REQUESTED_VISIBLE_AWAITING_CONTROL;
 import static android.view.ImeInsetsSourceConsumerProto.IS_SHOW_REQUESTED_DURING_HIDE_ANIMATION;
-import static android.view.InsetsState.ITYPE_IME;
 
 import android.annotation.Nullable;
 import android.os.IBinder;
@@ -55,9 +54,9 @@
     private boolean mIsShowRequestedDuringHideAnimation;
 
     public ImeInsetsSourceConsumer(
-            InsetsState state, Supplier<Transaction> transactionSupplier,
+            int id, InsetsState state, Supplier<Transaction> transactionSupplier,
             InsetsController controller) {
-        super(ITYPE_IME, state, transactionSupplier, controller);
+        super(id, WindowInsets.Type.ime(), state, transactionSupplier, controller);
     }
 
     @Override
@@ -137,7 +136,7 @@
         // If we had a request before to show from IME (tracked with mImeRequestedShow), reaching
         // this code here means that we now got control, so we can start the animation immediately.
         // If client window is trying to control IME and IME is already visible, it is immediate.
-        if (fromIme || (mState.getSource(getInternalType()).isVisible() && getControl() != null)) {
+        if (fromIme || (mState.getSource(getId()).isVisible() && getControl() != null)) {
             return ShowResult.SHOW_IMMEDIATELY;
         }
 
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index e1b27ff..709bc2b 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -21,7 +21,6 @@
 import static android.view.InsetsControllerProto.STATE;
 import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
-import static android.view.InsetsState.toPublicType;
 import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
 import static android.view.WindowInsets.Type.FIRST;
 import static android.view.WindowInsets.Type.LAST;
@@ -48,7 +47,6 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
-import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.InsetsSourceConsumer.ShowResult;
 import android.view.InsetsState.InternalInsetsType;
@@ -569,8 +567,9 @@
     private final InsetsState mLastDispatchedState = new InsetsState();
 
     private final Rect mFrame = new Rect();
-    private final BiFunction<InsetsController, Integer, InsetsSourceConsumer> mConsumerCreator;
+    private final BiFunction<InsetsController, InsetsSource, InsetsSourceConsumer> mConsumerCreator;
     private final SparseArray<InsetsSourceConsumer> mSourceConsumers = new SparseArray<>();
+    private final InsetsSourceConsumer mImeSourceConsumer;
     private final Host mHost;
     private final Handler mHandler;
 
@@ -621,19 +620,20 @@
             this::invokeControllableInsetsChangedListeners;
 
     public InsetsController(Host host) {
-        this(host, (controller, type) -> {
-            if (type == ITYPE_IME) {
-                return new ImeInsetsSourceConsumer(controller.mState, Transaction::new, controller);
+        this(host, (controller, source) -> {
+            if (source.getType() == ime()) {
+                return new ImeInsetsSourceConsumer(source.getId(), controller.mState,
+                        Transaction::new, controller);
             } else {
-                return new InsetsSourceConsumer(type, controller.mState, Transaction::new,
-                        controller);
+                return new InsetsSourceConsumer(source.getId(), source.getType(), controller.mState,
+                        Transaction::new, controller);
             }
         }, host.getHandler());
     }
 
     @VisibleForTesting
     public InsetsController(Host host,
-            BiFunction<InsetsController, Integer, InsetsSourceConsumer> consumerCreator,
+            BiFunction<InsetsController, InsetsSource, InsetsSourceConsumer> consumerCreator,
             Handler handler) {
         mHost = host;
         mConsumerCreator = consumerCreator;
@@ -683,6 +683,9 @@
                 dispatchAnimationEnd(finishedAnimations.get(i));
             }
         };
+
+        // Make mImeSourceConsumer always non-null.
+        mImeSourceConsumer = getSourceConsumer(new InsetsSource(ITYPE_IME, ime()));
     }
 
     @VisibleForTesting
@@ -741,28 +744,37 @@
 
     private void updateState(InsetsState newState) {
         mState.set(newState, 0 /* types */);
+        for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
+            final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+            final InsetsSource source = newState.peekSource(consumer.getId());
+            if (source == null && consumer != mImeSourceConsumer) {
+                // IME source consumer should always be there since we need to communicate with
+                // InputMethodManager no matter we have the source or not.
+                mSourceConsumers.removeAt(i);
+            }
+        }
         @InsetsType int existingTypes = 0;
         @InsetsType int visibleTypes = 0;
         @InsetsType int disabledUserAnimationTypes = 0;
         @InsetsType int[] cancelledUserAnimationTypes = {0};
-        for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
-            InsetsSource source = newState.peekSource(type);
+        for (int i = 0; i < InsetsState.SIZE; i++) {
+            InsetsSource source = newState.peekSource(i);
             if (source == null) continue;
-            @InsetsType int insetsType = toPublicType(type);
-            @AnimationType int animationType = getAnimationType(insetsType);
+            @InsetsType int type = source.getType();
+            @AnimationType int animationType = getAnimationType(type);
             if (!source.isUserControllable()) {
                 // The user animation is not allowed when visible frame is empty.
-                disabledUserAnimationTypes |= insetsType;
+                disabledUserAnimationTypes |= type;
                 if (animationType == ANIMATION_TYPE_USER) {
                     // Existing user animation needs to be cancelled.
                     animationType = ANIMATION_TYPE_NONE;
-                    cancelledUserAnimationTypes[0] |= insetsType;
+                    cancelledUserAnimationTypes[0] |= type;
                 }
             }
-            getSourceConsumer(type).updateSource(source, animationType);
-            existingTypes |= insetsType;
+            getSourceConsumer(source).updateSource(source, animationType);
+            existingTypes |= type;
             if (source.isVisible()) {
-                visibleTypes |= insetsType;
+                visibleTypes |= type;
             }
         }
 
@@ -899,25 +911,34 @@
         }
 
         @InsetsType int controllableTypes = 0;
+        int consumedControlCount = 0;
         final int[] showTypes = new int[1];
         final int[] hideTypes = new int[1];
 
         // Ensure to update all existing source consumers
         for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
             final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
-            final InsetsSourceControl control = mTmpControlArray.get(consumer.getInternalType());
+            final InsetsSourceControl control = mTmpControlArray.get(consumer.getId());
+            if (control != null) {
+                controllableTypes |= control.getType();
+                consumedControlCount++;
+            }
 
             // control may be null, but we still need to update the control to null if it got
             // revoked.
             consumer.setControl(control, showTypes, hideTypes);
         }
 
-        // Ensure to create source consumers if not available yet.
-        for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
-            final InsetsSourceControl control = mTmpControlArray.valueAt(i);
-            final InsetsSourceConsumer consumer = getSourceConsumer(control.getId());
-            consumer.setControl(control, showTypes, hideTypes);
-            controllableTypes |= control.getType();
+        if (consumedControlCount != mTmpControlArray.size()) {
+            // Whoops! The server sent us some controls without sending corresponding sources.
+            for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
+                final InsetsSourceControl control = mTmpControlArray.valueAt(i);
+                final InsetsSourceConsumer consumer = mSourceConsumers.get(control.getId());
+                if (consumer == null) {
+                    control.release(SurfaceControl::release);
+                    Log.e(TAG, control + " has no consumer.");
+                }
+            }
         }
 
         if (mTmpControlArray.size() > 0) {
@@ -1144,11 +1165,11 @@
             types &= ~mDisabledUserAnimationInsetsTypes;
 
             if (fromIme && (disabledTypes & ime()) != 0
-                    && !mState.getSource(ITYPE_IME).isVisible()) {
+                    && !mState.getSource(mImeSourceConsumer.getId()).isVisible()) {
                 // We've requested IMM to show IME, but the IME is not controllable. We need to
                 // cancel the request.
                 setRequestedVisibleTypes(0 /* visibleTypes */, ime());
-                if (getSourceConsumer(ITYPE_IME).onAnimationStateChanged(false /* running */)) {
+                if (mImeSourceConsumer.onAnimationStateChanged(false /* running */)) {
                     notifyVisibilityChanged();
                 }
             }
@@ -1157,17 +1178,18 @@
             // nothing to animate.
             listener.onCancelled(null);
             if (DEBUG) Log.d(TAG, "no types to animate in controlAnimationUnchecked");
+            Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0);
+            Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0);
             return;
         }
         cancelExistingControllers(types);
         if (DEBUG) Log.d(TAG, "controlAnimation types: " + types);
         mLastStartedAnimTypes |= types;
 
-        final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
         final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
 
         Pair<Integer, Boolean> typesReadyPair = collectSourceControls(
-                fromIme, internalTypes, controls, animationType);
+                fromIme, types, controls, animationType);
         int typesReady = typesReadyPair.first;
         boolean imeReady = typesReadyPair.second;
         if (DEBUG) Log.d(TAG, String.format(
@@ -1197,12 +1219,20 @@
             setRequestedVisibleTypes(mReportedRequestedVisibleTypes, types);
 
             Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0);
+            if (!fromIme) {
+                Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0);
+            }
             return;
         }
 
         if (typesReady == 0) {
             if (DEBUG) Log.d(TAG, "No types ready. onCancelled()");
             listener.onCancelled(null);
+            reportRequestedVisibleTypes();
+            Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0);
+            if (!fromIme) {
+                Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0);
+            }
             return;
         }
 
@@ -1257,13 +1287,15 @@
     /**
      * @return Pair of (types ready to animate, IME ready to animate).
      */
-    private Pair<Integer, Boolean> collectSourceControls(boolean fromIme,
-            ArraySet<Integer> internalTypes, SparseArray<InsetsSourceControl> controls,
-            @AnimationType int animationType) {
+    private Pair<Integer, Boolean> collectSourceControls(boolean fromIme, @InsetsType int types,
+            SparseArray<InsetsSourceControl> controls, @AnimationType int animationType) {
         int typesReady = 0;
         boolean imeReady = true;
-        for (int i = internalTypes.size() - 1; i >= 0; i--) {
-            final InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
+        for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
+            final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+            if ((consumer.getType() & types) == 0) {
+                continue;
+            }
             boolean show = animationType == ANIMATION_TYPE_SHOW
                     || animationType == ANIMATION_TYPE_USER;
             boolean canRun = true;
@@ -1291,7 +1323,7 @@
             if (!canRun) {
                 if (WARN) Log.w(TAG, String.format(
                         "collectSourceControls can't continue show for type: %s fromIme: %b",
-                        InsetsState.typeToString(consumer.getInternalType()), fromIme));
+                        WindowInsets.Type.toString(consumer.getType()), fromIme));
                 continue;
             }
             final InsetsSourceControl control = consumer.getControl();
@@ -1437,17 +1469,30 @@
     }
 
     @VisibleForTesting
-    public @NonNull InsetsSourceConsumer getSourceConsumer(int id) {
-        InsetsSourceConsumer consumer = mSourceConsumers.get(id);
+    public @NonNull InsetsSourceConsumer getSourceConsumer(InsetsSource source) {
+        final int sourceId = source.getId();
+        InsetsSourceConsumer consumer = mSourceConsumers.get(sourceId);
         if (consumer != null) {
             return consumer;
         }
-        consumer = mConsumerCreator.apply(this, id);
-        mSourceConsumers.put(id, consumer);
+        if (source.getType() == ime() && mImeSourceConsumer != null) {
+            // WindowInsets.Type.ime() should be only provided by one source.
+            mSourceConsumers.remove(mImeSourceConsumer.getId());
+            consumer = mImeSourceConsumer;
+            consumer.setId(sourceId);
+        } else {
+            consumer = mConsumerCreator.apply(this, source);
+        }
+        mSourceConsumers.put(sourceId, consumer);
         return consumer;
     }
 
     @VisibleForTesting
+    public @NonNull InsetsSourceConsumer getImeSourceConsumer() {
+        return mImeSourceConsumer;
+    }
+
+    @VisibleForTesting
     public void notifyVisibilityChanged() {
         mHost.notifyInsetsChanged();
     }
@@ -1467,14 +1512,14 @@
      * Called when current window gains focus.
      */
     public void onWindowFocusGained(boolean hasViewFocused) {
-        getSourceConsumer(ITYPE_IME).onWindowFocusGained(hasViewFocused);
+        mImeSourceConsumer.onWindowFocusGained(hasViewFocused);
     }
 
     /**
      * Called when current window loses focus.
      */
     public void onWindowFocusLost() {
-        getSourceConsumer(ITYPE_IME).onWindowFocusLost();
+        mImeSourceConsumer.onWindowFocusLost();
     }
 
     @VisibleForTesting
@@ -1518,13 +1563,12 @@
         // TODO(b/166736352): We should only skip the animation of specific types, not all types.
         boolean skipAnim = false;
         if ((types & ime()) != 0) {
-            final InsetsSourceConsumer consumer = mSourceConsumers.get(ITYPE_IME);
-            final InsetsSourceControl imeControl = consumer != null ? consumer.getControl() : null;
+            final InsetsSourceControl imeControl = mImeSourceConsumer.getControl();
             // Skip showing animation once that made by system for some reason.
             // (e.g. starting window with IME snapshot)
             if (imeControl != null) {
                 skipAnim = imeControl.getAndClearSkipAnimationOnce() && show
-                        && consumer.hasViewFocusWhenWindowFocusGain();
+                        && mImeSourceConsumer.hasViewFocusWhenWindowFocusGain();
             }
         }
         applyAnimation(types, show, fromIme, skipAnim, statsToken);
@@ -1536,6 +1580,10 @@
         if (types == 0) {
             // nothing to animate.
             if (DEBUG) Log.d(TAG, "applyAnimation, nothing to animate");
+            Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApi", 0);
+            if (!fromIme) {
+                Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0);
+            }
             return;
         }
 
@@ -1683,7 +1731,7 @@
         @InsetsType int result = 0;
         for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
             InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
-            InsetsSource source = mState.peekSource(consumer.getInternalType());
+            InsetsSource source = mState.peekSource(consumer.getId());
             if (consumer.getControl() != null && source != null && source.isUserControllable()) {
                 result |= consumer.getType();
             }
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 7aeada8..f46eb34 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -25,7 +25,6 @@
 import static android.view.InsetsSourceConsumerProto.PENDING_FRAME;
 import static android.view.InsetsSourceConsumerProto.PENDING_VISIBLE_FRAME;
 import static android.view.InsetsSourceConsumerProto.SOURCE_CONTROL;
-import static android.view.InsetsState.getDefaultVisibility;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 
@@ -34,7 +33,6 @@
 import android.graphics.Rect;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
-import android.view.InsetsState.InternalInsetsType;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets.Type.InsetsType;
 
@@ -72,7 +70,7 @@
 
     protected final InsetsController mController;
     protected final InsetsState mState;
-    private final @InternalInsetsType int mInternalType;
+    private int mId;
     private final @InsetsType int mType;
 
     private static final String TAG = "InsetsSourceConsumer";
@@ -88,16 +86,17 @@
     private Rect mPendingVisibleFrame;
 
     /**
-     * @param type The {@link InternalInsetsType} of the consumed insets.
+     * @param id The ID of the consumed insets.
+     * @param type The {@link InsetsType} of the consumed insets.
      * @param state The current {@link InsetsState} of the consumed insets.
      * @param transactionSupplier The source of new {@link Transaction} instances. The supplier
      *         must provide *new* instances, which will be explicitly closed by this class.
      * @param controller The {@link InsetsController} to use for insets interaction.
      */
-    public InsetsSourceConsumer(@InternalInsetsType int type, InsetsState state,
+    public InsetsSourceConsumer(int id, @InsetsType int type, InsetsState state,
             Supplier<Transaction> transactionSupplier, InsetsController controller) {
-        mType = InsetsState.toPublicType(type);
-        mInternalType = type;
+        mId = id;
+        mType = type;
         mState = state;
         mTransactionSupplier = transactionSupplier;
         mController = controller;
@@ -134,12 +133,11 @@
             mController.notifyControlRevoked(this);
 
             // Check if we need to restore server visibility.
-            final InsetsSource source = mState.getSource(mInternalType);
-            final boolean serverVisibility =
-                    mController.getLastDispatchedState().getSourceOrDefaultVisibility(
-                            mInternalType);
-            if (source.isVisible() != serverVisibility) {
-                source.setVisible(serverVisibility);
+            final InsetsSource localSource = mState.peekSource(mId);
+            final InsetsSource serverSource = mController.getLastDispatchedState().peekSource(mId);
+            if (localSource != null && serverSource != null
+                    && localSource.isVisible() != serverSource.isVisible()) {
+                localSource.setVisible(serverSource.isVisible());
                 mController.notifyVisibilityChanged();
             }
         } else {
@@ -196,12 +194,16 @@
         return (mController.getRequestedVisibleTypes() & mType) != 0;
     }
 
-    @InsetsType int getType() {
-        return mType;
+    int getId() {
+        return mId;
     }
 
-    @InternalInsetsType int getInternalType() {
-        return mInternalType;
+    void setId(int id) {
+        mId = id;
+    }
+
+    @InsetsType int getType() {
+        return mType;
     }
 
     /**
@@ -211,12 +213,14 @@
     public boolean onAnimationStateChanged(boolean running) {
         boolean insetsChanged = false;
         if (!running && mPendingFrame != null) {
-            InsetsSource source = mState.getSource(mInternalType);
-            source.setFrame(mPendingFrame);
-            source.setVisibleFrame(mPendingVisibleFrame);
+            final InsetsSource source = mState.peekSource(mId);
+            if (source != null) {
+                source.setFrame(mPendingFrame);
+                source.setVisibleFrame(mPendingVisibleFrame);
+                insetsChanged = true;
+            }
             mPendingFrame = null;
             mPendingVisibleFrame = null;
-            insetsChanged = true;
         }
 
         // We apply the visibility override after the animation is started. We don't do this before
@@ -248,25 +252,25 @@
 
     @VisibleForTesting(visibility = PACKAGE)
     public boolean applyLocalVisibilityOverride() {
-        final InsetsSource source = mState.peekSource(mInternalType);
-        final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(
-                mInternalType);
-        final boolean hasControl = mSourceControl != null;
+        final InsetsSource source = mState.peekSource(mId);
+        if (source == null) {
+            return false;
+        }
         final boolean requestedVisible = (mController.getRequestedVisibleTypes() & mType) != 0;
 
         // If we don't have control, we are not able to change the visibility.
-        if (!hasControl) {
+        if (mSourceControl == null) {
             if (DEBUG) Log.d(TAG, "applyLocalVisibilityOverride: No control in "
                     + mController.getHost().getRootViewTitle()
                     + " requestedVisible=" + requestedVisible);
             return false;
         }
-        if (isVisible == requestedVisible) {
+        if (source.isVisible() == requestedVisible) {
             return false;
         }
         if (DEBUG) Log.d(TAG, String.format("applyLocalVisibilityOverride: %s requestedVisible: %b",
                 mController.getHost().getRootViewTitle(), requestedVisible));
-        mState.getSource(mInternalType).setVisible(requestedVisible);
+        source.setVisible(requestedVisible);
         return true;
     }
 
@@ -301,7 +305,7 @@
 
     @VisibleForTesting(visibility = PACKAGE)
     public void updateSource(InsetsSource newSource, @AnimationType int animationType) {
-        InsetsSource source = mState.peekSource(mInternalType);
+        InsetsSource source = mState.peekSource(mId);
         if (source == null || animationType == ANIMATION_TYPE_NONE
                 || source.getFrame().equals(newSource.getFrame())) {
             mPendingFrame = null;
@@ -345,7 +349,7 @@
 
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
-        proto.write(INTERNAL_INSETS_TYPE, InsetsState.typeToString(mInternalType));
+        proto.write(INTERNAL_INSETS_TYPE, WindowInsets.Type.toString(mType));
         proto.write(HAS_WINDOW_FOCUS, mHasWindowFocus);
         proto.write(IS_REQUESTED_VISIBLE, (mController.getRequestedVisibleTypes() & mType) != 0);
         if (mSourceControl != null) {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5e8e191..5c23c31 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -27,12 +27,14 @@
 import static android.view.SurfaceControlProto.LAYER_ID;
 import static android.view.SurfaceControlProto.NAME;
 
+import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.Size;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -3166,6 +3168,7 @@
           *
           * @hide
           */
+        @RequiresPermission(Manifest.permission.WAKEUP_SURFACE_FLINGER)
         public Transaction setEarlyWakeupStart() {
             nativeSetEarlyWakeupStart(mNativeObject);
             return this;
@@ -3176,6 +3179,7 @@
          *
          * @hide
          */
+        @RequiresPermission(Manifest.permission.WAKEUP_SURFACE_FLINGER)
         public Transaction setEarlyWakeupEnd() {
             nativeSetEarlyWakeupEnd(mNativeObject);
             return this;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ff4ea81..ea7a64e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -51,8 +51,6 @@
 import static android.view.ViewRootImplProto.WINDOW_ATTRIBUTES;
 import static android.view.ViewRootImplProto.WIN_FRAME;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
-import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
-import static android.view.WindowCallbacks.RESIZE_MODE_INVALID;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
@@ -515,7 +513,6 @@
     private boolean mPendingDragResizing;
     private boolean mDragResizing;
     private boolean mInvalidateRootRequested;
-    private int mResizeMode = RESIZE_MODE_INVALID;
     private int mCanvasOffsetX;
     private int mCanvasOffsetY;
     private boolean mActivityRelaunched;
@@ -1806,7 +1803,7 @@
         CompatibilityInfo.applyOverrideScaleIfNeeded(mergedConfiguration);
         final boolean forceNextWindowRelayout = args.argi1 != 0;
         final int displayId = args.argi3;
-        final int resizeMode = args.argi5;
+        final boolean dragResizing = args.argi5 != 0;
 
         final Rect frame = frames.frame;
         final Rect displayFrame = frames.displayFrame;
@@ -1822,16 +1819,14 @@
         final boolean attachedFrameChanged = LOCAL_LAYOUT
                 && !Objects.equals(mTmpFrames.attachedFrame, attachedFrame);
         final boolean displayChanged = mDisplay.getDisplayId() != displayId;
-        final boolean resizeModeChanged = mResizeMode != resizeMode;
         final boolean compatScaleChanged = mTmpFrames.compatScale != compatScale;
         if (msg == MSG_RESIZED && !frameChanged && !configChanged && !attachedFrameChanged
-                && !displayChanged && !resizeModeChanged && !forceNextWindowRelayout
+                && !displayChanged && !forceNextWindowRelayout
                 && !compatScaleChanged) {
             return;
         }
 
-        mPendingDragResizing = resizeMode != RESIZE_MODE_INVALID;
-        mResizeMode = resizeMode;
+        mPendingDragResizing = dragResizing;
         mTmpFrames.compatScale = compatScale;
         mInvCompatScale = 1f / compatScale;
 
@@ -3028,7 +3023,7 @@
                         frame.width() < desiredWindowWidth && frame.width() != mWidth)
                 || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT &&
                         frame.height() < desiredWindowHeight && frame.height() != mHeight));
-        windowShouldResize |= mDragResizing && mResizeMode == RESIZE_MODE_FREEFORM;
+        windowShouldResize |= mDragResizing && mPendingDragResizing;
 
         // If the activity was just relaunched, it might have unfrozen the task bounds (while
         // relaunching), so we need to force a call into window manager to pick up the latest
@@ -3275,7 +3270,7 @@
                                         && mWinFrame.height() == mPendingBackDropFrame.height();
                         // TODO: Need cutout?
                         startDragResizing(mPendingBackDropFrame, !backdropSizeMatchesFrame,
-                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mResizeMode);
+                                mAttachInfo.mContentInsets, mAttachInfo.mStableInsets);
                     } else {
                         // We shouldn't come here, but if we come we should end the resize.
                         endDragResizing();
@@ -8841,7 +8836,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private void dispatchResized(ClientWindowFrames frames, boolean reportDraw,
             MergedConfiguration mergedConfiguration, InsetsState insetsState, boolean forceLayout,
-            boolean alwaysConsumeSystemBars, int displayId, int syncSeqId, int resizeMode) {
+            boolean alwaysConsumeSystemBars, int displayId, int syncSeqId, boolean dragResizing) {
         Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED);
         SomeArgs args = SomeArgs.obtain();
         final boolean sameProcessCall = (Binder.getCallingPid() == android.os.Process.myPid());
@@ -8863,7 +8858,7 @@
         args.argi2 = alwaysConsumeSystemBars ? 1 : 0;
         args.argi3 = displayId;
         args.argi4 = syncSeqId;
-        args.argi5 = resizeMode;
+        args.argi5 = dragResizing ? 1 : 0;
 
         msg.obj = args;
         mHandler.sendMessage(msg);
@@ -10255,11 +10250,11 @@
         public void resized(ClientWindowFrames frames, boolean reportDraw,
                 MergedConfiguration mergedConfiguration, InsetsState insetsState,
                 boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int syncSeqId,
-                int resizeMode) {
+                boolean dragResizing) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
                 viewAncestor.dispatchResized(frames, reportDraw, mergedConfiguration, insetsState,
-                        forceLayout, alwaysConsumeSystemBars, displayId, syncSeqId, resizeMode);
+                        forceLayout, alwaysConsumeSystemBars, displayId, syncSeqId, dragResizing);
             }
         }
 
@@ -10464,13 +10459,13 @@
      * Start a drag resizing which will inform all listeners that a window resize is taking place.
      */
     private void startDragResizing(Rect initialBounds, boolean fullscreen, Rect systemInsets,
-            Rect stableInsets, int resizeMode) {
+            Rect stableInsets) {
         if (!mDragResizing) {
             mDragResizing = true;
             if (mUseMTRenderer) {
                 for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
                     mWindowCallbacks.get(i).onWindowDragResizeStart(
-                            initialBounds, fullscreen, systemInsets, stableInsets, resizeMode);
+                            initialBounds, fullscreen, systemInsets, stableInsets);
                 }
             }
             mFullRedrawNeeded = true;
diff --git a/core/java/android/view/WindowCallbacks.java b/core/java/android/view/WindowCallbacks.java
index a7f0ef0..94b2d93 100644
--- a/core/java/android/view/WindowCallbacks.java
+++ b/core/java/android/view/WindowCallbacks.java
@@ -28,22 +28,6 @@
  */
 public interface WindowCallbacks {
 
-    int RESIZE_MODE_INVALID = -1;
-
-    /**
-     * The window is being resized by dragging one of the window corners,
-     * in this case the surface would be fullscreen-sized. The client should
-     * render to the actual frame location (instead of (0,curScrollY)).
-     */
-    int RESIZE_MODE_FREEFORM = 0;
-
-    /**
-     * The window is being resized by dragging on the docked divider. The client should render
-     * at (0, 0) and extend its background to the background frame passed into
-     * {@link IWindow#resized}.
-     */
-    int RESIZE_MODE_DOCKED_DIVIDER = 1;
-
     /**
      * Called by the system when the window got changed by the user, before the layouter got called.
      * It also gets called when the insets changed, or when the window switched between a fullscreen
@@ -69,7 +53,7 @@
      * @param stableInsets The stable insets for the window.
      */
     void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
-            Rect stableInsets, int resizeMode);
+            Rect stableInsets);
 
     /**
      * Called when a drag resize ends.
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 43d427d..9472d86 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -55,13 +55,6 @@
     int PRESENCE_INTERNAL = 1 << 0;
     int PRESENCE_EXTERNAL = 1 << 1;
 
-    // Alternate bars position values
-    int ALT_BAR_UNKNOWN = -1;
-    int ALT_BAR_LEFT = 1 << 0;
-    int ALT_BAR_RIGHT = 1 << 1;
-    int ALT_BAR_BOTTOM = 1 << 2;
-    int ALT_BAR_TOP = 1 << 3;
-
     // Navigation bar position values
     int NAV_BAR_INVALID = -1;
     int NAV_BAR_LEFT = 1 << 0;
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index a9fea017..4c95728 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -16,8 +16,6 @@
 
 package android.view;
 
-import static android.view.WindowCallbacks.RESIZE_MODE_INVALID;
-
 import android.annotation.Nullable;
 import android.content.res.Configuration;
 import android.graphics.PixelFormat;
@@ -552,7 +550,7 @@
                 mTmpConfig.setConfiguration(mConfiguration, mConfiguration);
                 s.mClient.resized(mTmpFrames, false /* reportDraw */, mTmpConfig, state,
                         false /* forceLayout */, false /* alwaysConsumeSystemBars */, s.mDisplayId,
-                        Integer.MAX_VALUE, RESIZE_MODE_INVALID);
+                        Integer.MAX_VALUE, false /* dragResizing */);
             } catch (RemoteException e) {
                 // Too bad
             }
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index 313dc43..15ba1a1 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -22,6 +22,8 @@
 import android.os.Parcelable;
 import android.view.View;
 
+import java.util.Objects;
+
 /**
  * A unique identifier for an autofill node inside an {@link android.app.Activity}.
  */
@@ -76,6 +78,23 @@
     @NonNull
     public static final AutofillId NO_AUTOFILL_ID = new AutofillId(0);
 
+    /**
+     * Creates an {@link AutofillId} with the virtual id.
+     *
+     * This method is used by a {@link View} that contains the virtual view hierarchy. Use this
+     * method to create the {@link AutofillId} for each virtual view.
+     *
+     * @param host the view hosting the virtual view hierarchy which is used to show autofill
+     *            suggestions.
+     * @param virtualId id identifying the virtual view inside the host view.
+     * @return an {@link AutofillId} for the virtual view
+     */
+    @NonNull
+    public static AutofillId create(@NonNull View host, int virtualId) {
+        Objects.requireNonNull(host);
+        return new AutofillId(host.getAutofillId(), virtualId);
+    }
+
     /** @hide */
     @NonNull
     @TestApi
diff --git a/core/java/android/view/inputmethod/HandwritingGesture.java b/core/java/android/view/inputmethod/HandwritingGesture.java
index 07b1e1f..2516269 100644
--- a/core/java/android/view/inputmethod/HandwritingGesture.java
+++ b/core/java/android/view/inputmethod/HandwritingGesture.java
@@ -159,7 +159,7 @@
      * @hide
      */
     @TestApi
-    public @GestureType int getGestureType() {
+    public final @GestureType int getGestureType() {
         return mType;
     }
 
@@ -173,7 +173,7 @@
      * example 2: join can fail if the gesture is drawn over text but there is no whitespace.
      */
     @Nullable
-    public String getFallbackText() {
+    public final String getFallbackText() {
         return mFallbackText;
     }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 66d64c4..3d8982b 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -22,10 +22,10 @@
 import android.os.BatteryStats;
 import android.os.BatteryStats.BitDescription;
 import android.os.BatteryStats.CpuUsageDetails;
+import android.os.BatteryStats.EnergyConsumerDetails;
 import android.os.BatteryStats.HistoryItem;
 import android.os.BatteryStats.HistoryStepDetails;
 import android.os.BatteryStats.HistoryTag;
-import android.os.BatteryStats.MeasuredEnergyDetails;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.ParcelFormatException;
@@ -984,9 +984,9 @@
     /**
      * Records measured energy data.
      */
-    public void recordMeasuredEnergyDetails(long elapsedRealtimeMs, long uptimeMs,
-            MeasuredEnergyDetails measuredEnergyDetails) {
-        mHistoryCur.measuredEnergyDetails = measuredEnergyDetails;
+    public void recordEnergyConsumerDetails(long elapsedRealtimeMs, long uptimeMs,
+            EnergyConsumerDetails energyConsumerDetails) {
+        mHistoryCur.energyConsumerDetails = energyConsumerDetails;
         mHistoryCur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG;
         writeHistoryItem(elapsedRealtimeMs, uptimeMs);
     }
@@ -1293,7 +1293,7 @@
                 && mHistoryLastWritten.batteryPlugType == cur.batteryPlugType
                 && mHistoryLastWritten.batteryTemperature == cur.batteryTemperature
                 && mHistoryLastWritten.batteryVoltage == cur.batteryVoltage
-                && mHistoryLastWritten.measuredEnergyDetails == null
+                && mHistoryLastWritten.energyConsumerDetails == null
                 && mHistoryLastWritten.cpuUsageDetails == null) {
             // We can merge this new change in with the last one.  Merging is
             // allowed as long as only the states have changed, and within those states
@@ -1396,7 +1396,7 @@
         cur.eventCode = HistoryItem.EVENT_NONE;
         cur.eventTag = null;
         cur.tagsFirstOccurrence = false;
-        cur.measuredEnergyDetails = null;
+        cur.energyConsumerDetails = null;
         cur.cpuUsageDetails = null;
         if (DEBUG) {
             Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
@@ -1517,7 +1517,7 @@
         if (stateIntChanged) {
             firstToken |= BatteryStatsHistory.DELTA_STATE_FLAG;
         }
-        if (cur.measuredEnergyDetails != null) {
+        if (cur.energyConsumerDetails != null) {
             extensionFlags |= BatteryStatsHistory.EXTENSION_MEASURED_ENERGY_FLAG;
             if (!mMeasuredEnergyHeaderWritten) {
                 extensionFlags |= BatteryStatsHistory.EXTENSION_MEASURED_ENERGY_HEADER_FLAG;
@@ -1653,22 +1653,22 @@
         dest.writeDouble(cur.wifiRailChargeMah);
         if (extensionFlags != 0) {
             dest.writeInt(extensionFlags);
-            if (cur.measuredEnergyDetails != null) {
+            if (cur.energyConsumerDetails != null) {
                 if (DEBUG) {
-                    Slog.i(TAG, "WRITE DELTA: measuredEnergyDetails=" + cur.measuredEnergyDetails);
+                    Slog.i(TAG, "WRITE DELTA: measuredEnergyDetails=" + cur.energyConsumerDetails);
                 }
                 if (!mMeasuredEnergyHeaderWritten) {
-                    MeasuredEnergyDetails.EnergyConsumer[] consumers =
-                            cur.measuredEnergyDetails.consumers;
+                    EnergyConsumerDetails.EnergyConsumer[] consumers =
+                            cur.energyConsumerDetails.consumers;
                     dest.writeInt(consumers.length);
-                    for (MeasuredEnergyDetails.EnergyConsumer consumer : consumers) {
+                    for (EnergyConsumerDetails.EnergyConsumer consumer : consumers) {
                         dest.writeInt(consumer.type);
                         dest.writeInt(consumer.ordinal);
                         dest.writeString(consumer.name);
                     }
                     mMeasuredEnergyHeaderWritten = true;
                 }
-                mVarintParceler.writeLongArray(dest, cur.measuredEnergyDetails.chargeUC);
+                mVarintParceler.writeLongArray(dest, cur.energyConsumerDetails.chargeUC);
             }
 
             if (cur.cpuUsageDetails != null) {
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index b88116d..67eee4f 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -36,7 +36,7 @@
     private final BatteryStats.HistoryStepDetails mReadHistoryStepDetails =
             new BatteryStats.HistoryStepDetails();
     private final SparseArray<BatteryStats.HistoryTag> mHistoryTags = new SparseArray<>();
-    private BatteryStats.MeasuredEnergyDetails mMeasuredEnergyDetails;
+    private BatteryStats.EnergyConsumerDetails mEnergyConsumerDetails;
     private BatteryStats.CpuUsageDetails mCpuUsageDetails;
     private final BatteryStatsHistory.VarintParceler mVarintParceler =
             new BatteryStatsHistory.VarintParceler();
@@ -230,8 +230,8 @@
         if ((cur.states2 & BatteryStats.HistoryItem.STATE2_EXTENSIONS_FLAG) != 0) {
             final int extensionFlags = src.readInt();
             if ((extensionFlags & BatteryStatsHistory.EXTENSION_MEASURED_ENERGY_HEADER_FLAG) != 0) {
-                if (mMeasuredEnergyDetails == null) {
-                    mMeasuredEnergyDetails = new BatteryStats.MeasuredEnergyDetails();
+                if (mEnergyConsumerDetails == null) {
+                    mEnergyConsumerDetails = new BatteryStats.EnergyConsumerDetails();
                 }
 
                 final int consumerCount = src.readInt();
@@ -241,28 +241,28 @@
                             "EnergyConsumer count too high: " + consumerCount
                                     + ". Max = " + MAX_ENERGY_CONSUMER_COUNT);
                 }
-                mMeasuredEnergyDetails.consumers =
-                        new BatteryStats.MeasuredEnergyDetails.EnergyConsumer[consumerCount];
-                mMeasuredEnergyDetails.chargeUC = new long[consumerCount];
+                mEnergyConsumerDetails.consumers =
+                        new BatteryStats.EnergyConsumerDetails.EnergyConsumer[consumerCount];
+                mEnergyConsumerDetails.chargeUC = new long[consumerCount];
                 for (int i = 0; i < consumerCount; i++) {
-                    BatteryStats.MeasuredEnergyDetails.EnergyConsumer consumer =
-                            new BatteryStats.MeasuredEnergyDetails.EnergyConsumer();
+                    BatteryStats.EnergyConsumerDetails.EnergyConsumer consumer =
+                            new BatteryStats.EnergyConsumerDetails.EnergyConsumer();
                     consumer.type = src.readInt();
                     consumer.ordinal = src.readInt();
                     consumer.name = src.readString();
-                    mMeasuredEnergyDetails.consumers[i] = consumer;
+                    mEnergyConsumerDetails.consumers[i] = consumer;
                 }
             }
 
             if ((extensionFlags & BatteryStatsHistory.EXTENSION_MEASURED_ENERGY_FLAG) != 0) {
-                if (mMeasuredEnergyDetails == null) {
+                if (mEnergyConsumerDetails == null) {
                     throw new IllegalStateException("MeasuredEnergyDetails without a header");
                 }
 
-                mVarintParceler.readLongArray(src, mMeasuredEnergyDetails.chargeUC);
-                cur.measuredEnergyDetails = mMeasuredEnergyDetails;
+                mVarintParceler.readLongArray(src, mEnergyConsumerDetails.chargeUC);
+                cur.energyConsumerDetails = mEnergyConsumerDetails;
             } else {
-                cur.measuredEnergyDetails = null;
+                cur.energyConsumerDetails = null;
             }
 
             if ((extensionFlags & BatteryStatsHistory.EXTENSION_CPU_USAGE_HEADER_FLAG) != 0) {
@@ -295,7 +295,7 @@
                 cur.cpuUsageDetails = null;
             }
         } else {
-            cur.measuredEnergyDetails = null;
+            cur.energyConsumerDetails = null;
             cur.cpuUsageDetails = null;
         }
     }
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index 134a917..63785f2 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -139,6 +139,15 @@
     }
 
     @Override
+    public boolean isDeviceContext() {
+        Context context = mContext.get();
+        if (context != null) {
+            return context.isDeviceContext();
+        }
+        return false;
+    }
+
+    @Override
     public boolean isConfigurationContext() {
         Context context = mContext.get();
         if (context != null) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 145aeaf..b5b27f52 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -274,7 +274,6 @@
     private boolean mApplyFloatingVerticalInsets = false;
     private boolean mApplyFloatingHorizontalInsets = false;
 
-    private int mResizeMode = RESIZE_MODE_INVALID;
     private final int mResizeShadowSize;
     private final Paint mVerticalResizeShadowPaint = new Paint();
     private final Paint mHorizontalResizeShadowPaint = new Paint();
@@ -808,9 +807,7 @@
         updateElevation();
         mAllowUpdateElevation = true;
 
-        if (changed
-                && (mResizeMode == RESIZE_MODE_DOCKED_DIVIDER
-                    || mDrawLegacyNavigationBarBackground)) {
+        if (changed && mDrawLegacyNavigationBarBackground) {
             getViewRootImpl().requestInvalidateRootRenderNode();
         }
     }
@@ -2392,7 +2389,7 @@
 
     @Override
     public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
-            Rect stableInsets, int resizeMode) {
+            Rect stableInsets) {
         if (mWindow.isDestroyed()) {
             // If the owner's window is gone, we should not be able to come here anymore.
             releaseThreadedRenderer();
@@ -2418,7 +2415,6 @@
 
             updateColorViews(null /* insets */, false);
         }
-        mResizeMode = resizeMode;
         getViewRootImpl().requestInvalidateRootRenderNode();
     }
 
@@ -2426,7 +2422,6 @@
     public void onWindowDragResizeEnd() {
         releaseThreadedRenderer();
         updateColorViews(null /* insets */, false);
-        mResizeMode = RESIZE_MODE_INVALID;
         getViewRootImpl().requestInvalidateRootRenderNode();
     }
 
@@ -2471,9 +2466,7 @@
     }
 
     private void drawResizingShadowIfNeeded(RecordingCanvas canvas) {
-        if (mResizeMode != RESIZE_MODE_DOCKED_DIVIDER || mWindow.mIsFloating
-                || mWindow.isTranslucent()
-                || mWindow.isShowingWallpaper()) {
+        if (mWindow.mIsFloating || mWindow.isTranslucent() || mWindow.isShowingWallpaper()) {
             return;
         }
         canvas.save();
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/EnergyConsumerStats.java
similarity index 96%
rename from core/java/com/android/internal/power/MeasuredEnergyStats.java
rename to core/java/com/android/internal/power/EnergyConsumerStats.java
index 290cba0..325df57 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/EnergyConsumerStats.java
@@ -37,15 +37,14 @@
 import java.util.Arrays;
 
 /**
- * Tracks the measured charge consumption of various subsystems according to their
+ * Tracks the charge consumption of various subsystems according to their
  * {@link StandardPowerBucket} or custom power bucket (which is tied to
  * {@link android.hardware.power.stats.EnergyConsumer.ordinal}).
  *
- * This class doesn't use a TimeBase, and instead requires manually decisions about when to
+ * This class doesn't use a TimeBase, and instead requires manual decisions about when to
  * accumulate since it is trivial. However, in the future, a TimeBase could be used instead.
  */
-@VisibleForTesting
-public class MeasuredEnergyStats {
+public class EnergyConsumerStats {
     private static final String TAG = "MeasuredEnergyStats";
 
     // Note: {@link BatteryStats#VERSION} MUST be updated if standard
@@ -205,7 +204,7 @@
          */
         private String getBucketName(int index) {
             if (isValidStandardBucket(index)) {
-                return DebugUtils.valueToString(MeasuredEnergyStats.class, "POWER_BUCKET_", index);
+                return DebugUtils.valueToString(EnergyConsumerStats.class, "POWER_BUCKET_", index);
             }
             final int customBucket = indexToCustomBucket(index);
             StringBuilder name = new StringBuilder().append("CUSTOM_").append(customBucket);
@@ -242,7 +241,7 @@
      * supportedStandardBuckets must be of size {@link #NUMBER_STANDARD_POWER_BUCKETS}.
      * numCustomBuckets >= 0 is the number of (non-standard) custom power buckets on the device.
      */
-    public MeasuredEnergyStats(MeasuredEnergyStats.Config config) {
+    public EnergyConsumerStats(EnergyConsumerStats.Config config) {
         mConfig = config;
         final int numTotalBuckets = config.getNumberOfBuckets();
         mAccumulatedChargeMicroCoulomb = new long[numTotalBuckets];
@@ -259,15 +258,15 @@
      * Reads a MeasuredEnergyStats from the supplied Parcel.
      */
     @Nullable
-    public static MeasuredEnergyStats createFromParcel(Config config, Parcel in) {
+    public static EnergyConsumerStats createFromParcel(Config config, Parcel in) {
         if (!in.readBoolean()) {
             return null;
         }
-        return new MeasuredEnergyStats(config, in);
+        return new EnergyConsumerStats(config, in);
     }
 
     /** Construct from parcel. */
-    public MeasuredEnergyStats(MeasuredEnergyStats.Config config, Parcel in) {
+    public EnergyConsumerStats(EnergyConsumerStats.Config config, Parcel in) {
         mConfig = config;
 
         final int size = in.readInt();
@@ -533,21 +532,22 @@
      * possible (not necessarily supported) standard and custom buckets.
      *
      * Corresponding write performed by
-     * {@link #writeSummaryToParcel(MeasuredEnergyStats, Parcel)}.
+     * {@link #writeSummaryToParcel(EnergyConsumerStats, Parcel)}.
      *
      * @return a new MeasuredEnergyStats object as described.
      *         Returns null if the stats contain no non-0 information (such as if template is null
      *         or if the parcel indicates there is no data to populate).
      */
-    public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(
-            @Nullable Config config, Parcel in) {
+    @Nullable
+    public static EnergyConsumerStats createAndReadSummaryFromParcel(@Nullable Config config,
+            Parcel in) {
         final int arraySize = in.readInt();
         // Check if any MeasuredEnergyStats exists on the parcel
         if (arraySize == 0) return null;
 
         if (config == null) {
             // Nothing supported anymore. Create placeholder object just to consume the parcel data.
-            final MeasuredEnergyStats mes = new MeasuredEnergyStats(
+            final EnergyConsumerStats mes = new EnergyConsumerStats(
                     new Config(new boolean[arraySize], null, new int[0], new String[]{""}));
             mes.readSummaryFromParcel(in);
             return null;
@@ -557,12 +557,12 @@
             Slog.wtf(TAG, "Size of MeasuredEnergyStats parcel (" + arraySize
                     + ") does not match config (" + config.getNumberOfBuckets() + ").");
             // Something is horribly wrong. Just consume the parcel and return null.
-            final MeasuredEnergyStats mes = new MeasuredEnergyStats(config);
+            final EnergyConsumerStats mes = new EnergyConsumerStats(config);
             mes.readSummaryFromParcel(in);
             return null;
         }
 
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+        final EnergyConsumerStats stats = new EnergyConsumerStats(config);
         stats.readSummaryFromParcel(in);
         if (stats.containsInterestingData()) {
             return stats;
@@ -585,7 +585,7 @@
      *
      * Corresponding read performed by {@link #createAndReadSummaryFromParcel}.
      */
-    public static void writeSummaryToParcel(@Nullable MeasuredEnergyStats stats, Parcel dest) {
+    public static void writeSummaryToParcel(@Nullable EnergyConsumerStats stats, Parcel dest) {
         if (stats == null) {
             dest.writeInt(0);
             return;
@@ -607,7 +607,7 @@
     }
 
     /** Reset accumulated charges of the given stats. */
-    public static void resetIfNotNull(@Nullable MeasuredEnergyStats stats) {
+    public static void resetIfNotNull(@Nullable EnergyConsumerStats stats) {
         if (stats != null) stats.reset();
     }
 
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 2ac4309..4a5ed7e 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -53,7 +53,7 @@
     @Override
     public void resized(ClientWindowFrames frames, boolean reportDraw,
             MergedConfiguration mergedConfiguration, InsetsState insetsState, boolean forceLayout,
-            boolean alwaysConsumeSystemBars, int displayId, int seqId, int resizeMode) {
+            boolean alwaysConsumeSystemBars, int displayId, int seqId, boolean dragResizing) {
         if (reportDraw) {
             try {
                 mSession.finishDrawing(this, null /* postDrawTransaction */, seqId);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 65f5522..91a5d3a 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1170,24 +1170,6 @@
         getTrustManager().reportEnabledTrustAgentsChanged(userHandle);
     }
 
-    public boolean isCredentialRequiredToDecrypt(boolean defaultValue) {
-        final int value = Settings.Global.getInt(mContentResolver,
-                Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, -1);
-        return value == -1 ? defaultValue : (value != 0);
-    }
-
-    public void setCredentialRequiredToDecrypt(boolean required) {
-        if (!(getUserManager().isSystemUser() || getUserManager().isPrimaryUser())) {
-            throw new IllegalStateException(
-                    "Only the system or primary user may call setCredentialRequiredForDecrypt()");
-        }
-
-        if (isDeviceEncryptionEnabled()){
-            Settings.Global.putInt(mContext.getContentResolver(),
-               Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, required ? 1 : 0);
-        }
-    }
-
     private void throwIfCalledOnMainThread() {
         if (Looper.getMainLooper().isCurrentThread()) {
             throw new IllegalStateException("should not be called from the main thread.");
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 28ac464..19cb30e4 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -3201,6 +3201,30 @@
     return nativeToJavaStatus(status);
 }
 
+static jboolean android_media_AudioSystem_supportsBluetoothVariableLatency(JNIEnv *env,
+                                                                           jobject thiz) {
+    bool supports;
+    if (AudioSystem::supportsBluetoothVariableLatency(&supports) != NO_ERROR) {
+        supports = false;
+    }
+    return supports;
+}
+
+static int android_media_AudioSystem_setBluetoothVariableLatencyEnabled(JNIEnv *env, jobject thiz,
+                                                                        jboolean enabled) {
+    return (jint)check_AudioSystem_Command(
+            AudioSystem::setBluetoothVariableLatencyEnabled(enabled));
+}
+
+static jboolean android_media_AudioSystem_isBluetoothVariableLatencyEnabled(JNIEnv *env,
+                                                                            jobject thiz) {
+    bool enabled;
+    if (AudioSystem::isBluetoothVariableLatencyEnabled(&enabled) != NO_ERROR) {
+        enabled = false;
+    }
+    return enabled;
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gMethods[] =
@@ -3364,7 +3388,13 @@
          {"getPreferredMixerAttributes", "(Landroid/media/AudioAttributes;ILjava/util/List;)I",
           (void *)android_media_AudioSystem_getPreferredMixerAttributes},
          {"clearPreferredMixerAttributes", "(Landroid/media/AudioAttributes;II)I",
-          (void *)android_media_AudioSystem_clearPreferredMixerAttributes}};
+          (void *)android_media_AudioSystem_clearPreferredMixerAttributes},
+         {"supportsBluetoothVariableLatency", "()Z",
+          (void *)android_media_AudioSystem_supportsBluetoothVariableLatency},
+         {"setBluetoothVariableLatencyEnabled", "(Z)I",
+          (void *)android_media_AudioSystem_setBluetoothVariableLatencyEnabled},
+         {"isBluetoothVariableLatencyEnabled", "()Z",
+          (void *)android_media_AudioSystem_isBluetoothVariableLatencyEnabled}};
 
 static const JNINativeMethod gEventHandlerMethods[] = {
     {"native_setup",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 78a6a66..6dd7073 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3632,7 +3632,7 @@
     <permission android:name="android.permission.SET_APP_SPECIFIC_LOCALECONFIG"
         android:protectionLevel="signature" />
 
-    <!-- @hide Allows an application to monitor {@link android.provider.Settings.Config} access.
+    <!-- @SystemApi @hide Allows an application to monitor {@link android.provider.Settings.Config} access.
     <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS"
         android:protectionLevel="signature"/>
@@ -4984,7 +4984,7 @@
     <permission android:name="android.permission.ROTATE_SURFACE_FLINGER"
         android:protectionLevel="signature|recents" />
 
-    <!-- @SystemApi Allows an application to provide hints to SurfaceFlinger that can influence
+    <!-- Allows an application to provide hints to SurfaceFlinger that can influence
          its wakes up time to compose the next frame. This is a subset of the capabilities granted
          by {@link #ACCESS_SURFACE_FLINGER}.
          <p>Not for use by third-party applications.
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index d4644c5..44f85da 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2963,6 +2963,18 @@
              Context.createAttributionContext() using the first attribution tag
              contained here. -->
         <attr name="attributionTags" />
+        <!-- If true, and this is an {@link android.R.attr#isolatedProcess} service, the service
+             is allowed to be bound in a shared isolated process with other isolated services.
+             Note that these other isolated services can also belong to other apps from different
+             vendors.
+             <p>
+             Shared isolated processes are created when using the
+             {@link android.content.Context#BIND_SHARED_ISOLATED_PROCESS) during service binding.
+             <p>
+             Note that when this flag is used, the {@link android.R.attr#process} attribute is
+             ignored when the process is bound into a shared isolated process by a client.
+        -->
+        <attr name="allowSharedIsolatedProcess" format="boolean" />
     </declare-styleable>
 
     <!-- @hide The <code>apex-system-service</code> tag declares an apex system service
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fb10d3d..9cf9501 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2011,6 +2011,9 @@
     <!-- The default volume for the ring stream -->
     <integer name="config_audio_ring_vol_default">5</integer>
 
+    <!-- Enable sound dose computation and warnings -->
+    <bool name="config_audio_csd_enabled_default">false</bool>
+
     <!-- The default value for whether head tracking for
          spatial audio is enabled for a newly connected audio device -->
     <bool name="config_spatial_audio_head_tracking_enabled_default">false</bool>
@@ -2130,6 +2133,8 @@
     <string name="config_systemAutomotiveCalendarSyncManager" translatable="false"></string>
     <!-- The name of the package that will hold the default automotive navigation role. -->
     <string name="config_defaultAutomotiveNavigation" translatable="false"></string>
+    <!-- The name of the package that will hold the system wear health service role. -->
+    <string name="config_systemWearHealthService" translatable="false"></string>
 
     <!-- The name of the package that will handle updating the device management role. -->
     <string name="config_devicePolicyManagementUpdater" translatable="false"></string>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index d7354ea..2924ddf 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -121,6 +121,7 @@
     <public name="visualQueryDetectionService" />
     <public name="physicalKeyboardHintLanguageTag" />
     <public name="physicalKeyboardHintLayoutType" />
+    <public name="allowSharedIsolatedProcess" />
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01cd0000">
@@ -134,6 +135,8 @@
   </staging-public-group>
 
   <staging-public-group type="string" first-id="0x01cb0000">
+    <!-- @hide @SystemApi -->
+    <public name="config_systemWearHealthService" />
   </staging-public-group>
 
   <staging-public-group type="dimen" first-id="0x01ca0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 87298d5..388bc95 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4572,6 +4572,28 @@
        "Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."
     </string>
 
+    <!-- Message shown in dialog when user goes over a multiple of 100% of the safe weekly dose -->
+    <string name="csd_dose_reached_warning" product="default">
+        "Warning,\nYou have exceeded the amount of loud sound signals one can safely listen to in a week over headphones.\n\nGoing over this limit will permanently damage your hearing."
+    </string>
+
+    <!-- Message shown in dialog when user goes over 500% of the safe weekly dose and volume is
+    automatically lowered -->
+    <string name="csd_dose_repeat_warning" product="default">
+        "Warning,\nYou have exceeded 5 times the amount of loud sound signals one can safely listen to in a week over headphones.\n\nVolume has been lowered to protect your hearing."
+    </string>
+
+    <!-- Message shown in dialog when user's dose enters RS2 -->
+    <string name="csd_entering_RS2_warning" product="default">
+        "The level at which you are listening to media can result in hearing damage when sustained over long periods of time.\n\nContinuing to play at this level for long periods of time could damage your hearing."
+    </string>
+
+    <!-- Message shown in dialog when user is momentarily listening to unsafely loud content
+    over headphones -->
+    <string name="csd_momentary_exposure_warning" product="default">
+        "Warning,\nYou are currently listening to loud content played at an unsafe level.\n\nContinuing to listen this loud will permanently damage your hearing."
+    </string>
+
     <!-- Dialog title for dialog shown when the accessibility shortcut is activated, and we want
      to confirm that the user understands what's going to happen-->
     <string name="accessibility_shortcut_warning_dialog_title">Use Accessibility Shortcut?</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6fa5534..068bb91 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -283,6 +283,7 @@
   <java-symbol type="attr" name="autofillSaveCustomSubtitleMaxHeight"/>
   <java-symbol type="bool" name="action_bar_embed_tabs" />
   <java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" />
+  <java-symbol type="bool" name="config_audio_csd_enabled_default" />
   <java-symbol type="integer" name="config_audio_notif_vol_default" />
   <java-symbol type="integer" name="config_audio_notif_vol_steps" />
   <java-symbol type="integer" name="config_audio_ring_vol_default" />
@@ -659,6 +660,10 @@
   <java-symbol type="string" name="contentServiceSync" />
   <java-symbol type="string" name="contentServiceSyncNotificationTitle" />
   <java-symbol type="string" name="contentServiceTooManyDeletesNotificationDesc" />
+  <java-symbol type="string" name="csd_dose_reached_warning" />
+  <java-symbol type="string" name="csd_dose_repeat_warning" />
+  <java-symbol type="string" name="csd_entering_RS2_warning" />
+  <java-symbol type="string" name="csd_momentary_exposure_warning" />
   <java-symbol type="string" name="date_and_time" />
   <java-symbol type="string" name="date_picker_decrement_day_button" />
   <java-symbol type="string" name="date_picker_decrement_month_button" />
@@ -2256,6 +2261,7 @@
   <java-symbol type="drawable" name="scrubber_control_selector_holo" />
   <java-symbol type="drawable" name="scrubber_progress_horizontal_holo_dark" />
   <java-symbol type="drawable" name="progress_small_material" />
+  <java-symbol type="drawable" name="ic_chevron_end" />
   <java-symbol type="string" name="chooseUsbActivity" />
   <java-symbol type="string" name="ext_media_badremoval_notification_message" />
   <java-symbol type="string" name="ext_media_badremoval_notification_title" />
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_amp_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_amp_24.xml
deleted file mode 100644
index 1f57318..0000000
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_amp_24.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24"
-    android:tint="#d14d2c">
-  <path
-      android:fillColor="@android:color/white"
-      android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11.17,19.5h-0.83l0.82,-5.83 -4.18,0.01 5.85,-9.17h0.83l-0.84,5.84h4.17l-5.82,9.15z"/>
-</vector>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_custom_24.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_energy_24.xml
similarity index 70%
rename from core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_custom_24.xml
rename to core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_energy_24.xml
index 39f9689..11f8d7b 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_custom_24.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/drawable/gm_energy_24.xml
@@ -1,9 +1,9 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24"
-    android:tint="#d14d2c">
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="#d14d2c">
   <path
       android:fillColor="@android:color/white"
       android:pathData="M22,9L22,7h-2L20,5c0,-1.1 -0.9,-2 -2,-2L4,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2v-2h2v-2h-2v-2h2v-2h-2L20,9h2zM18,19L4,19L4,5h14v14zM6,13h5v4L6,17v-4zM12,7h4v3h-4L12,7zM6,7h5v5L6,12L6,7zM12,11h4v6h-4v-6z"/>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml b/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml
index 2dbe48b..6cc70bd 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/res/values/colors.xml
@@ -17,5 +17,5 @@
 
 <resources>
     <color name="battery_consumer_bg_power_profile">#ffffff</color>
-    <color name="battery_consumer_bg_measured_energy">#fff5eb</color>
+    <color name="battery_consumer_bg_energy_consumption">#fff5eb</color>
 </resources>
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
index 19bb718..f691a1b 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
@@ -33,15 +33,15 @@
 
     enum EntryType {
         UID_TOTAL_POWER,
-        UID_POWER_MODELED,
-        UID_POWER_MODELED_PROCESS_STATE,
-        UID_POWER_MEASURED,
-        UID_POWER_MEASURED_PROCESS_STATE,
+        UID_POWER_PROFILE,
+        UID_POWER_PROFILE_PROCESS_STATE,
+        UID_POWER_ENERGY_CONSUMPTION,
+        UID_POWER_ENERGY_PROCESS_STATE,
         UID_POWER_CUSTOM,
         UID_DURATION,
         DEVICE_TOTAL_POWER,
         DEVICE_POWER_MODELED,
-        DEVICE_POWER_MEASURED,
+        DEVICE_POWER_ENERGY_CONSUMPTION,
         DEVICE_POWER_CUSTOM,
         DEVICE_DURATION,
     }
@@ -111,12 +111,12 @@
                             BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
                             .getConsumedPower());
         } else {
-            addEntry("Consumed (measured)", EntryType.UID_TOTAL_POWER,
+            addEntry("Consumed (PowerStats)", EntryType.UID_TOTAL_POWER,
                     requestedBatteryConsumer.getConsumedPower(),
                     batteryUsageStats.getAggregateBatteryConsumer(
                             BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
                             .getConsumedPower());
-            addEntry("Consumed (modeled)", EntryType.UID_TOTAL_POWER,
+            addEntry("Consumed (PowerProfile)", EntryType.UID_TOTAL_POWER,
                     requestedModeledBatteryConsumer.getConsumedPower(),
                     modeledBatteryUsageStats.getAggregateBatteryConsumer(
                             BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS)
@@ -128,21 +128,21 @@
             final int powerModel = requestedBatteryConsumer.getPowerModel(component);
             if (powerModel == BatteryConsumer.POWER_MODEL_POWER_PROFILE
                     || powerModel == BatteryConsumer.POWER_MODEL_UNDEFINED) {
-                addEntry(metricTitle, EntryType.UID_POWER_MODELED,
+                addEntry(metricTitle, EntryType.UID_POWER_PROFILE,
                         requestedBatteryConsumer.getConsumedPower(component),
                         totalPowerByComponentMah[component]);
-                addProcessStateEntries(metricTitle, EntryType.UID_POWER_MODELED_PROCESS_STATE,
+                addProcessStateEntries(metricTitle, EntryType.UID_POWER_PROFILE_PROCESS_STATE,
                         requestedBatteryConsumer, component);
             } else {
-                addEntry(metricTitle + " (measured)", EntryType.UID_POWER_MEASURED,
+                addEntry(metricTitle + " (PowerStats)", EntryType.UID_POWER_ENERGY_CONSUMPTION,
                         requestedBatteryConsumer.getConsumedPower(component),
                         totalPowerByComponentMah[component]);
-                addProcessStateEntries(metricTitle, EntryType.UID_POWER_MEASURED_PROCESS_STATE,
+                addProcessStateEntries(metricTitle, EntryType.UID_POWER_ENERGY_PROCESS_STATE,
                         requestedBatteryConsumer, component);
-                addEntry(metricTitle + " (modeled)", EntryType.UID_POWER_MODELED,
+                addEntry(metricTitle + " (PowerProfile)", EntryType.UID_POWER_PROFILE,
                         requestedModeledBatteryConsumer.getConsumedPower(component),
                         totalModeledPowerByComponentMah[component]);
-                addProcessStateEntries(metricTitle, EntryType.UID_POWER_MODELED_PROCESS_STATE,
+                addProcessStateEntries(metricTitle, EntryType.UID_POWER_PROFILE_PROCESS_STATE,
                         requestedModeledBatteryConsumer, component);
             }
         }
@@ -150,7 +150,7 @@
         for (int component = 0; component < customComponentCount; component++) {
             final String name = requestedBatteryConsumer.getCustomPowerComponentName(
                     BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component);
-            addEntry(name + " (custom)", EntryType.UID_POWER_CUSTOM,
+            addEntry(name + " (PowerStats)", EntryType.UID_POWER_CUSTOM,
                     requestedBatteryConsumer.getConsumedPowerForCustomComponent(
                             BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component),
                     totalCustomPowerByComponentMah[component]
@@ -223,10 +223,10 @@
                     deviceBatteryConsumer.getConsumedPower(),
                     appsBatteryConsumer.getConsumedPower());
         } else {
-            addEntry("Consumed (measured)", EntryType.DEVICE_TOTAL_POWER,
+            addEntry("Consumed (PowerStats)", EntryType.DEVICE_TOTAL_POWER,
                     deviceBatteryConsumer.getConsumedPower(),
                     appsBatteryConsumer.getConsumedPower());
-            addEntry("Consumed (modeled)", EntryType.DEVICE_TOTAL_POWER,
+            addEntry("Consumed (PowerProfile)", EntryType.DEVICE_TOTAL_POWER,
                     modeledDeviceBatteryConsumer.getConsumedPower(),
                     modeledAppsBatteryConsumer.getConsumedPower());
         }
@@ -244,10 +244,10 @@
                         deviceBatteryConsumer.getConsumedPower(component),
                         appsBatteryConsumer.getConsumedPower(component));
             } else {
-                addEntry(metricTitle + " (measured)", EntryType.DEVICE_POWER_MEASURED,
+                addEntry(metricTitle + " (PowerStats)", EntryType.DEVICE_POWER_ENERGY_CONSUMPTION,
                         deviceBatteryConsumer.getConsumedPower(component),
                         appsBatteryConsumer.getConsumedPower(component));
-                addEntry(metricTitle + " (modeled)", EntryType.DEVICE_POWER_MODELED,
+                addEntry(metricTitle + " (PowerProfile)", EntryType.DEVICE_POWER_MODELED,
                         modeledDeviceBatteryConsumer.getConsumedPower(component),
                         modeledAppsBatteryConsumer.getConsumedPower(component));
             }
@@ -258,7 +258,7 @@
         for (int component = 0; component < customComponentCount; component++) {
             final String name = deviceBatteryConsumer.getCustomPowerComponentName(
                     BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component);
-            addEntry(name + " (custom)", EntryType.DEVICE_POWER_CUSTOM,
+            addEntry(name + " (PowerStats)", EntryType.DEVICE_POWER_CUSTOM,
                     deviceBatteryConsumer.getConsumedPowerForCustomComponent(
                             BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + component),
                     appsBatteryConsumer.getConsumedPowerForCustomComponent(
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
index 0f45c3b..e165c49 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryStatsViewerActivity.java
@@ -285,38 +285,38 @@
                     setPowerText(viewHolder.value1TextView, entry.value1);
                     setProportionText(viewHolder.value2TextView, entry);
                     break;
-                case UID_POWER_MODELED:
+                case UID_POWER_PROFILE:
                     setTitleIconAndBackground(viewHolder, entry.title,
                             R.drawable.gm_calculate_24,
                             R.color.battery_consumer_bg_power_profile);
                     setPowerText(viewHolder.value1TextView, entry.value1);
                     setProportionText(viewHolder.value2TextView, entry);
                     break;
-                case UID_POWER_MODELED_PROCESS_STATE:
+                case UID_POWER_PROFILE_PROCESS_STATE:
                     setTitleIconAndBackground(viewHolder, "    " + entry.title,
                             R.drawable.gm_calculate_24,
                             R.color.battery_consumer_bg_power_profile);
                     setPowerText(viewHolder.value1TextView, entry.value1);
                     viewHolder.value2TextView.setVisibility(View.INVISIBLE);
                     break;
-                case UID_POWER_MEASURED:
+                case UID_POWER_ENERGY_CONSUMPTION:
                     setTitleIconAndBackground(viewHolder, entry.title,
-                            R.drawable.gm_amp_24,
-                            R.color.battery_consumer_bg_measured_energy);
+                            R.drawable.gm_energy_24,
+                            R.color.battery_consumer_bg_energy_consumption);
                     setPowerText(viewHolder.value1TextView, entry.value1);
                     setProportionText(viewHolder.value2TextView, entry);
                     break;
-                case UID_POWER_MEASURED_PROCESS_STATE:
+                case UID_POWER_ENERGY_PROCESS_STATE:
                     setTitleIconAndBackground(viewHolder, "    " + entry.title,
-                            R.drawable.gm_amp_24,
-                            R.color.battery_consumer_bg_measured_energy);
+                            R.drawable.gm_energy_24,
+                            R.color.battery_consumer_bg_energy_consumption);
                     setPowerText(viewHolder.value1TextView, entry.value1);
                     viewHolder.value2TextView.setVisibility(View.INVISIBLE);
                     break;
                 case UID_POWER_CUSTOM:
                     setTitleIconAndBackground(viewHolder, entry.title,
-                            R.drawable.gm_custom_24,
-                            R.color.battery_consumer_bg_measured_energy);
+                            R.drawable.gm_energy_24,
+                            R.color.battery_consumer_bg_energy_consumption);
                     setPowerText(viewHolder.value1TextView, entry.value1);
                     setProportionText(viewHolder.value2TextView, entry);
                     break;
@@ -339,17 +339,17 @@
                     setPowerText(viewHolder.value1TextView, entry.value1);
                     setPowerText(viewHolder.value2TextView, entry.value2);
                     break;
-                case DEVICE_POWER_MEASURED:
+                case DEVICE_POWER_ENERGY_CONSUMPTION:
                     setTitleIconAndBackground(viewHolder, entry.title,
-                            R.drawable.gm_amp_24,
-                            R.color.battery_consumer_bg_measured_energy);
+                            R.drawable.gm_energy_24,
+                            R.color.battery_consumer_bg_energy_consumption);
                     setPowerText(viewHolder.value1TextView, entry.value1);
                     setPowerText(viewHolder.value2TextView, entry.value2);
                     break;
                 case DEVICE_POWER_CUSTOM:
                     setTitleIconAndBackground(viewHolder, entry.title,
-                            R.drawable.gm_custom_24,
-                            R.color.battery_consumer_bg_measured_energy);
+                            R.drawable.gm_energy_24,
+                            R.color.battery_consumer_bg_energy_consumption);
                     setPowerText(viewHolder.value1TextView, entry.value1);
                     setPowerText(viewHolder.value2TextView, entry.value2);
                     break;
diff --git a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
index f82523c..0676f89 100644
--- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
+++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
@@ -275,11 +275,11 @@
 
         uidBuilder.setConsumedPower(keyFg, 9100, BatteryConsumer.POWER_MODEL_POWER_PROFILE)
                 .setUsageDurationMillis(keyFg, 8100)
-                .setConsumedPower(keyBg, 9200, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY)
+                .setConsumedPower(keyBg, 9200, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
                 .setUsageDurationMillis(keyBg, 8200)
-                .setConsumedPower(keyFgs, 9300, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY)
+                .setConsumedPower(keyFgs, 9300, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
                 .setUsageDurationMillis(keyFgs, 8300)
-                .setConsumedPower(keyCached, 9400, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY)
+                .setConsumedPower(keyCached, 9400, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
                 .setUsageDurationMillis(keyFgs, 8400);
 
         builder.getOrCreateUidBatteryConsumerBuilder(UID_1)
@@ -302,7 +302,7 @@
                         BatteryConsumer.POWER_MODEL_POWER_PROFILE) // Empty
                 .setConsumedPower(
                         BatteryConsumer.POWER_COMPONENT_CAMERA, 20150,
-                        BatteryConsumer.POWER_MODEL_MEASURED_ENERGY)
+                        BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION)
                 .setConsumedPowerForCustomComponent(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 20200)
                 .setUsageDurationMillis(
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 9b8a0e9..958fdc6 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -60,7 +60,7 @@
 public class ImeInsetsSourceConsumerTest {
 
     Context mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-    ImeInsetsSourceConsumer mImeConsumer;
+    InsetsSourceConsumer mImeConsumer;
     @Spy InsetsController mController;
     SurfaceControl mLeash;
 
@@ -86,7 +86,7 @@
                     false,
                     TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
                     SOFT_INPUT_ADJUST_RESIZE, 0, 0);
-            mImeConsumer = (ImeInsetsSourceConsumer) mController.getSourceConsumer(ITYPE_IME);
+            mImeConsumer = mController.getImeSourceConsumer();
         });
     }
 
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 597df0b..cc5f7f8 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -89,7 +89,8 @@
         mInsetsState = new InsetsState();
         mInsetsState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 500, 100));
         mInsetsState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(400, 0, 500, 500));
-        InsetsSourceConsumer topConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mInsetsState,
+        InsetsSourceConsumer topConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR,
+                WindowInsets.Type.statusBars(), mInsetsState,
                 () -> mMockTransaction, mMockController);
         topConsumer.setControl(
                 new InsetsSourceControl(ITYPE_STATUS_BAR, WindowInsets.Type.statusBars(),
@@ -97,7 +98,8 @@
                 new int[1], new int[1]);
 
         InsetsSourceConsumer navConsumer = new InsetsSourceConsumer(ITYPE_NAVIGATION_BAR,
-                mInsetsState, () -> mMockTransaction, mMockController);
+                WindowInsets.Type.navigationBars(), mInsetsState,
+                () -> mMockTransaction, mMockController);
         navConsumer.setControl(
                 new InsetsSourceControl(ITYPE_NAVIGATION_BAR, WindowInsets.Type.navigationBars(),
                         mNavLeash, true, new Point(400, 0), Insets.of(0, 0, 100, 0)),
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 88249ad..c917302 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -100,6 +100,9 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class InsetsControllerTest {
+    private InsetsSource mStatusSource;
+    private InsetsSource mNavSource;
+    private InsetsSource mImeSource;
     private InsetsController mController;
     private SurfaceSession mSession = new SurfaceSession();
     private SurfaceControl mLeash;
@@ -125,10 +128,10 @@
             mTestClock = new OffsettableClock();
             mTestHandler = new TestHandler(null, mTestClock);
             mTestHost = spy(new TestHost(mViewRoot));
-            mController = new InsetsController(mTestHost, (controller, type) -> {
-                if (type == ITYPE_IME) {
-                    return new InsetsSourceConsumer(type, controller.getState(),
-                            Transaction::new, controller) {
+            mController = new InsetsController(mTestHost, (controller, source) -> {
+                if (source.getType() == ime()) {
+                    return new InsetsSourceConsumer(source.getId(), source.getType(),
+                            controller.getState(), Transaction::new, controller) {
 
                         private boolean mImeRequestedShow;
 
@@ -143,18 +146,25 @@
                         }
                     };
                 } else {
-                    return new InsetsSourceConsumer(type, controller.getState(), Transaction::new,
-                            controller);
+                    return new InsetsSourceConsumer(source.getId(), source.getType(),
+                            controller.getState(), Transaction::new, controller);
                 }
             }, mTestHandler);
             final Rect rect = new Rect(5, 5, 5, 5);
-            mController.getState().getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 10));
-            mController.getState().getSource(ITYPE_NAVIGATION_BAR).setFrame(
-                    new Rect(0, 90, 100, 100));
-            mController.getState().getSource(ITYPE_IME).setFrame(new Rect(0, 50, 100, 100));
-            mController.getState().setDisplayFrame(new Rect(0, 0, 100, 100));
-            mController.getState().setDisplayCutout(new DisplayCutout(
+            mStatusSource = new InsetsSource(ITYPE_STATUS_BAR, statusBars());
+            mStatusSource.setFrame(new Rect(0, 0, 100, 10));
+            mNavSource = new InsetsSource(ITYPE_NAVIGATION_BAR, navigationBars());
+            mNavSource.setFrame(new Rect(0, 90, 100, 100));
+            mImeSource = new InsetsSource(ITYPE_IME, ime());
+            mImeSource.setFrame(new Rect(0, 0, 100, 10));
+            InsetsState state = new InsetsState();
+            state.addSource(mStatusSource);
+            state.addSource(mNavSource);
+            state.addSource(mImeSource);
+            state.setDisplayFrame(new Rect(0, 0, 100, 100));
+            state.setDisplayCutout(new DisplayCutout(
                     Insets.of(10, 10, 10, 10), rect, rect, rect, rect));
+            mController.onStateChanged(state);
             mController.calculateInsets(
                     false,
                     false,
@@ -168,7 +178,7 @@
     @Test
     public void testControlsChanged() {
         mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR, statusBars()));
-        assertNotNull(mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl().getLeash());
+        assertNotNull(mController.getSourceConsumer(mStatusSource).getControl().getLeash());
         mController.addOnControllableInsetsChangedListener(
                 ((controller, typeMask) -> assertEquals(statusBars(), typeMask)));
     }
@@ -180,7 +190,7 @@
         mController.addOnControllableInsetsChangedListener(listener);
         mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR, statusBars()));
         mController.onControlsChanged(new InsetsSourceControl[0]);
-        assertNull(mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl());
+        assertNull(mController.getSourceConsumer(mStatusSource).getControl());
         InOrder inOrder = Mockito.inOrder(listener);
         inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(0));
         inOrder.verify(listener).onControllableInsetsChanged(eq(mController), eq(statusBars()));
@@ -240,7 +250,7 @@
             WindowInsetsAnimationControlListener loggingListener =
                     mock(WindowInsetsAnimationControlListener.class);
             mController.setSystemDrivenInsetsAnimationLoggingListener(loggingListener);
-            mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained(true);
+            mController.getSourceConsumer(mImeSource).onWindowFocusGained(true);
             // since there is no focused view, forcefully make IME visible.
             mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
             verify(loggingListener).onReady(notNull(), anyInt());
@@ -252,7 +262,7 @@
         prepareControls();
 
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained(true);
+            mController.getSourceConsumer(mImeSource).onWindowFocusGained(true);
             // since there is no focused view, forcefully make IME visible.
             mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
             mController.show(all());
@@ -265,7 +275,7 @@
             mController.hide(all());
             mController.cancelExistingAnimations();
             assertEquals(0, mController.getRequestedVisibleTypes() & types);
-            mController.getSourceConsumer(ITYPE_IME).onWindowFocusLost();
+            mController.getSourceConsumer(mImeSource).onWindowFocusLost();
         });
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
@@ -275,14 +285,14 @@
         InsetsSourceControl ime = createControl(ITYPE_IME, ime());
         mController.onControlsChanged(new InsetsSourceControl[] { ime });
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained(true);
+            mController.getSourceConsumer(mImeSource).onWindowFocusGained(true);
             mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */);
             mController.cancelExistingAnimations();
             assertTrue(isRequestedVisible(mController, ime()));
             mController.hide(ime(), true /* fromIme */, null /* statsToken */);
             mController.cancelExistingAnimations();
             assertFalse(isRequestedVisible(mController, ime()));
-            mController.getSourceConsumer(ITYPE_IME).onWindowFocusLost();
+            mController.getSourceConsumer(mImeSource).onWindowFocusLost();
         });
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
@@ -901,7 +911,8 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             // Simulate IME insets is not controllable
             mController.onControlsChanged(new InsetsSourceControl[0]);
-            final InsetsSourceConsumer imeInsetsConsumer = mController.getSourceConsumer(ITYPE_IME);
+            final InsetsSourceConsumer imeInsetsConsumer =
+                    mController.getSourceConsumer(mImeSource);
             assertNull(imeInsetsConsumer.getControl());
 
             // Verify IME requested visibility should be updated to IME consumer from controller.
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index cec3fc5..3a3eeee 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -100,7 +100,7 @@
             mState.addSource(mSpyInsetsSource);
 
             mController = new InsetsController(new ViewRootInsetsControllerHost(mViewRoot));
-            mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mState,
+            mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, statusBars(), mState,
                     () -> mMockTransaction, mController) {
                 @Override
                 public void removeSurface() {
@@ -146,7 +146,7 @@
         InsetsController controller = new InsetsController(new ViewRootInsetsControllerHost(
                 mViewRoot));
         InsetsSourceConsumer consumer = new InsetsSourceConsumer(
-                ITYPE_IME, state, null, controller);
+                ITYPE_IME, ime(), state, null, controller);
 
         InsetsSource source = new InsetsSource(ITYPE_IME, ime());
         source.setFrame(0, 1, 2, 3);
@@ -216,9 +216,9 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             InsetsState state = new InsetsState();
             ViewRootInsetsControllerHost host = new ViewRootInsetsControllerHost(mViewRoot);
-            InsetsController insetsController = new InsetsController(host, (controller, type) -> {
-                if (type == ITYPE_IME) {
-                    return new InsetsSourceConsumer(ITYPE_IME, state,
+            InsetsController insetsController = new InsetsController(host, (controller, source) -> {
+                if (source.getType() == ime()) {
+                    return new InsetsSourceConsumer(ITYPE_IME, ime(), state,
                             () -> mMockTransaction, controller) {
                         @Override
                         public int requestShow(boolean fromController) {
@@ -226,10 +226,11 @@
                         }
                     };
                 }
-                return new InsetsSourceConsumer(type, controller.getState(), Transaction::new,
-                        controller);
+                return new InsetsSourceConsumer(source.getId(), source.getType(),
+                        controller.getState(), Transaction::new, controller);
             }, host.getHandler());
-            InsetsSourceConsumer imeConsumer = insetsController.getSourceConsumer(ITYPE_IME);
+            InsetsSource imeSource = new InsetsSource(ITYPE_IME, ime());
+            InsetsSourceConsumer imeConsumer = insetsController.getSourceConsumer(imeSource);
 
             // Initial IME insets source control with its leash.
             imeConsumer.setControl(new InsetsSourceControl(ITYPE_IME, ime(), mLeash,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index a1d0337..38c3aa0 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.os;
 
+import com.android.internal.power.EnergyConsumerStatsTest;
+
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
 
@@ -34,7 +36,7 @@
         LongMultiStateCounterTest.class,
         PowerProfileTest.class,
 
-        com.android.internal.power.MeasuredEnergyStatsTest.class
+        EnergyConsumerStatsTest.class
     })
 public class BatteryStatsTests {
 }
\ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/EnergyConsumerStatsTest.java
similarity index 81%
rename from core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
rename to core/tests/coretests/src/com/android/internal/power/EnergyConsumerStatsTest.java
index 88349b3..e09cfd2 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/EnergyConsumerStatsTest.java
@@ -18,13 +18,13 @@
 
 import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
 
-import static com.android.internal.power.MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS;
-import static com.android.internal.power.MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH;
-import static com.android.internal.power.MeasuredEnergyStats.POWER_BUCKET_CPU;
-import static com.android.internal.power.MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE;
-import static com.android.internal.power.MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON;
-import static com.android.internal.power.MeasuredEnergyStats.POWER_BUCKET_SCREEN_OTHER;
-import static com.android.internal.power.MeasuredEnergyStats.POWER_BUCKET_WIFI;
+import static com.android.internal.power.EnergyConsumerStats.NUMBER_STANDARD_POWER_BUCKETS;
+import static com.android.internal.power.EnergyConsumerStats.POWER_BUCKET_BLUETOOTH;
+import static com.android.internal.power.EnergyConsumerStats.POWER_BUCKET_CPU;
+import static com.android.internal.power.EnergyConsumerStats.POWER_BUCKET_SCREEN_DOZE;
+import static com.android.internal.power.EnergyConsumerStats.POWER_BUCKET_SCREEN_ON;
+import static com.android.internal.power.EnergyConsumerStats.POWER_BUCKET_SCREEN_OTHER;
+import static com.android.internal.power.EnergyConsumerStats.POWER_BUCKET_WIFI;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -43,13 +43,10 @@
 import java.util.Arrays;
 
 /**
- * Test class for {@link MeasuredEnergyStats}.
- *
- * To run the tests, use
- * atest FrameworksCoreTests:com.android.internal.power.MeasuredEnergyStatsTest
+ * Test class for {@link EnergyConsumerStats}.
  */
 @SmallTest
-public class MeasuredEnergyStatsTest {
+public class EnergyConsumerStatsTest {
 
     @Test
     public void testConstruction() {
@@ -59,11 +56,11 @@
         supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
         supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
 
-        final MeasuredEnergyStats.Config config =
-                new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+        final EnergyConsumerStats.Config config =
+                new EnergyConsumerStats.Config(supportedStandardBuckets, customBucketNames,
                         new int[]{POWER_BUCKET_SCREEN_ON, POWER_BUCKET_WIFI},
                         new String[]{"state0", "state1", "state3"});
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+        final EnergyConsumerStats stats = new EnergyConsumerStats(config);
 
         for (int bucket = 0; bucket < NUMBER_STANDARD_POWER_BUCKETS; bucket++) {
             if (supportedStandardBuckets[bucket]) {
@@ -95,10 +92,10 @@
         supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
         supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
 
-        final MeasuredEnergyStats.Config config =
-                new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+        final EnergyConsumerStats.Config config =
+                new EnergyConsumerStats.Config(supportedStandardBuckets, customBucketNames,
                         new int[]{POWER_BUCKET_SCREEN_ON}, new String[]{"s0", "s1"});
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+        final EnergyConsumerStats stats = new EnergyConsumerStats(config);
 
         stats.setState(0, 1000);
         stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10, 2000);
@@ -112,7 +109,7 @@
         stats.writeToParcel(parcel);
 
         parcel.setDataPosition(0);
-        MeasuredEnergyStats newStats = new MeasuredEnergyStats(config, parcel);
+        EnergyConsumerStats newStats = new EnergyConsumerStats(config, parcel);
 
         for (int bucket = 0; bucket < NUMBER_STANDARD_POWER_BUCKETS; bucket++) {
             assertEquals(stats.getAccumulatedStandardBucketCharge(bucket),
@@ -139,17 +136,17 @@
         supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
         supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
 
-        final MeasuredEnergyStats.Config config =
-                new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+        final EnergyConsumerStats.Config config =
+                new EnergyConsumerStats.Config(supportedStandardBuckets, customBucketNames,
                         new int[]{POWER_BUCKET_SCREEN_ON, POWER_BUCKET_WIFI},
                         new String[] {"state0", "state1", "state2"});
 
         final Parcel parcel = Parcel.obtain();
-        MeasuredEnergyStats.Config.writeToParcel(config, parcel);
+        EnergyConsumerStats.Config.writeToParcel(config, parcel);
 
         parcel.setDataPosition(0);
 
-        final MeasuredEnergyStats.Config newConfig = MeasuredEnergyStats.Config.createFromParcel(
+        final EnergyConsumerStats.Config newConfig = EnergyConsumerStats.Config.createFromParcel(
                 parcel);
 
         assertThat(newConfig).isNotNull();
@@ -172,11 +169,11 @@
     @Test
     public void testCreateAndReadConfigFromParcel_nullConfig() {
         final Parcel parcel = Parcel.obtain();
-        MeasuredEnergyStats.Config.writeToParcel(null, parcel);
+        EnergyConsumerStats.Config.writeToParcel(null, parcel);
 
         parcel.setDataPosition(0);
 
-        final MeasuredEnergyStats.Config newConfig = MeasuredEnergyStats.Config.createFromParcel(
+        final EnergyConsumerStats.Config newConfig = EnergyConsumerStats.Config.createFromParcel(
                 parcel);
 
         assertThat(newConfig).isNull();
@@ -190,10 +187,10 @@
         supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
         supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
 
-        final MeasuredEnergyStats.Config config =
-                new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+        final EnergyConsumerStats.Config config =
+                new EnergyConsumerStats.Config(supportedStandardBuckets, customBucketNames,
                         new int[0], new String[]{"s"});
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+        final EnergyConsumerStats stats = new EnergyConsumerStats(config);
 
         stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10);
         stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5);
@@ -202,12 +199,12 @@
         stats.updateCustomBucket(1, 60);
 
         final Parcel parcel = Parcel.obtain();
-        MeasuredEnergyStats.writeSummaryToParcel(stats, parcel);
+        EnergyConsumerStats.writeSummaryToParcel(stats, parcel);
 
         parcel.setDataPosition(0);
 
-        MeasuredEnergyStats newStats =
-                MeasuredEnergyStats.createAndReadSummaryFromParcel(config, parcel);
+        EnergyConsumerStats newStats =
+                EnergyConsumerStats.createAndReadSummaryFromParcel(config, parcel);
 
         for (int i = 0; i < NUMBER_STANDARD_POWER_BUCKETS; i++) {
             assertEquals(stats.isStandardBucketSupported(i),
@@ -232,17 +229,17 @@
         supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
         supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
 
-        final MeasuredEnergyStats.Config config =
-                new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+        final EnergyConsumerStats.Config config =
+                new EnergyConsumerStats.Config(supportedStandardBuckets, customBucketNames,
                         new int[0], new String[]{"s"});
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+        final EnergyConsumerStats stats = new EnergyConsumerStats(config);
         stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10);
         stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5);
         stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40);
         stats.updateCustomBucket(0, 50);
 
         final Parcel parcel = Parcel.obtain();
-        MeasuredEnergyStats.writeSummaryToParcel(stats, parcel);
+        EnergyConsumerStats.writeSummaryToParcel(stats, parcel);
         parcel.setDataPosition(0);
 
         final boolean[] newSupportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
@@ -250,12 +247,12 @@
         newSupportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = true; // switched false > true
         newSupportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = false; // switched true > false
 
-        final MeasuredEnergyStats.Config newConfig =
-                new MeasuredEnergyStats.Config(newSupportedStandardBuckets, customBucketNames,
+        final EnergyConsumerStats.Config newConfig =
+                new EnergyConsumerStats.Config(newSupportedStandardBuckets, customBucketNames,
                         new int[0], new String[]{"s"});
 
-        final MeasuredEnergyStats newStats =
-                MeasuredEnergyStats.createAndReadSummaryFromParcel(newConfig, parcel);
+        final EnergyConsumerStats newStats =
+                EnergyConsumerStats.createAndReadSummaryFromParcel(newConfig, parcel);
 
         for (int i = 0; i < NUMBER_STANDARD_POWER_BUCKETS; i++) {
             if (!newSupportedStandardBuckets[i]) {
@@ -288,10 +285,10 @@
         supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
         supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
 
-        final MeasuredEnergyStats.Config config =
-                new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+        final EnergyConsumerStats.Config config =
+                new EnergyConsumerStats.Config(supportedStandardBuckets, customBucketNames,
                         new int[0], new String[]{"s"});
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+        final EnergyConsumerStats stats = new EnergyConsumerStats(config);
 
         stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10);
         stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5);
@@ -300,11 +297,11 @@
         stats.updateCustomBucket(1, 60);
 
         final Parcel parcel = Parcel.obtain();
-        MeasuredEnergyStats.writeSummaryToParcel(stats, parcel);
+        EnergyConsumerStats.writeSummaryToParcel(stats, parcel);
         parcel.setDataPosition(0);
 
-        MeasuredEnergyStats newStats =
-                MeasuredEnergyStats.createAndReadSummaryFromParcel(null, parcel);
+        EnergyConsumerStats newStats =
+                EnergyConsumerStats.createAndReadSummaryFromParcel(null, parcel);
         assertNull(newStats);
         parcel.recycle();
     }
@@ -317,29 +314,29 @@
         supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
         supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
 
-        final MeasuredEnergyStats.Config config =
-                new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+        final EnergyConsumerStats.Config config =
+                new EnergyConsumerStats.Config(supportedStandardBuckets, customBucketNames,
                         new int[0], new String[]{"s"});
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+        final EnergyConsumerStats stats = new EnergyConsumerStats(config);
 
         stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 0);
         stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40);
 
         final Parcel parcel = Parcel.obtain();
-        MeasuredEnergyStats.writeSummaryToParcel(stats, parcel);
+        EnergyConsumerStats.writeSummaryToParcel(stats, parcel);
 
         final boolean[] newSupportedStandardBuckets = new boolean[NUMBER_STANDARD_POWER_BUCKETS];
         newSupportedStandardBuckets[POWER_BUCKET_SCREEN_ON] = true;
         newSupportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = true; // switched false > true
         newSupportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = false; // switched true > false
-        final MeasuredEnergyStats.Config newConfig =
-                new MeasuredEnergyStats.Config(newSupportedStandardBuckets, customBucketNames,
+        final EnergyConsumerStats.Config newConfig =
+                new EnergyConsumerStats.Config(newSupportedStandardBuckets, customBucketNames,
                         new int[0], new String[]{"s"});
 
         parcel.setDataPosition(0);
 
-        final MeasuredEnergyStats newStats =
-                MeasuredEnergyStats.createAndReadSummaryFromParcel(newConfig, parcel);
+        final EnergyConsumerStats newStats =
+                EnergyConsumerStats.createAndReadSummaryFromParcel(newConfig, parcel);
 
         // The only non-0 entry in stats is no longer supported, so now there's no interesting data.
         assertNull(newStats);
@@ -355,11 +352,11 @@
         supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
         supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
 
-        final MeasuredEnergyStats.Config config =
-                new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+        final EnergyConsumerStats.Config config =
+                new EnergyConsumerStats.Config(supportedStandardBuckets, customBucketNames,
                         new int[]{POWER_BUCKET_SCREEN_ON, POWER_BUCKET_SCREEN_OTHER},
                         new String[]{"s0", "s1"});
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+        final EnergyConsumerStats stats = new EnergyConsumerStats(config);
 
         stats.setState(0, 1000);
         stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10, 2000);
@@ -391,11 +388,11 @@
 
     @Test
     public void testIsValidCustomBucket() {
-        final MeasuredEnergyStats.Config config =
-                new MeasuredEnergyStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
+        final EnergyConsumerStats.Config config =
+                new EnergyConsumerStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
                         new String[]{"A", "B", "C"},
                         new int[0], new String[]{"s"});
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+        final EnergyConsumerStats stats = new EnergyConsumerStats(config);
         assertFalse(stats.isValidCustomBucket(-1));
         assertTrue(stats.isValidCustomBucket(0));
         assertTrue(stats.isValidCustomBucket(1));
@@ -403,10 +400,10 @@
         assertFalse(stats.isValidCustomBucket(3));
         assertFalse(stats.isValidCustomBucket(4));
 
-        final MeasuredEnergyStats.Config boringConfig =
-                new MeasuredEnergyStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
+        final EnergyConsumerStats.Config boringConfig =
+                new EnergyConsumerStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
                         new String[0], new int[0], new String[]{"s"});
-        final MeasuredEnergyStats boringStats = new MeasuredEnergyStats(boringConfig);
+        final EnergyConsumerStats boringStats = new EnergyConsumerStats(boringConfig);
         assertFalse(boringStats.isValidCustomBucket(-1));
         assertFalse(boringStats.isValidCustomBucket(0));
         assertFalse(boringStats.isValidCustomBucket(1));
@@ -414,11 +411,11 @@
 
     @Test
     public void testGetAccumulatedCustomBucketCharges() {
-        final MeasuredEnergyStats.Config config =
-                new MeasuredEnergyStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
+        final EnergyConsumerStats.Config config =
+                new EnergyConsumerStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
                         new String[]{"A", "B", "C"},
                         new int[0], new String[]{"s"});
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+        final EnergyConsumerStats stats = new EnergyConsumerStats(config);
         stats.updateCustomBucket(0, 50);
         stats.updateCustomBucket(1, 60);
         stats.updateCustomBucket(2, 13);
@@ -434,10 +431,10 @@
 
     @Test
     public void testGetAccumulatedCustomBucketCharges_empty() {
-        final MeasuredEnergyStats.Config config =
-                new MeasuredEnergyStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
+        final EnergyConsumerStats.Config config =
+                new EnergyConsumerStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
                         new String[0], new int[0], new String[]{"s"});
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+        final EnergyConsumerStats stats = new EnergyConsumerStats(config);
 
         final long[] output = stats.getAccumulatedCustomBucketCharges();
         assertEquals(0, output.length);
@@ -446,13 +443,13 @@
     @Test
     public void testGetNumberCustomChargeBuckets() {
         assertEquals(0,
-                new MeasuredEnergyStats(
-                        new MeasuredEnergyStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
+                new EnergyConsumerStats(
+                        new EnergyConsumerStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
                                 new String[0], new int[0], new String[]{"s"}))
                         .getNumberCustomPowerBuckets());
         assertEquals(3,
-                new MeasuredEnergyStats(
-                        new MeasuredEnergyStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
+                new EnergyConsumerStats(
+                        new EnergyConsumerStats.Config(new boolean[NUMBER_STANDARD_POWER_BUCKETS],
                                 new String[]{"A", "B", "C"}, new int[0], new String[]{"s"}))
                         .getNumberCustomPowerBuckets());
     }
@@ -466,10 +463,10 @@
         supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
 
         final int[] supportedMultiStateBuckets = new int[]{POWER_BUCKET_SCREEN_ON};
-        final MeasuredEnergyStats.Config config =
-                new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
+        final EnergyConsumerStats.Config config =
+                new EnergyConsumerStats.Config(supportedStandardBuckets, customBucketNames,
                         supportedMultiStateBuckets, new String[]{"s1", "s2"});
-        final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
+        final EnergyConsumerStats stats = new EnergyConsumerStats(config);
         stats.setState(1, 0);
         stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10, 1000);
         stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5, 2000);
@@ -482,7 +479,7 @@
         assertThat(stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 1))
                 .isEqualTo(15);
 
-        MeasuredEnergyStats.resetIfNotNull(stats);
+        EnergyConsumerStats.resetIfNotNull(stats);
         // All charges should be reset to 0
         for (int i = 0; i < NUMBER_STANDARD_POWER_BUCKETS; i++) {
             if (supportedStandardBuckets[i]) {
@@ -517,13 +514,13 @@
         int exp;
 
         exp = POWER_BUCKET_SCREEN_ON;
-        assertEquals(exp, MeasuredEnergyStats.getDisplayPowerBucket(Display.STATE_ON));
-        assertEquals(exp, MeasuredEnergyStats.getDisplayPowerBucket(Display.STATE_VR));
-        assertEquals(exp, MeasuredEnergyStats.getDisplayPowerBucket(Display.STATE_ON_SUSPEND));
+        assertEquals(exp, EnergyConsumerStats.getDisplayPowerBucket(Display.STATE_ON));
+        assertEquals(exp, EnergyConsumerStats.getDisplayPowerBucket(Display.STATE_VR));
+        assertEquals(exp, EnergyConsumerStats.getDisplayPowerBucket(Display.STATE_ON_SUSPEND));
 
         exp = POWER_BUCKET_SCREEN_DOZE;
-        assertEquals(exp, MeasuredEnergyStats.getDisplayPowerBucket(Display.STATE_DOZE));
-        assertEquals(exp, MeasuredEnergyStats.getDisplayPowerBucket(Display.STATE_DOZE_SUSPEND));
+        assertEquals(exp, EnergyConsumerStats.getDisplayPowerBucket(Display.STATE_DOZE));
+        assertEquals(exp, EnergyConsumerStats.getDisplayPowerBucket(Display.STATE_DOZE_SUSPEND));
     }
 
     @Test
@@ -534,7 +531,7 @@
         final int[] supportedMultiStateBuckets = {POWER_BUCKET_CPU, POWER_BUCKET_WIFI};
         final String[] stateNames = {"s"};
 
-        final MeasuredEnergyStats.Config config = new MeasuredEnergyStats.Config(
+        final EnergyConsumerStats.Config config = new EnergyConsumerStats.Config(
                 supportedStandardBuckets,
                 customBucketNames,
                 supportedMultiStateBuckets,
@@ -542,7 +539,7 @@
         assertTrue(
                 "All standard and custom bucket supports match",
                 config.isCompatible(
-                        new MeasuredEnergyStats.Config(
+                        new EnergyConsumerStats.Config(
                                 supportedStandardBuckets,
                                 customBucketNames,
                                 supportedMultiStateBuckets,
@@ -554,7 +551,7 @@
         assertFalse(
                 "Standard bucket support mismatch",
                 config.isCompatible(
-                        new MeasuredEnergyStats.Config(
+                        new EnergyConsumerStats.Config(
                                 differentSupportedStandardBuckets,
                                 customBucketNames,
                                 supportedMultiStateBuckets,
@@ -562,7 +559,7 @@
         assertFalse(
                 "Custom bucket support mismatch",
                 config.isCompatible(
-                        new MeasuredEnergyStats.Config(
+                        new EnergyConsumerStats.Config(
                                 supportedStandardBuckets,
                                 new String[]{"C", "B"},
                                 supportedMultiStateBuckets,
@@ -570,7 +567,7 @@
         assertFalse(
                 "Multi-state bucket mismatch",
                 config.isCompatible(
-                        new MeasuredEnergyStats.Config(
+                        new EnergyConsumerStats.Config(
                                 supportedStandardBuckets,
                                 new String[]{"A"},
                                 new int[] {POWER_BUCKET_CPU, POWER_BUCKET_BLUETOOTH},
@@ -578,7 +575,7 @@
         assertFalse(
                 "Multi-state bucket state list mismatch",
                 config.isCompatible(
-                        new MeasuredEnergyStats.Config(
+                        new EnergyConsumerStats.Config(
                                 supportedStandardBuckets,
                                 new String[]{"A"},
                                 supportedMultiStateBuckets,
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index ec8b2d6..8fe28ae 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -22,8 +22,8 @@
 import android.graphics.Typeface;
 import android.text.FontConfig;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Log;
+import android.util.SparseIntArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -117,17 +117,18 @@
             }
         }
 
+
         final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily(
                 defaultFonts, languageTags, variant, false, cache);
-
         // Insert family into fallback map.
         for (int i = 0; i < fallbackMap.size(); i++) {
             final String name = fallbackMap.keyAt(i);
             final NativeFamilyListSet familyListSet = fallbackMap.valueAt(i);
-            if (familyListSet.seenXmlFamilies.contains(xmlFamily)) {
+            int identityHash = System.identityHashCode(xmlFamily);
+            if (familyListSet.seenXmlFamilies.get(identityHash, -1) != -1) {
                 continue;
             } else {
-                familyListSet.seenXmlFamilies.add(xmlFamily);
+                familyListSet.seenXmlFamilies.append(identityHash, 1);
             }
             final ArrayList<FontConfig.Font> fallback = specificFallbackFonts.get(name);
             if (fallback == null) {
@@ -213,7 +214,7 @@
                 return;
             }
             familyListSet.familyList.add(family);
-            familyListSet.seenXmlFamilies.add(xmlFamily);
+            familyListSet.seenXmlFamilies.append(System.identityHashCode(xmlFamily), 1);
         }
         fallbackListMap.put(familyName, familyListSet);
     }
@@ -276,7 +277,7 @@
 
     private static final class NativeFamilyListSet {
         public List<FontFamily> familyList = new ArrayList<>();
-        public Set<FontConfig.FontFamily> seenXmlFamilies = new ArraySet<>();
+        public SparseIntArray seenXmlFamilies = new SparseIntArray();
     }
 
     /** @hide */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index af13bf5..94aeb2b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -346,7 +346,7 @@
         public void resized(ClientWindowFrames frames, boolean reportDraw,
                 MergedConfiguration newMergedConfiguration, InsetsState insetsState,
                 boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int syncSeqId,
-                int resizeMode) {}
+                boolean dragResizing) {}
 
         @Override
         public void insetsControlChanged(InsetsState insetsState,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index b075b14..3341470 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -17,12 +17,20 @@
 package com.android.wm.shell.desktopmode
 
 import android.app.ActivityManager
-import android.app.WindowConfiguration
 import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
 import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
 import android.app.WindowConfiguration.WindowingMode
 import android.content.Context
-import android.view.WindowManager
+import android.os.IBinder
+import android.view.SurfaceControl
+import android.view.WindowManager.TRANSIT_CHANGE
+import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_TO_FRONT
+import android.window.TransitionInfo
+import android.window.TransitionRequestInfo
 import android.window.WindowContainerTransaction
 import androidx.annotation.BinderThread
 import com.android.internal.protolog.common.ProtoLog
@@ -51,7 +59,7 @@
     private val transitions: Transitions,
     private val desktopModeTaskRepository: DesktopModeTaskRepository,
     @ShellMainThread private val mainExecutor: ShellExecutor
-) : RemoteCallable<DesktopTasksController> {
+) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler {
 
     private val desktopMode: DesktopModeImpl
 
@@ -69,6 +77,7 @@
             { createExternalInterface() },
             this
         )
+        transitions.addHandler(this)
     }
 
     /** Show all tasks, that are part of the desktop, on top of launcher */
@@ -81,7 +90,7 @@
         // Execute transaction if there are pending operations
         if (!wct.isEmpty) {
             if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-                transitions.startTransition(WindowManager.TRANSIT_TO_FRONT, wct, null /* handler */)
+                transitions.startTransition(TRANSIT_TO_FRONT, wct, null /* handler */)
             } else {
                 shellTaskOrganizer.applyTransaction(wct)
             }
@@ -101,11 +110,11 @@
         // Bring other apps to front first
         bringDesktopAppsToFront(wct)
 
-        wct.setWindowingMode(task.getToken(), WindowConfiguration.WINDOWING_MODE_FREEFORM)
+        wct.setWindowingMode(task.getToken(), WINDOWING_MODE_FREEFORM)
         wct.reorder(task.getToken(), true /* onTop */)
 
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            transitions.startTransition(WindowManager.TRANSIT_CHANGE, wct, null /* handler */)
+            transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
         } else {
             shellTaskOrganizer.applyTransaction(wct)
         }
@@ -121,10 +130,10 @@
         ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToFullscreen: %d", task.taskId)
 
         val wct = WindowContainerTransaction()
-        wct.setWindowingMode(task.getToken(), WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
+        wct.setWindowingMode(task.getToken(), WINDOWING_MODE_FULLSCREEN)
         wct.setBounds(task.getToken(), null)
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            transitions.startTransition(WindowManager.TRANSIT_CHANGE, wct, null /* handler */)
+            transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
         } else {
             shellTaskOrganizer.applyTransaction(wct)
         }
@@ -181,6 +190,80 @@
         return mainExecutor
     }
 
+    override fun startAnimation(
+        transition: IBinder,
+        info: TransitionInfo,
+        startTransaction: SurfaceControl.Transaction,
+        finishTransaction: SurfaceControl.Transaction,
+        finishCallback: Transitions.TransitionFinishCallback
+    ): Boolean {
+        // This handler should never be the sole handler, so should not animate anything.
+        return false
+    }
+
+    override fun handleRequest(
+        transition: IBinder,
+        request: TransitionRequestInfo
+    ): WindowContainerTransaction? {
+        // Check if we should skip handling this transition
+        val task: ActivityManager.RunningTaskInfo? = request.triggerTask
+        val shouldHandleRequest =
+            when {
+                // Only handle open or to front transitions
+                request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> false
+                // Only handle when it is a task transition
+                task == null -> false
+                // Only handle standard type tasks
+                task.activityType != ACTIVITY_TYPE_STANDARD -> false
+                // Only handle fullscreen or freeform tasks
+                task.windowingMode != WINDOWING_MODE_FULLSCREEN &&
+                    task.windowingMode != WINDOWING_MODE_FREEFORM -> false
+                // Otherwise process it
+                else -> true
+            }
+
+        if (!shouldHandleRequest) {
+            return null
+        }
+
+        val activeTasks = desktopModeTaskRepository.getActiveTasks()
+
+        // Check if we should switch a fullscreen task to freeform
+        if (task?.windowingMode == WINDOWING_MODE_FULLSCREEN) {
+            // If there are any visible desktop tasks, switch the task to freeform
+            if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) {
+                ProtoLog.d(
+                    WM_SHELL_DESKTOP_MODE,
+                    "DesktopTasksController#handleRequest: switch fullscreen task to freeform," +
+                        " taskId=%d",
+                    task.taskId
+                )
+                return WindowContainerTransaction().apply {
+                    setWindowingMode(task.token, WINDOWING_MODE_FREEFORM)
+                }
+            }
+        }
+
+        // CHeck if we should switch a freeform task to fullscreen
+        if (task?.windowingMode == WINDOWING_MODE_FREEFORM) {
+            // If no visible desktop tasks, switch this task to freeform as the transition came
+            // outside of this controller
+            if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) {
+                ProtoLog.d(
+                    WM_SHELL_DESKTOP_MODE,
+                    "DesktopTasksController#handleRequest: switch freeform task to fullscreen," +
+                        " taskId=%d",
+                    task.taskId
+                )
+                return WindowContainerTransaction().apply {
+                    setWindowingMode(task.token, WINDOWING_MODE_FULLSCREEN)
+                    setBounds(task.token, null)
+                }
+            }
+        }
+        return null
+    }
+
     /** Creates a new instance of the external interface to pass to another process. */
     private fun createExternalInterface(): ExternalInterfaceBinder {
         return IDesktopModeImpl(this)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 9d6711f..65da757b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -239,7 +239,7 @@
         public void resized(ClientWindowFrames frames, boolean reportDraw,
                 MergedConfiguration mergedConfiguration, InsetsState insetsState,
                 boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int seqId,
-                int resizeMode) {
+                boolean dragResizing) {
             final TaskSnapshotWindow snapshot = mOuter.get();
             if (snapshot == null) {
                 return;
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
index 8465678..122c18d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
@@ -33,7 +33,6 @@
 import com.android.server.wm.flicker.taskBarLayerIsVisibleAtStartAndEnd
 import com.android.server.wm.flicker.taskBarWindowIsAlwaysVisible
 import com.android.server.wm.traces.common.ComponentNameMatcher
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.Assume
 import org.junit.Test
 
@@ -49,15 +48,6 @@
     protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(),
     protected val tapl: LauncherInstrumentation = LauncherInstrumentation()
 ) {
-    init {
-        flicker.scenario.setIsTablet(
-            WindowManagerStateHelper(instrumentation, clearCacheAfterParsing = false)
-                .currentState
-                .wmState
-                .isTablet
-        )
-    }
-
     /** Specification of the test transition to execute */
     abstract val transition: FlickerBuilder.() -> Unit
 
@@ -68,7 +58,7 @@
     @FlickerBuilderProvider
     fun buildFlicker(): FlickerBuilder {
         return FlickerBuilder(instrumentation).apply {
-            setup { flicker.scenario.setIsTablet(wmHelper.currentState.wmState.isTablet) }
+            setup { flicker.scenario.setIsTablet(tapl.isTablet) }
             transition()
         }
     }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 2b90243..a4c8d6f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -120,7 +120,7 @@
      */
     @Before
     fun setup() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(tapl.isTablet)
     }
 
     /**
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 157aa98..7d5dd89 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -103,7 +103,7 @@
      */
     @Before
     fun setup() {
-        Assume.assumeFalse(flicker.scenario.isTablet)
+        Assume.assumeFalse(tapl.isTablet)
     }
 
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index 5b656b3..0160f18 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -24,6 +24,7 @@
 import com.android.server.wm.flicker.FlickerBuilder
 import com.android.server.wm.flicker.FlickerTest
 import com.android.server.wm.flicker.FlickerTestFactory
+import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
 import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory
 import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
 import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
@@ -98,7 +99,17 @@
         }
     }
 
-    @Presubmit @Test fun primaryAppWindowKeepVisible() = flicker.appWindowKeepVisible(primaryApp)
+    @Presubmit
+    @Test fun primaryAppWindowKeepVisible() {
+        Assume.assumeFalse(isShellTransitionsEnabled)
+        flicker.appWindowKeepVisible(primaryApp)
+    }
+
+    @FlakyTest(bugId = 263213649)
+    @Test fun primaryAppWindowKeepVisible_ShellTransit() {
+        Assume.assumeTrue(isShellTransitionsEnabled)
+        flicker.appWindowKeepVisible(primaryApp)
+    }
 
     @Presubmit
     @Test
@@ -106,14 +117,27 @@
 
     @Presubmit
     @Test
-    fun primaryAppBoundsChanges() =
+    fun primaryAppBoundsChanges() {
+        Assume.assumeFalse(isShellTransitionsEnabled)
         flicker.splitAppLayerBoundsChanges(
             primaryApp,
             landscapePosLeft = true,
             portraitPosTop = false
         )
+    }
 
-    @FlakyTest(bugId = 250530664)
+    @FlakyTest(bugId = 263213649)
+    @Test
+    fun primaryAppBoundsChanges_ShellTransit() {
+        Assume.assumeTrue(isShellTransitionsEnabled)
+        flicker.splitAppLayerBoundsChanges(
+            primaryApp,
+            landscapePosLeft = true,
+            portraitPosTop = false
+        )
+    }
+
+    @Presubmit
     @Test
     fun secondaryAppBoundsChanges() =
         flicker.splitAppLayerBoundsChanges(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
index abf9426..af63f7c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
@@ -53,7 +53,7 @@
 
     @Before
     fun before() {
-        Assume.assumeTrue(flicker.scenario.isTablet)
+        Assume.assumeTrue(tapl.isTablet)
     }
 
     override val transition: FlickerBuilder.() -> Unit
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index de2473b..9a92879 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -17,10 +17,18 @@
 package com.android.wm.shell.desktopmode
 
 import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
 import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
 import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
 import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.os.Binder
 import android.testing.AndroidTestingRunner
+import android.view.WindowManager
+import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_TO_FRONT
+import android.window.TransitionRequestInfo
 import android.window.WindowContainerTransaction
 import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
 import androidx.test.filters.SmallTest
@@ -29,6 +37,7 @@
 import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
 import com.android.wm.shell.TestShellExecutor
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
@@ -37,9 +46,11 @@
 import com.android.wm.shell.sysui.ShellController
 import com.android.wm.shell.sysui.ShellInit
 import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.After
+import org.junit.Assume.assumeTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -79,6 +90,7 @@
         desktopModeTaskRepository = DesktopModeTaskRepository()
 
         whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
+        whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
 
         controller = createController()
 
@@ -221,6 +233,114 @@
         assertThat(controller.getTaskWindowingMode(999)).isEqualTo(WINDOWING_MODE_UNDEFINED)
     }
 
+    @Test
+    fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() {
+        assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+        val freeformTask = setUpFreeformTask()
+        markTaskVisible(freeformTask)
+        val fullscreenTask = createFullscreenTask()
+
+        val result = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+        assertThat(result?.changes?.get(fullscreenTask.token.asBinder())?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FREEFORM)
+    }
+
+    @Test
+    fun handleRequest_fullscreenTask_freeformNotVisible_returnNull() {
+        assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+        val freeformTask = setUpFreeformTask()
+        markTaskHidden(freeformTask)
+        val fullscreenTask = createFullscreenTask()
+        assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
+    }
+
+    @Test
+    fun handleRequest_fullscreenTask_noOtherTasks_returnNull() {
+        assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+        val fullscreenTask = createFullscreenTask()
+        assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
+    }
+
+    @Test
+    fun handleRequest_freeformTask_freeformVisible_returnNull() {
+        assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+        val freeformTask1 = setUpFreeformTask()
+        markTaskVisible(freeformTask1)
+
+        val freeformTask2 = createFreeformTask()
+        assertThat(controller.handleRequest(Binder(), createTransition(freeformTask2))).isNull()
+    }
+
+    @Test
+    fun handleRequest_freeformTask_freeformNotVisible_returnSwitchToFullscreenWCT() {
+        assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+        val freeformTask1 = setUpFreeformTask()
+        markTaskHidden(freeformTask1)
+
+        val freeformTask2 = createFreeformTask()
+        val result =
+            controller.handleRequest(
+                Binder(),
+                createTransition(freeformTask2, type = TRANSIT_TO_FRONT)
+            )
+        assertThat(result?.changes?.get(freeformTask2.token.asBinder())?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+    }
+
+    @Test
+    fun handleRequest_freeformTask_noOtherTasks_returnSwitchToFullscreenWCT() {
+        assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+        val task = createFreeformTask()
+        val result = controller.handleRequest(Binder(), createTransition(task))
+        assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+            .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+    }
+
+    @Test
+    fun handleRequest_notOpenOrToFrontTransition_returnNull() {
+        assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+        val task =
+            TestRunningTaskInfoBuilder()
+                .setActivityType(ACTIVITY_TYPE_STANDARD)
+                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+                .build()
+        val transition = createTransition(task = task, type = WindowManager.TRANSIT_CLOSE)
+        val result = controller.handleRequest(Binder(), transition)
+        assertThat(result).isNull()
+    }
+
+    @Test
+    fun handleRequest_noTriggerTask_returnNull() {
+        assumeTrue(ENABLE_SHELL_TRANSITIONS)
+        assertThat(controller.handleRequest(Binder(), createTransition(task = null))).isNull()
+    }
+
+    @Test
+    fun handleRequest_triggerTaskNotStandard_returnNull() {
+        assumeTrue(ENABLE_SHELL_TRANSITIONS)
+        val task = TestRunningTaskInfoBuilder().setActivityType(ACTIVITY_TYPE_HOME).build()
+        assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
+    }
+
+    @Test
+    fun handleRequest_triggerTaskNotFullscreenOrFreeform_returnNull() {
+        assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+        val task =
+            TestRunningTaskInfoBuilder()
+                .setActivityType(ACTIVITY_TYPE_STANDARD)
+                .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
+                .build()
+        assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
+    }
+
     private fun setUpFreeformTask(): RunningTaskInfo {
         val task = createFreeformTask()
         whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
@@ -254,7 +374,7 @@
 
     private fun getLatestWct(): WindowContainerTransaction {
         val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
-        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+        if (ENABLE_SHELL_TRANSITIONS) {
             verify(transitions).startTransition(anyInt(), arg.capture(), isNull())
         } else {
             verify(shellTaskOrganizer).applyTransaction(arg.capture())
@@ -263,12 +383,19 @@
     }
 
     private fun verifyWCTNotExecuted() {
-        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+        if (ENABLE_SHELL_TRANSITIONS) {
             verify(transitions, never()).startTransition(anyInt(), any(), isNull())
         } else {
             verify(shellTaskOrganizer, never()).applyTransaction(any())
         }
     }
+
+    private fun createTransition(
+        task: RunningTaskInfo?,
+        @WindowManager.TransitionType type: Int = TRANSIT_OPEN
+    ): TransitionRequestInfo {
+        return TransitionRequestInfo(type, task, null /* remoteTransition */)
+    }
 }
 
 private fun WindowContainerTransaction.assertReorderAt(index: Int, task: RunningTaskInfo) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 44ecee7..fdd6233 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -6281,6 +6281,49 @@
     }
 
     /**
+     * @hide
+     * Lower media volume to RS1
+     */
+    public void lowerVolumeToRs1() {
+        try {
+            getService().lowerVolumeToRs1(mApplicationContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     * Sound dose warning at every 100% of dose during integration window
+     */
+    public static final int CSD_WARNING_DOSE_REACHED_1X = 1;
+    /**
+     * @hide
+     * Sound dose warning when 500% of dose is reached during integration window
+     */
+    public static final int CSD_WARNING_DOSE_REPEATED_5X = 2;
+    /**
+     * @hide
+     * Sound dose warning after a momentary exposure event
+     */
+    public static final int CSD_WARNING_MOMENTARY_EXPOSURE = 3;
+    /**
+     * @hide
+     * Sound dose warning at every 100% of dose during integration window
+     */
+    public static final int CSD_WARNING_ACCUMULATION_START = 4;
+
+    /** @hide */
+    @IntDef(flag = false, value = {
+            CSD_WARNING_DOSE_REACHED_1X,
+            CSD_WARNING_DOSE_REPEATED_5X,
+            CSD_WARNING_MOMENTARY_EXPOSURE,
+            CSD_WARNING_ACCUMULATION_START }
+    )
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CsdWarning {}
+
+    /**
      * Only useful for volume controllers.
      * @hide
      */
@@ -8774,6 +8817,55 @@
         }
     }
 
+    /**
+     * Requests if the implementation supports controlling the latency modes
+     * over the Bluetooth A2DP or LE Audio links.
+     *
+     * @return true if supported, false otherwise
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public boolean supportsBluetoothVariableLatency() {
+        try {
+            return getService().supportsBluetoothVariableLatency();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Enables or disables the variable Bluetooth latency control mechanism in the
+     * audio framework and the audio HAL. This does not apply to the latency mode control
+     * on the spatializer output as this is a built-in feature.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public void setBluetoothVariableLatencyEnabled(boolean enabled) {
+        try {
+            getService().setBluetoothVariableLatencyEnabled(enabled);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Indicates if the variable Bluetooth latency control mechanism is enabled or disabled.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public boolean isBluetoothVariableLatencyEnabled() {
+        try {
+            return getService().isBluetoothVariableLatencyEnabled();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     //====================================================================
     // Mute await connection
 
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 3e0d657..a743586 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -2476,4 +2476,30 @@
      */
     public static native int clearPreferredMixerAttributes(
             @NonNull AudioAttributes attributes, int portId, int uid);
+
+
+    /**
+     * Requests if the implementation supports controlling the latency modes
+     * over the Bluetooth A2DP or LE Audio links.
+     *
+     * @return true if supported, false otherwise
+     *
+     * @hide
+     */
+    public static native boolean supportsBluetoothVariableLatency();
+
+    /**
+     * Enables or disables the variable Bluetooth latency control mechanism in the
+     * audio framework and the audio HAL. This does not apply to the latency mode control
+     * on the spatializer output as this is a built-in feature.
+     *
+     * @hide
+     */
+    public static native int setBluetoothVariableLatencyEnabled(boolean enabled);
+
+    /**
+     * Indicates if the variable Bluetooth latency control mechanism is enabled or disabled.
+     * @hide
+     */
+    public static native boolean isBluetoothVariableLatencyEnabled();
 }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 5502db2..0f63cc4 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -261,6 +261,8 @@
 
     void disableSafeMediaVolume(String callingPackage);
 
+    void lowerVolumeToRs1(String callingPackage);
+
     int setHdmiSystemAudioSupported(boolean on);
 
     boolean isHdmiSystemAudioSupported();
@@ -585,4 +587,16 @@
             IPreferredMixerAttributesDispatcher dispatcher);
     oneway void unregisterPreferredMixerAttributesDispatcher(
             IPreferredMixerAttributesDispatcher dispatcher);
+
+    @EnforcePermission("MODIFY_AUDIO_ROUTING")
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)")
+    boolean supportsBluetoothVariableLatency();
+
+    @EnforcePermission("MODIFY_AUDIO_ROUTING")
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)")
+    void setBluetoothVariableLatencyEnabled(boolean enabled);
+
+    @EnforcePermission("MODIFY_AUDIO_ROUTING")
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)")
+    boolean isBluetoothVariableLatencyEnabled();
 }
diff --git a/media/java/android/media/IVolumeController.aidl b/media/java/android/media/IVolumeController.aidl
index 7f37265..9cb2eba 100644
--- a/media/java/android/media/IVolumeController.aidl
+++ b/media/java/android/media/IVolumeController.aidl
@@ -39,4 +39,19 @@
      *     {@link VolumePolicy#A11Y_MODE_INDEPENDENT_A11Y_VOLUME}
      */
     void setA11yMode(int mode);
+
+    /**
+     * Display a sound-dose related warning.
+     * This method will never be called if the CSD (Computed Sound Dose) feature is
+     * not enabled. See com.android.android.server.audio.SoundDoseHelper for the state of
+     * the feature.
+     * @param warning the type of warning to display, values are one of
+     *        {@link android.media.AudioManager#CSD_WARNING_DOSE_REACHED_1X},
+     *        {@link android.media.AudioManager#CSD_WARNING_DOSE_REPEATED_5X},
+     *        {@link android.media.AudioManager#CSD_WARNING_MOMENTARY_EXPOSURE},
+     *        {@link android.media.AudioManager#CSD_WARNING_ACCUMULATION_START}.
+     * @param displayDurationMs the time expressed in milliseconds after which the dialog will be
+     *        automatically dismissed, or -1 if there is no automatic timeout.
+     */
+    void displayCsdWarning(int warning, int displayDurationMs);
 }
diff --git a/media/java/android/media/RouteListingPreference.java b/media/java/android/media/RouteListingPreference.java
index b7043cb..6557277 100644
--- a/media/java/android/media/RouteListingPreference.java
+++ b/media/java/android/media/RouteListingPreference.java
@@ -54,20 +54,24 @@
             };
 
     @NonNull private final List<Item> mItems;
+    private final boolean mUseSystemOrdering;
 
     /**
      * Creates an instance with the given values.
      *
      * @param items See {@link #getItems()}.
+     * @param useSystemOrdering See {@link #getUseSystemOrdering()}
      */
-    public RouteListingPreference(@NonNull List<Item> items) {
+    public RouteListingPreference(@NonNull List<Item> items, boolean useSystemOrdering) {
         mItems = List.copyOf(Objects.requireNonNull(items));
+        mUseSystemOrdering = useSystemOrdering;
     }
 
     private RouteListingPreference(Parcel in) {
         List<Item> items =
                 in.readParcelableList(new ArrayList<>(), Item.class.getClassLoader(), Item.class);
         mItems = List.copyOf(items);
+        mUseSystemOrdering = in.readBoolean();
     }
 
     /**
@@ -79,6 +83,15 @@
         return mItems;
     }
 
+    /**
+     * Returns true if the application would like media route listing to use the system's ordering
+     * strategy, or false if the application would like route listing to respect the ordering
+     * obtained from {@link #getItems()}.
+     */
+    public boolean getUseSystemOrdering() {
+        return mUseSystemOrdering;
+    }
+
     // RouteListingPreference Parcelable implementation.
 
     @Override
@@ -89,6 +102,7 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeParcelableList(mItems, flags);
+        dest.writeBoolean(mUseSystemOrdering);
     }
 
     // Equals and hashCode.
@@ -102,12 +116,12 @@
             return false;
         }
         RouteListingPreference that = (RouteListingPreference) other;
-        return mItems.equals(that.mItems);
+        return mItems.equals(that.mItems) && mUseSystemOrdering == that.mUseSystemOrdering;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mItems);
+        return Objects.hash(mItems, mUseSystemOrdering);
     }
 
     /** Holds preference information for a specific route in a {@link RouteListingPreference}. */
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
index a4215e68..d60dfd9 100644
--- a/media/java/android/media/projection/MediaProjectionManager.java
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -74,8 +74,16 @@
      * Returns an {@link Intent} that <b>must</b> be passed to
      * {@link Activity#startActivityForResult(Intent, int)} (or similar) in order to start screen
      * capture. The activity will prompt the user whether to allow screen capture.  The result of
-     * this activity (received by overriding {@link Activity#onActivityResult(int, int, Intent)})
+     * this activity (received by overriding {@link Activity#onActivityResult(int, int, Intent)}
      * should be passed to {@link #getMediaProjection(int, Intent)}.
+     * <p>
+     * Identical to calling {@link #createScreenCaptureIntent(MediaProjectionConfig)} with
+     * a {@link MediaProjectionConfig#createConfigForUserChoice()}.
+     * </p>
+     * <p>
+     * Should be used instead of {@link #createScreenCaptureIntent(MediaProjectionConfig)} when the
+     * calling app does not want to customize the activity shown to the user.
+     * </p>
      */
     @NonNull
     public Intent createScreenCaptureIntent() {
@@ -99,20 +107,25 @@
      *
      * <p>
      * If {@link MediaProjectionConfig} was created from:
-     * <li>
-     *     <ul>
+     * <ul>
+     *     <li>
      *         {@link MediaProjectionConfig#createConfigForDisplay(int)}, then creates an
      *         {@link Intent} for capturing this particular display. The activity limits the user's
      *         choice to just the display specified.
-     *     </ul>
-     *     <ul>
+     *     </li>
+     *     <li>
      *         {@link MediaProjectionConfig#createConfigForUserChoice()}, then creates an
      *         {@link Intent} for deferring which region to capture to the user. This gives the
      *         user the same behaviour as calling {@link #createScreenCaptureIntent()}. The
      *         activity gives the user the choice between
      *         {@link android.view.Display#DEFAULT_DISPLAY}, or a different region.
-     *     </ul>
-     * </li>
+     *     </li>
+     * </ul>
+     * </p>
+     * <p>
+     * Should be used instead of {@link #createScreenCaptureIntent()} when the calling app wants to
+     * customize the activity shown to the user.
+     * </p>
      *
      * @param config Customization for the {@link MediaProjection} that this {@link Intent} requests
      *               the user's consent for.
diff --git a/media/java/android/media/tv/AdBuffer.aidl b/media/java/android/media/tv/AdBuffer.aidl
new file mode 100644
index 0000000..b1e2d3e
--- /dev/null
+++ b/media/java/android/media/tv/AdBuffer.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+parcelable AdBuffer;
diff --git a/media/java/android/media/tv/AdBuffer.java b/media/java/android/media/tv/AdBuffer.java
new file mode 100644
index 0000000..ed44508
--- /dev/null
+++ b/media/java/android/media/tv/AdBuffer.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.NonNull;
+import android.media.MediaCodec.BufferFlag;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SharedMemory;
+
+/**
+ * Buffer for advertisement data.
+ * @hide
+ */
+public class AdBuffer implements Parcelable {
+    private final int mId;
+    @NonNull
+    private final String mMimeType;
+    @NonNull
+    private final SharedMemory mBuffer;
+    private final int mOffset;
+    private final int mLength;
+    private final long mPresentationTimeUs;
+    private final int mFlags;
+
+    public AdBuffer(
+            int id,
+            @NonNull String mimeType,
+            @NonNull SharedMemory buffer,
+            int offset,
+            int length,
+            long presentationTimeUs,
+            @BufferFlag int flags) {
+        this.mId = id;
+        this.mMimeType = mimeType;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mimeType);
+        this.mBuffer = buffer;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, buffer);
+        this.mOffset = offset;
+        this.mLength = length;
+        this.mPresentationTimeUs = presentationTimeUs;
+        this.mFlags = flags;
+    }
+
+    /**
+     * Gets corresponding AD request ID.
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * Gets the mime type of the data.
+     */
+    @NonNull
+    public String getMimeType() {
+        return mMimeType;
+    }
+
+    /**
+     * Gets the shared memory which stores the data.
+     */
+    @NonNull
+    public SharedMemory getSharedMemory() {
+        return mBuffer;
+    }
+
+    /**
+     * Gets the offset of the buffer.
+     */
+    public int getOffset() {
+        return mOffset;
+    }
+
+    /**
+     * Gets the data length.
+     */
+    public int getLength() {
+        return mLength;
+    }
+
+    /**
+     * Gets the presentation time in microseconds.
+     */
+    public long getPresentationTimeUs() {
+        return mPresentationTimeUs;
+    }
+
+    /**
+     * Gets the flags.
+     */
+    @BufferFlag
+    public int getFlags() {
+        return mFlags;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        dest.writeInt(mId);
+        dest.writeString(mMimeType);
+        dest.writeTypedObject(mBuffer, flags);
+        dest.writeInt(mOffset);
+        dest.writeInt(mLength);
+        dest.writeLong(mPresentationTimeUs);
+        dest.writeInt(mFlags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    private AdBuffer(@NonNull Parcel in) {
+        int id = in.readInt();
+        String mimeType = in.readString();
+        SharedMemory buffer = (SharedMemory) in.readTypedObject(SharedMemory.CREATOR);
+        int offset = in.readInt();
+        int length = in.readInt();
+        long presentationTimeUs = in.readLong();
+        int flags = in.readInt();
+
+        this.mId = id;
+        this.mMimeType = mimeType;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mMimeType);
+        this.mBuffer = buffer;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mBuffer);
+        this.mOffset = offset;
+        this.mLength = length;
+        this.mPresentationTimeUs = presentationTimeUs;
+        this.mFlags = flags;
+    }
+
+    public static final @NonNull Parcelable.Creator<AdBuffer> CREATOR =
+            new Parcelable.Creator<AdBuffer>() {
+                @Override
+                public AdBuffer[] newArray(int size) {
+                    return new AdBuffer[size];
+                }
+
+                @Override
+                public AdBuffer createFromParcel(Parcel in) {
+                    return new AdBuffer(in);
+            }
+    };
+}
diff --git a/media/java/android/media/tv/AdRequest.java b/media/java/android/media/tv/AdRequest.java
index f2fb93d..60dfc5e 100644
--- a/media/java/android/media/tv/AdRequest.java
+++ b/media/java/android/media/tv/AdRequest.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
@@ -69,10 +70,25 @@
     private final long mEchoInterval;
     private final String mMediaFileType;
     private final Bundle mMetadata;
+    private final Uri mUri;
 
     public AdRequest(int id, @RequestType int requestType,
             @Nullable ParcelFileDescriptor fileDescriptor, long startTime, long stopTime,
             long echoInterval, @Nullable String mediaFileType, @NonNull Bundle metadata) {
+        this(id, requestType, fileDescriptor, null, startTime, stopTime, echoInterval,
+                mediaFileType, metadata);
+    }
+
+    /** @hide */
+    public AdRequest(int id, @RequestType int requestType, @Nullable Uri uri, long startTime,
+            long stopTime, long echoInterval, @NonNull Bundle metadata) {
+        this(id, requestType, null, uri, startTime, stopTime, echoInterval, null, metadata);
+    }
+
+    private AdRequest(int id, @RequestType int requestType,
+            @Nullable ParcelFileDescriptor fileDescriptor, @Nullable Uri uri, long startTime,
+            long stopTime, long echoInterval, @Nullable String mediaFileType,
+            @NonNull Bundle metadata) {
         mId = id;
         mRequestType = requestType;
         mFileDescriptor = fileDescriptor;
@@ -81,15 +97,23 @@
         mEchoInterval = echoInterval;
         mMediaFileType = mediaFileType;
         mMetadata = metadata;
+        mUri = uri;
     }
 
     private AdRequest(Parcel source) {
         mId = source.readInt();
         mRequestType = source.readInt();
-        if (source.readInt() != 0) {
+        int readInt = source.readInt();
+        if (readInt == 1) {
             mFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(source);
+            mUri = null;
+        } else if (readInt == 2) {
+            String stringUri = source.readString();
+            mUri = stringUri == null ? null : Uri.parse(stringUri);
+            mFileDescriptor = null;
         } else {
             mFileDescriptor = null;
+            mUri = null;
         }
         mStartTime = source.readLong();
         mStopTime = source.readLong();
@@ -117,7 +141,7 @@
      * Gets the file descriptor of the AD media.
      *
      * @return The file descriptor of the AD media. Can be {@code null} for
-     *         {@link #REQUEST_TYPE_STOP}
+     *         {@link #REQUEST_TYPE_STOP} or a URI is used.
      */
     @Nullable
     public ParcelFileDescriptor getFileDescriptor() {
@@ -125,6 +149,18 @@
     }
 
     /**
+     * Gets the URI of the AD media.
+     *
+     * @return The URI of the AD media. Can be {@code null} for {@link #REQUEST_TYPE_STOP} or a file
+     *         descriptor is used.
+     * @hide
+     */
+    @Nullable
+    public Uri getUri() {
+        return mUri;
+    }
+
+    /**
      * Gets the start time of the AD media in milliseconds.
      * <p>0 means start immediately
      */
@@ -189,6 +225,10 @@
         if (mFileDescriptor != null) {
             dest.writeInt(1);
             mFileDescriptor.writeToParcel(dest, flags);
+        } else if (mUri != null) {
+            dest.writeInt(2);
+            String stringUri = mUri.toString();
+            dest.writeString(stringUri);
         } else {
             dest.writeInt(0);
         }
diff --git a/media/java/android/media/tv/AdResponse.java b/media/java/android/media/tv/AdResponse.java
index 08c66ab..a15e8c1 100644
--- a/media/java/android/media/tv/AdResponse.java
+++ b/media/java/android/media/tv/AdResponse.java
@@ -34,7 +34,8 @@
             RESPONSE_TYPE_PLAYING,
             RESPONSE_TYPE_FINISHED,
             RESPONSE_TYPE_STOPPED,
-            RESPONSE_TYPE_ERROR
+            RESPONSE_TYPE_ERROR,
+            RESPONSE_TYPE_BUFFERING
     })
     public @interface ResponseType {}
 
@@ -42,6 +43,8 @@
     public static final int RESPONSE_TYPE_FINISHED = 2;
     public static final int RESPONSE_TYPE_STOPPED = 3;
     public static final int RESPONSE_TYPE_ERROR = 4;
+    /** @hide */
+    public static final int RESPONSE_TYPE_BUFFERING = 5;
 
     public static final @NonNull Parcelable.Creator<AdResponse> CREATOR =
             new Parcelable.Creator<AdResponse>() {
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index 49148ce..ed2fd20 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -17,6 +17,7 @@
 package android.media.tv;
 
 import android.content.ComponentName;
+import android.media.tv.AdBuffer;
 import android.media.tv.AdResponse;
 import android.media.tv.AitInfo;
 import android.media.tv.BroadcastInfoResponse;
@@ -59,4 +60,5 @@
 
     // For ad response
     void onAdResponse(in AdResponse response, int seq);
+    void onAdBufferConsumed(in AdBuffer buffer, int seq);
 }
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 2a33ee6..f7c1e3c 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -20,6 +20,7 @@
 import android.content.Intent;
 import android.graphics.Rect;
 import android.media.PlaybackParams;
+import android.media.tv.AdBuffer;
 import android.media.tv.AdRequest;
 import android.media.tv.BroadcastInfoRequest;
 import android.media.tv.DvbDeviceInfo;
@@ -110,6 +111,7 @@
 
     // For ad request
     void requestAd(in IBinder sessionToken, in AdRequest request, int userId);
+    void notifyAdBuffer(in IBinder sessionToken, in AdBuffer buffer, int userId);
 
     // For TV input hardware binding
     List<TvInputHardwareInfo> getHardwareList();
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 9820034..326b98d 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -18,6 +18,7 @@
 
 import android.graphics.Rect;
 import android.media.PlaybackParams;
+import android.media.tv.AdBuffer;
 import android.media.tv.AdRequest;
 import android.media.tv.BroadcastInfoRequest;
 import android.media.tv.TvTrackInfo;
@@ -71,4 +72,5 @@
 
     // For ad request
     void requestAd(in AdRequest request);
+    void notifyAdBuffer(in AdBuffer buffer);
 }
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 9dfdb78..b2a8d1c 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.media.tv.AdBuffer;
 import android.media.tv.AdResponse;
 import android.media.tv.AitInfo;
 import android.media.tv.BroadcastInfoResponse;
@@ -56,4 +57,5 @@
 
     // For ad response
     void onAdResponse(in AdResponse response);
+    void onAdBufferConsumed(in AdBuffer buffer);
 }
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 8911f6c..634f102 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -74,6 +74,7 @@
     private static final int DO_REMOVE_BROADCAST_INFO = 25;
     private static final int DO_SET_IAPP_NOTIFICATION_ENABLED = 26;
     private static final int DO_REQUEST_AD = 27;
+    private static final int DO_NOTIFY_AD_BUFFER = 28;
 
     private final boolean mIsRecordingSession;
     private final HandlerCaller mCaller;
@@ -224,6 +225,7 @@
             case DO_START_RECORDING: {
                 SomeArgs args = (SomeArgs) msg.obj;
                 mTvInputRecordingSessionImpl.startRecording((Uri) args.arg1, (Bundle) args.arg2);
+                args.recycle();
                 break;
             }
             case DO_STOP_RECORDING: {
@@ -254,6 +256,10 @@
                 mTvInputSessionImpl.requestAd((AdRequest) msg.obj);
                 break;
             }
+            case DO_NOTIFY_AD_BUFFER: {
+                mTvInputSessionImpl.notifyAdBuffer((AdBuffer) msg.obj);
+                break;
+            }
             default: {
                 Log.w(TAG, "Unhandled message code: " + msg.what);
                 break;
@@ -424,6 +430,11 @@
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_REQUEST_AD, request));
     }
 
+    @Override
+    public void notifyAdBuffer(AdBuffer buffer) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_AD_BUFFER, buffer));
+    }
+
     private final class TvInputEventReceiver extends InputEventReceiver {
         public TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
             super(inputChannel, looper);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 04d28e7..690fcb1 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -965,6 +965,19 @@
                 });
             }
         }
+
+        void postAdBufferConsumed(AdBuffer buffer) {
+            if (mSession.mIAppNotificationEnabled) {
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (mSession.getInteractiveAppSession() != null) {
+                            mSession.getInteractiveAppSession().notifyAdBufferConsumed(buffer);
+                        }
+                    }
+                });
+            }
+        }
     }
 
     /**
@@ -1412,6 +1425,18 @@
                     record.postAdResponse(response);
                 }
             }
+
+            @Override
+            public void onAdBufferConsumed(AdBuffer buffer, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postAdBufferConsumed(buffer);
+                }
+            }
         };
         ITvInputManagerCallback managerCallback = new ITvInputManagerCallback.Stub() {
             @Override
@@ -3204,6 +3229,21 @@
             }
         }
 
+        /**
+         * Notifies when the advertisement buffer is filled and ready to be read.
+         */
+        public void notifyAdBuffer(AdBuffer buffer) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifyAdBuffer(mToken, buffer, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
         private final class InputEventHandler extends Handler {
             public static final int MSG_SEND_INPUT_EVENT = 1;
             public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 70acf25..c46cdbc 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -913,6 +913,27 @@
             });
         }
 
+        /**
+         * Notifies the advertisement buffer is consumed.
+         * @hide
+         */
+        public void notifyAdBufferConsumed(AdBuffer buffer) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) Log.d(TAG, "notifyAdBufferConsumed");
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onAdBufferConsumed(buffer);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in notifyAdBufferConsumed", e);
+                    }
+                }
+            });
+        }
+
         private void notifyTimeShiftStartPositionChanged(final long timeMs) {
             executeOrPostRunnableOnMainThread(new Runnable() {
                 @MainThread
@@ -1129,6 +1150,13 @@
         }
 
         /**
+         * Called when advertisement buffer is ready.
+         * @hide
+         */
+        public void onAdBuffer(AdBuffer buffer) {
+        }
+
+        /**
          * Tunes to a given channel.
          *
          * <p>No video will be displayed until {@link #notifyVideoAvailable()} is called.
@@ -1753,6 +1781,10 @@
             onRequestAd(request);
         }
 
+        void notifyAdBuffer(AdBuffer buffer) {
+            onAdBuffer(buffer);
+        }
+
         /**
          * Takes care of dispatching incoming input events and tells whether the event was handled.
          */
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
index 9b8ec5e..98357fc 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
@@ -17,6 +17,7 @@
 package android.media.tv.interactive;
 
 import android.graphics.Rect;
+import android.media.tv.AdBuffer;
 import android.media.tv.AdRequest;
 import android.media.tv.BroadcastInfoRequest;
 import android.net.Uri;
@@ -37,6 +38,7 @@
     void onSessionStateChanged(int state, int err, int seq);
     void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId, int seq);
     void onTeletextAppStateChanged(int state, int seq);
+    void onAdBuffer(in AdBuffer buffer, int seq);
     void onCommandRequest(in String cmdType, in Bundle parameters, int seq);
     void onSetVideoBounds(in Rect rect, int seq);
     void onRequestCurrentChannelUri(int seq);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
index 38fc717..8bfceee 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
@@ -17,6 +17,7 @@
 package android.media.tv.interactive;
 
 import android.graphics.Rect;
+import android.media.tv.AdBuffer;
 import android.media.tv.AdResponse;
 import android.media.tv.BroadcastInfoResponse;
 import android.media.tv.TvTrackInfo;
@@ -71,6 +72,7 @@
     void notifyBroadcastInfoResponse(in IBinder sessionToken, in BroadcastInfoResponse response,
             int UserId);
     void notifyAdResponse(in IBinder sessionToken, in AdResponse response, int UserId);
+    void notifyAdBufferConsumed(in IBinder sessionToken, in AdBuffer buffer, int userId);
 
     void createMediaView(in IBinder sessionToken, in IBinder windowToken, in Rect frame,
             int userId);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
index 9e33536..1953117 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl
@@ -19,6 +19,7 @@
 import android.graphics.Rect;
 import android.media.tv.BroadcastInfoResponse;
 import android.net.Uri;
+import android.media.tv.AdBuffer;
 import android.media.tv.AdResponse;
 import android.media.tv.BroadcastInfoResponse;
 import android.media.tv.TvTrackInfo;
@@ -59,6 +60,7 @@
     void dispatchSurfaceChanged(int format, int width, int height);
     void notifyBroadcastInfoResponse(in BroadcastInfoResponse response);
     void notifyAdResponse(in AdResponse response);
+    void notifyAdBufferConsumed(in AdBuffer buffer);
 
     void createMediaView(in IBinder windowToken, in Rect frame);
     void relayoutMediaView(in Rect frame);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
index 4ce5871..cd4f410 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
@@ -17,6 +17,7 @@
 package android.media.tv.interactive;
 
 import android.graphics.Rect;
+import android.media.tv.AdBuffer;
 import android.media.tv.AdRequest;
 import android.media.tv.BroadcastInfoRequest;
 import android.media.tv.interactive.ITvInteractiveAppSession;
@@ -36,6 +37,7 @@
     void onSessionStateChanged(int state, int err);
     void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId);
     void onTeletextAppStateChanged(int state);
+    void onAdBuffer(in AdBuffer buffer);
     void onCommandRequest(in String cmdType, in Bundle parameters);
     void onSetVideoBounds(in Rect rect);
     void onRequestCurrentChannelUri();
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
index a2fdfe0..b646326 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Rect;
+import android.media.tv.AdBuffer;
 import android.media.tv.AdResponse;
 import android.media.tv.BroadcastInfoResponse;
 import android.media.tv.TvContentRating;
@@ -83,6 +84,7 @@
     private static final int DO_REMOVE_MEDIA_VIEW = 29;
     private static final int DO_NOTIFY_RECORDING_STARTED = 30;
     private static final int DO_NOTIFY_RECORDING_STOPPED = 31;
+    private static final int DO_NOTIFY_AD_BUFFER_CONSUMED = 32;
 
     private final HandlerCaller mCaller;
     private Session mSessionImpl;
@@ -253,6 +255,10 @@
                 mSessionImpl.removeMediaView(true);
                 break;
             }
+            case DO_NOTIFY_AD_BUFFER_CONSUMED: {
+                mSessionImpl.notifyAdBufferConsumed((AdBuffer) msg.obj);
+                break;
+            }
             default: {
                 Log.w(TAG, "Unhandled message code: " + msg.what);
                 break;
@@ -425,6 +431,11 @@
     }
 
     @Override
+    public void notifyAdBufferConsumed(AdBuffer buffer) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_NOTIFY_AD_BUFFER_CONSUMED, buffer));
+    }
+
+    @Override
     public void createMediaView(IBinder windowToken, Rect frame) {
         mCaller.executeOrSendMessage(
                 mCaller.obtainMessageOO(DO_CREATE_MEDIA_VIEW, windowToken, frame));
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
index 287df40..c57efc8 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -23,6 +23,7 @@
 import android.annotation.SystemService;
 import android.content.Context;
 import android.graphics.Rect;
+import android.media.tv.AdBuffer;
 import android.media.tv.AdRequest;
 import android.media.tv.AdResponse;
 import android.media.tv.BroadcastInfoRequest;
@@ -558,6 +559,18 @@
                     record.postTeletextAppStateChanged(state);
                 }
             }
+
+            @Override
+            public void onAdBuffer(AdBuffer buffer, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postAdBuffer(buffer);
+                }
+            }
         };
         ITvInteractiveAppManagerCallback managerCallback =
                 new ITvInteractiveAppManagerCallback.Stub() {
@@ -1278,6 +1291,21 @@
         }
 
         /**
+         * Notifies the advertisement buffer is consumed.
+         */
+        public void notifyAdBufferConsumed(AdBuffer buffer) {
+            if (mToken == null) {
+                Log.w(TAG, "The session has been already released");
+                return;
+            }
+            try {
+                mService.notifyAdBufferConsumed(mToken, buffer, mUserId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
          * Releases this session.
          */
         public void release() {
@@ -1808,6 +1836,17 @@
                 }
             });
         }
+
+        void postAdBuffer(AdBuffer buffer) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (mSession.getInputSession() != null) {
+                        mSession.getInputSession().notifyAdBuffer(buffer);
+                    }
+                }
+            });
+        }
     }
 
     /**
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 00150d5..4ed7ca5 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -30,6 +30,7 @@
 import android.content.Intent;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.media.tv.AdBuffer;
 import android.media.tv.AdRequest;
 import android.media.tv.AdResponse;
 import android.media.tv.BroadcastInfoRequest;
@@ -616,6 +617,13 @@
         public void onAdResponse(@NonNull AdResponse response) {
         }
 
+        /**
+         * Called when an advertisement buffer is consumed.
+         * @hide
+         */
+        public void onAdBufferConsumed(AdBuffer buffer) {
+        }
+
         @Override
         public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
             return false;
@@ -1190,6 +1198,17 @@
         }
 
         /**
+         * Calls {@link #onAdBufferConsumed}.
+         */
+        void notifyAdBufferConsumed(AdBuffer buffer) {
+            if (DEBUG) {
+                Log.d(TAG,
+                        "notifyAdBufferConsumed (buffer=" + buffer + ")");
+            }
+            onAdBufferConsumed(buffer);
+        }
+
+        /**
          * Calls {@link #onRecordingStarted(String)}.
          */
         void notifyRecordingStarted(String recordingId) {
@@ -1290,6 +1309,33 @@
             });
         }
 
+
+        /**
+         * Notifies when the advertisement buffer is filled and ready to be read.
+         * @hide
+         */
+        @CallSuper
+        public void notifyAdBuffer(AdBuffer buffer) {
+            executeOrPostRunnableOnMainThread(new Runnable() {
+                @MainThread
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) {
+                            Log.d(TAG,
+                                    "notifyAdBuffer(buffer=" + buffer + ")");
+                        }
+                        if (mSessionCallback != null) {
+                            mSessionCallback.onAdBuffer(buffer);
+                        }
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in notifyAdBuffer", e);
+                    }
+                }
+            });
+        }
+
+
         /**
          * Takes care of dispatching incoming input events and tells whether the event was handled.
          */
diff --git a/packages/CredentialManager/res/drawable/ic_profile.xml b/packages/CredentialManager/res/drawable/ic_profile.xml
deleted file mode 100644
index ae65940..0000000
--- a/packages/CredentialManager/res/drawable/ic_profile.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
-        android:viewportWidth="46"
-        android:viewportHeight="46"
-        android:width="46dp"
-        android:height="46dp">
-    <path
-        android:pathData="M45.4247 22.9953C45.4247 35.0229 35.4133 44.7953 23.0359 44.7953C10.6585 44.7953 0.646973 35.0229 0.646973 22.9953C0.646973 10.9677 10.6585 1.19531 23.0359 1.19531C35.4133 1.19531 45.4247 10.9677 45.4247 22.9953Z"
-        android:strokeColor="#202124"
-        android:strokeAlpha="0.13"
-        android:strokeWidth="1" />
-</vector>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index a3ebf1e..91ffc44 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -9,6 +9,8 @@
   <string name="string_cancel">Cancel</string>
   <!-- Button label to confirm choosing the default dialog information and continue. [CHAR LIMIT=40] -->
   <string name="string_continue">Continue</string>
+  <!-- Button label to create this credential in other available places. [CHAR LIMIT=40] -->
+  <string name="string_more_options">More options</string>
   <!-- This appears as a text button where users can click to create this passkey in other available places. [CHAR LIMIT=80] -->
   <string name="string_create_in_another_place">Create in another place</string>
   <!-- This appears as a text button where users can click to create this password or other credential types in other available places. [CHAR LIMIT=80] -->
@@ -20,11 +22,11 @@
   <!-- This appears as the title of the modal bottom sheet introducing what is passkey to users. [CHAR LIMIT=200] -->
   <string name="passkey_creation_intro_title">Safer with passkeys</string>
   <!-- This appears as the description body of the modal bottom sheet introducing why passkey beneficial on the passwords side. [CHAR LIMIT=200] -->
-  <string name="passkey_creation_intro_body_password">No need to create or remember complex passwords</string>
+  <string name="passkey_creation_intro_body_password">With passkeys, you don’t need to create or remember complex passwords</string>
   <!-- This appears as the description body of the modal bottom sheet introducing why passkey beneficial on the safety side. [CHAR LIMIT=200] -->
-  <string name="passkey_creation_intro_body_fingerprint">Use your fingerprint, face, or screen lock to create a unique passkey</string>
+  <string name="passkey_creation_intro_body_fingerprint">Passkeys are encrypted digital keys you create using your fingerprint, face, or screen lock</string>
   <!-- This appears as the description body of the modal bottom sheet introducing why passkey beneficial on the using other devices side. [CHAR LIMIT=200] -->
-  <string name="passkey_creation_intro_body_device">Passkeys are saved to a password manager, so you can sign in on other devices</string>
+  <string name="passkey_creation_intro_body_device">They are saved to a password manager, so you can sign in on other devices</string>
   <!-- This appears as the title of the modal bottom sheet which provides all available providers for users to choose. [CHAR LIMIT=200] -->
   <string name="choose_provider_title">Choose where to <xliff:g id="createTypes" example="create your passkeys">%1$s</xliff:g></string>
   <!-- Create types which are inserted as a placeholder for string choose_provider_title. [CHAR LIMIT=200] -->
@@ -33,26 +35,23 @@
   <string name="save_your_sign_in_info">save your sign-in info</string>
 
   <!-- This appears as the description body of the modal bottom sheet which provides all available providers for users to choose. [CHAR LIMIT=200] -->
-  <string name="choose_provider_body">Set a default password manager to save your passwords and passkeys and sign in faster next time.</string>
+  <string name="choose_provider_body">Select a password manager to save your info and sign in faster next time.</string>
   <!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is passkey. [CHAR LIMIT=200] -->
-  <string name="choose_create_option_passkey_title">Create a passkey in <xliff:g id="providerInfoDisplayName" example="Google Password Manager">%1$s</xliff:g>?</string>
+  <string name="choose_create_option_passkey_title">Create passkey for <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
   <!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is password. [CHAR LIMIT=200] -->
-  <string name="choose_create_option_password_title">Save your password to <xliff:g id="providerInfoDisplayName" example="Google Password Manager">%1$s</xliff:g>?</string>
+  <string name="choose_create_option_password_title">Save password for <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
   <!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is others. [CHAR LIMIT=200] -->
-  <string name="choose_create_option_sign_in_title">Save your sign-in info to <xliff:g id="providerInfoDisplayName" example="Google Password Manager">%1$s</xliff:g>?</string>
+  <string name="choose_create_option_sign_in_title">Save sign-in info for <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
   <!-- This appears as the description body of the modal bottom sheet for users to choose the create option inside a provider. [CHAR LIMIT=200] -->
-  <string name="choose_create_option_description">You can use your <xliff:g id="appDomainName" example="Tribank">%1$s</xliff:g> <xliff:g id="type" example="passkey">%2$s</xliff:g> on any device. It is saved to <xliff:g id="providerInfoDisplayName" example="Google Password Manager">%3$s</xliff:g> for <xliff:g id="createInfoDisplayName" example="[email protected]">%4$s</xliff:g></string>
-  <!-- Types which are inserted as a placeholder for string choose_create_option_description. [CHAR LIMIT=200] -->
+  <string name="choose_create_option_description">You can use your <xliff:g id="appDomainName" example="Tribank">%1$s</xliff:g> <xliff:g id="credentialTypes" example="passkey">%2$s</xliff:g> on any device. It is saved to <xliff:g id="providerInfoDisplayName" example="Google Password Manager">%3$s</xliff:g> for <xliff:g id="createInfoDisplayName" example="[email protected]">%4$s</xliff:g>.</string>
+  <!-- Types which are inserted as a placeholder as credentialTypes for other strings. [CHAR LIMIT=200] -->
   <string name="passkey">passkey</string>
   <string name="password">password</string>
   <string name="sign_ins">sign-ins</string>
+  <string name="sign_in_info">sign-in info</string>
 
-  <!-- This appears as the title of the modal bottom sheet for users to choose other available places the created passkey can be created to. [CHAR LIMIT=200] -->
-  <string name="create_passkey_in_title">Create passkey in</string>
   <!-- This appears as the title of the modal bottom sheet for users to choose other available places the created password can be saved to. [CHAR LIMIT=200] -->
-  <string name="save_password_to_title">Save password to</string>
-  <!-- This appears as the title of the modal bottom sheet for users to choose other available places the created other credential types can be saved to. [CHAR LIMIT=200] -->
-  <string name="save_sign_in_to_title">Save sign-in to</string>
+  <string name="save_credential_to_title">Save <xliff:g id="credentialTypes" example="passkey">%1$s</xliff:g> to</string>
   <!-- This appears as the title of the modal bottom sheet for users to choose to create a passkey on another device. [CHAR LIMIT=200] -->
   <string name="create_passkey_in_other_device_title">Create a passkey in another device?</string>
   <!-- This appears as the title of the modal bottom sheet for users to confirm whether they should use the selected provider as default or not. [CHAR LIMIT=200] -->
@@ -65,11 +64,13 @@
   <!-- Button label to set the selected provider on the modal bottom sheet not as default but just use once. [CHAR LIMIT=40] -->
   <string name="use_once">Use once</string>
   <!-- Appears as an option row subtitle to show how many passwords and passkeys are saved in this option when there are passwords and passkeys. [CHAR LIMIT=80] -->
-  <string name="more_options_usage_passwords_passkeys"><xliff:g id="passwordsNumber" example="1">%1$s</xliff:g> passwords, <xliff:g id="passkeysNumber" example="2">%2$s</xliff:g> passkeys</string>
+  <string name="more_options_usage_passwords_passkeys"><xliff:g id="passwordsNumber" example="1">%1$s</xliff:g> passwords • <xliff:g id="passkeysNumber" example="2">%2$s</xliff:g> passkeys</string>
   <!-- Appears as an option row subtitle to show how many passwords and passkeys are saved in this option when there are only passwords. [CHAR LIMIT=80] -->
   <string name="more_options_usage_passwords"><xliff:g id="passwordsNumber" example="3">%1$s</xliff:g> passwords</string>
   <!-- Appears as an option row subtitle to show how many passwords and passkeys are saved in this option when there are only passkeys. [CHAR LIMIT=80] -->
   <string name="more_options_usage_passkeys"><xliff:g id="passkeysNumber" example="4">%1$s</xliff:g> passkeys</string>
+  <!-- Appears as an option row subtitle to show how many total credentials are saved in this option when the request type is other sign-ins. [CHAR LIMIT=80] -->
+  <string name="more_options_usage_credentials"><xliff:g id="totalCredentialsNumber" example="5">%1$s</xliff:g> credentials</string>
   <!-- Appears before a request display name when the credential type is passkey . [CHAR LIMIT=80] -->
   <string name="passkey_before_subtitle">Passkey</string>
   <!-- Appears as an option row title that users can choose to use another device for this creation. [CHAR LIMIT=80] -->
@@ -92,8 +93,8 @@
   <string name="get_dialog_title_choose_sign_in_for">Choose a saved sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string>
   <!-- Appears as an option row for viewing all the available sign-in options. [CHAR LIMIT=80] -->
   <string name="get_dialog_use_saved_passkey_for">Sign in another way</string>
-  <!-- Button label to close the dialog when the user does not want to use any sign-in. [CHAR LIMIT=40] -->
-  <string name="get_dialog_button_label_no_thanks">No thanks</string>
+  <!-- Appears as a text button in the snackbar for users to click to view all options. [CHAR LIMIT=80] -->
+  <string name="snackbar_action">View options</string>
   <!-- Button label to continue with the selected sign-in. [CHAR LIMIT=40] -->
   <string name="get_dialog_button_label_continue">Continue</string>
   <!-- Separator for sign-in type and username in a sign-in entry. -->
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 37453c9..6a6a3c5 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -36,17 +36,22 @@
 import android.credentials.ui.BaseDialogResult
 import android.credentials.ui.ProviderPendingIntentResponse
 import android.credentials.ui.UserSelectionDialogResult
-import android.graphics.drawable.Icon
 import android.os.Binder
 import android.os.Bundle
 import android.os.ResultReceiver
 import android.service.credentials.CredentialProviderService
 import android.util.ArraySet
-import com.android.credentialmanager.createflow.CreateCredentialUiState
+import com.android.credentialmanager.createflow.RequestDisplayInfo
+import com.android.credentialmanager.createflow.EnabledProviderInfo
+import com.android.credentialmanager.createflow.DisabledProviderInfo
 import com.android.credentialmanager.getflow.GetCredentialUiState
 import com.android.credentialmanager.jetpack.developer.CreatePasswordRequest.Companion.toBundle
 import com.android.credentialmanager.jetpack.developer.CreatePublicKeyCredentialRequest
 import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
+import com.android.credentialmanager.jetpack.provider.Action
+import com.android.credentialmanager.jetpack.provider.CreateEntry
+import com.android.credentialmanager.jetpack.provider.CredentialCountInformation
+import com.android.credentialmanager.jetpack.provider.CredentialEntry
 
 // Consider repo per screen, similar to view model?
 class CredentialManagerRepo(
@@ -130,19 +135,24 @@
     )
   }
 
-  fun createCredentialInitialUiState(): CreateCredentialUiState {
-    val requestDisplayInfo = CreateFlowUtils.toRequestDisplayInfo(requestInfo, context)
+  fun getCreateProviderEnableListInitialUiState(): List<EnabledProviderInfo> {
     val providerEnabledList = CreateFlowUtils.toEnabledProviderList(
       // Handle runtime cast error
-      providerEnabledList as List<CreateCredentialProviderData>, requestDisplayInfo, context)
-    val providerDisabledList = CreateFlowUtils.toDisabledProviderList(
-      // Handle runtime cast error
-      providerDisabledList, context)
+      providerEnabledList as List<CreateCredentialProviderData>, context)
     providerEnabledList.forEach{providerInfo -> providerInfo.createOptions =
       providerInfo.createOptions.sortedWith(compareBy { it.lastUsedTimeMillis }).reversed()
     }
-    return CreateFlowUtils.toCreateCredentialUiState(
-      providerEnabledList, providerDisabledList, requestDisplayInfo, false)
+    return providerEnabledList
+  }
+
+  fun getCreateProviderDisableListInitialUiState(): List<DisabledProviderInfo>? {
+    return CreateFlowUtils.toDisabledProviderList(
+      // Handle runtime cast error
+      providerDisabledList, context)
+  }
+
+  fun getCreateRequestDisplayInfoInitialUiState(): RequestDisplayInfo {
+    return CreateFlowUtils.toRequestDisplayInfo(requestInfo, context)
   }
 
   companion object {
@@ -163,33 +173,33 @@
 
   // TODO: below are prototype functionalities. To be removed for productionization.
   private fun testCreateCredentialEnabledProviderList(): List<CreateCredentialProviderData> {
-    return listOf(
-      CreateCredentialProviderData
-        .Builder("io.enpass.app")
-        .setSaveEntries(
-          listOf<Entry>(
-            newCreateEntry("key1", "subkey-1", "[email protected]",
-              20, 7, 27, 10000),
-            newCreateEntry("key1", "subkey-2", "[email protected]",
-              20, 7, 27, 11000),
-          )
-        )
-        .setRemoteEntry(
-          newRemoteEntry("key2", "subkey-1")
-        )
-        .build(),
-      CreateCredentialProviderData
-        .Builder("com.dashlane")
-        .setSaveEntries(
-          listOf<Entry>(
-            newCreateEntry("key1", "subkey-3", "[email protected]",
-              20, 7, 27, 30000),
-            newCreateEntry("key1", "subkey-4", "[email protected]",
-              20, 7, 27, 31000),
-          )
-        )
-        .build(),
-    )
+      return listOf(
+          CreateCredentialProviderData
+              .Builder("io.enpass.app")
+              .setSaveEntries(
+                  listOf<Entry>(
+                      newCreateEntry("key1", "subkey-1", "[email protected]",
+                          20, 7, 27, 10000),
+                      newCreateEntry("key1", "subkey-2", "[email protected]",
+                          20, 7, 27, 11000),
+                  )
+              )
+              .setRemoteEntry(
+                  newRemoteEntry("key2", "subkey-1")
+              )
+              .build(),
+          CreateCredentialProviderData
+              .Builder("com.dashlane")
+              .setSaveEntries(
+                  listOf<Entry>(
+                      newCreateEntry("key1", "subkey-3", "[email protected]",
+                          20, 7, 27, 30000),
+                      newCreateEntry("key1", "subkey-4", "[email protected]",
+                          20, 7, 27, 31000),
+                  )
+              )
+              .build(),
+      )
   }
 
   private fun testDisabledProviderList(): List<DisabledProviderData>? {
@@ -258,137 +268,107 @@
     )
   }
 
-  private fun newActionEntry(
-    key: String,
-    subkey: String,
-    credentialType: String,
-    text: String,
-    subtext: String? = null,
-  ): Entry {
-    val slice = Slice.Builder(
-      Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(credentialType, 1)
-    ).addText(
-      text, null, listOf(Entry.HINT_ACTION_TITLE)
-    )
-    if (subtext != null) {
-      slice.addText(subtext, null, listOf(Entry.HINT_ACTION_SUBTEXT))
+    private fun newActionEntry(
+            key: String,
+            subkey: String,
+            credentialType: String,
+            text: String,
+            subtext: String? = null,
+    ): Entry {
+        val action = Action(text, subtext, null)
+
+        return Entry(
+                key,
+                subkey,
+                Action.toSlice(action)
+        )
     }
-    return Entry(
-      key,
-      subkey,
-      slice.build()
-    )
-  }
 
-  private fun newAuthenticationEntry(
-    key: String,
-    subkey: String,
-    credentialType: String,
-  ): Entry {
-    val slice = Slice.Builder(
-      Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(credentialType, 1)
-    )
-    return Entry(
-      key,
-      subkey,
-      slice.build()
-    )
-  }
-
-  private fun newGetEntry(
-    key: String,
-    subkey: String,
-    credentialType: String,
-    credentialTypeDisplayName: String,
-    userName: String,
-    userDisplayName: String?,
-    lastUsedTimeMillis: Long?,
-  ): Entry {
-    val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
-      .setPackage("com.androidauth.androidvault")
-    intent.putExtra("provider_extra_sample", "testprovider")
-
-    val pendingIntent = PendingIntent.getActivity(context, 1,
-      intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
-              or PendingIntent.FLAG_ONE_SHOT))
-
-    val slice = Slice.Builder(
-      Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(credentialType, 1)
-    ).addText(
-      credentialTypeDisplayName, null, listOf(Entry.HINT_CREDENTIAL_TYPE_DISPLAY_NAME)
-    ).addText(
-      userName, null, listOf(Entry.HINT_USER_NAME)
-    ).addIcon(
-      Icon.createWithResource(context, R.drawable.ic_passkey),
-      null,
-      listOf(Entry.HINT_PROFILE_ICON))
-    if (userDisplayName != null) {
-      slice.addText(userDisplayName, null, listOf(Entry.HINT_PASSKEY_USER_DISPLAY_NAME))
+    private fun newAuthenticationEntry(
+            key: String,
+            subkey: String,
+            credentialType: String,
+    ): Entry {
+        val slice = Slice.Builder(
+                Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(credentialType, 1)
+        )
+        return Entry(
+                key,
+                subkey,
+                slice.build()
+        )
     }
-    if (lastUsedTimeMillis != null) {
-      slice.addLong(lastUsedTimeMillis, null, listOf(Entry.HINT_LAST_USED_TIME_MILLIS))
+
+    private fun newGetEntry(
+            key: String,
+            subkey: String,
+            credentialType: String,
+            credentialTypeDisplayName: String,
+            userName: String,
+            userDisplayName: String?,
+            lastUsedTimeMillis: Long?,
+    ): Entry {
+        val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
+                .setPackage("com.androidauth.androidvault")
+        intent.putExtra("provider_extra_sample", "testprovider")
+
+        val pendingIntent = PendingIntent.getActivity(context, 1,
+                intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+                or PendingIntent.FLAG_ONE_SHOT))
+
+        val credentialEntry = CredentialEntry(credentialType, credentialTypeDisplayName, userName,
+                userDisplayName, pendingIntent, lastUsedTimeMillis
+                ?: 0L, null, false)
+
+        return Entry(
+                key,
+                subkey,
+                CredentialEntry.toSlice(credentialEntry),
+                pendingIntent,
+                null
+        )
+  }
+
+    private fun newCreateEntry(
+            key: String,
+            subkey: String,
+            providerDisplayName: String,
+            passwordCount: Int,
+            passkeyCount: Int,
+            totalCredentialCount: Int,
+            lastUsedTimeMillis: Long,
+    ): Entry {
+        val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
+                .setPackage("com.androidauth.androidvault")
+        intent.putExtra("provider_extra_sample", "testprovider")
+        val pendingIntent = PendingIntent.getActivity(context, 1,
+                intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+                or PendingIntent.FLAG_ONE_SHOT))
+        val createPasswordRequest = android.service.credentials.CreateCredentialRequest(
+                android.service.credentials.CallingAppInfo(
+                        context.applicationInfo.packageName, ArraySet<Signature>()),
+                TYPE_PASSWORD_CREDENTIAL,
+                toBundle("[email protected]", "password123")
+        )
+        val fillInIntent = Intent().putExtra(
+                CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST,
+                createPasswordRequest)
+
+        val createEntry = CreateEntry(
+                providerDisplayName, pendingIntent,
+                null, lastUsedTimeMillis,
+                listOf(
+                        CredentialCountInformation.createPasswordCountInformation(passwordCount),
+                        CredentialCountInformation.createPublicKeyCountInformation(passkeyCount),
+                ))
+        return Entry(
+                key,
+                subkey,
+                CreateEntry.toSlice(createEntry),
+                pendingIntent,
+                fillInIntent,
+        )
     }
-    return Entry(
-      key,
-      subkey,
-      slice.build(),
-      pendingIntent,
-      null
-    )
-  }
-
-  private fun newCreateEntry(
-    key: String,
-    subkey: String,
-    providerDisplayName: String,
-    passwordCount: Int,
-    passkeyCount: Int,
-    totalCredentialCount: Int,
-    lastUsedTimeMillis: Long,
-  ): Entry {
-    val intent = Intent("com.androidauth.androidvault.CONFIRM_PASSWORD")
-      .setPackage("com.androidauth.androidvault")
-    intent.putExtra("provider_extra_sample", "testprovider")
-    val pendingIntent = PendingIntent.getActivity(context, 1,
-      intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
-              or PendingIntent.FLAG_ONE_SHOT))
-    val createPasswordRequest = android.service.credentials.CreateCredentialRequest(
-            android.service.credentials.CallingAppInfo(
-                    context.applicationInfo.packageName, ArraySet<Signature>()),
-            TYPE_PASSWORD_CREDENTIAL,
-            toBundle("[email protected]", "password123")
-    )
-    val fillInIntent = Intent().putExtra(CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST,
-            createPasswordRequest)
-
-    val slice = Slice.Builder(
-      Entry.CREDENTIAL_MANAGER_ENTRY_URI, SliceSpec(Entry.VERSION, 1)
-    ).addText(
-        providerDisplayName, null, listOf(Entry.HINT_USER_PROVIDER_ACCOUNT_NAME))
-      .addIcon(
-        Icon.createWithResource(context, R.drawable.ic_passkey),
-        null,
-        listOf(Entry.HINT_CREDENTIAL_TYPE_ICON))
-      .addIcon(
-        Icon.createWithResource(context, R.drawable.ic_profile),
-        null,
-        listOf(Entry.HINT_PROFILE_ICON))
-      .addInt(
-        passwordCount, null, listOf(Entry.HINT_PASSWORD_COUNT))
-      .addInt(
-        passkeyCount, null, listOf(Entry.HINT_PASSKEY_COUNT))
-      .addInt(
-        totalCredentialCount, null, listOf(Entry.HINT_TOTAL_CREDENTIAL_COUNT))
-      .addLong(lastUsedTimeMillis, null, listOf(Entry.HINT_LAST_USED_TIME_MILLIS))
-      .build()
-    return Entry(
-      key,
-      subkey,
-      slice,
-      pendingIntent,
-      fillInIntent,
-    )
-  }
 
   private fun newRemoteEntry(
     key: String,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index db676b2..48aebec 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -42,9 +42,10 @@
 import com.android.credentialmanager.jetpack.developer.CreatePasswordRequest
 import com.android.credentialmanager.jetpack.developer.CreatePublicKeyCredentialRequest
 import com.android.credentialmanager.jetpack.developer.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL
-import com.android.credentialmanager.jetpack.provider.ActionUi
-import com.android.credentialmanager.jetpack.provider.CredentialEntryUi
-import com.android.credentialmanager.jetpack.provider.SaveEntryUi
+import com.android.credentialmanager.jetpack.provider.Action
+import com.android.credentialmanager.jetpack.provider.CredentialCountInformation
+import com.android.credentialmanager.jetpack.provider.CredentialEntry
+import com.android.credentialmanager.jetpack.provider.CreateEntry
 import org.json.JSONObject
 
 /** Utility functions for converting CredentialManager data structures to or from UI formats. */
@@ -107,7 +108,8 @@
       context: Context,
     ): List<CredentialEntryInfo> {
       return credentialEntries.map {
-        val credentialEntryUi = CredentialEntryUi.fromSlice(it.slice)
+        // TODO: handle NPE gracefully
+        val credentialEntry = CredentialEntry.fromSlice(it.slice)!!
 
         // Consider directly move the UI object into the class.
         return@map CredentialEntryInfo(
@@ -116,14 +118,13 @@
           entrySubkey = it.subkey,
           pendingIntent = it.pendingIntent,
           fillInIntent = it.frameworkExtrasIntent,
-          credentialType = credentialEntryUi.credentialType.toString(),
-          credentialTypeDisplayName = credentialEntryUi.credentialTypeDisplayName.toString(),
-          userName = credentialEntryUi.userName.toString(),
-          displayName = credentialEntryUi.userDisplayName?.toString(),
+          credentialType = credentialEntry.type.toString(),
+          credentialTypeDisplayName = credentialEntry.typeDisplayName.toString(),
+          userName = credentialEntry.username.toString(),
+          displayName = credentialEntry.displayName?.toString(),
           // TODO: proper fallback
-          icon = credentialEntryUi.entryIcon?.loadDrawable(context)
-            ?: context.getDrawable(R.drawable.ic_other_sign_in)!!,
-          lastUsedTimeMillis = credentialEntryUi.lastUsedTimeMillis,
+          icon = credentialEntry.icon?.loadDrawable(context),
+          lastUsedTimeMillis = credentialEntry.lastUsedTimeMillis,
         )
       }
     }
@@ -170,7 +171,8 @@
       providerIcon: Drawable,
     ): List<ActionEntryInfo> {
       return actionEntries.map {
-        val actionEntryUi = ActionUi.fromSlice(it.slice)
+        // TODO: handle NPE gracefully
+        val actionEntryUi = Action.fromSlice(it.slice)!!
 
         return@map ActionEntryInfo(
           providerId = providerId,
@@ -178,10 +180,10 @@
           entrySubkey = it.subkey,
           pendingIntent = it.pendingIntent,
           fillInIntent = it.frameworkExtrasIntent,
-          title = actionEntryUi.text.toString(),
+          title = actionEntryUi.title.toString(),
           // TODO: gracefully fail
           icon = providerIcon,
-          subTitle = actionEntryUi.subtext?.toString(),
+          subTitle = actionEntryUi.subTitle?.toString(),
         )
       }
     }
@@ -193,9 +195,8 @@
 
     fun toEnabledProviderList(
       providerDataList: List<CreateCredentialProviderData>,
-      requestDisplayInfo: RequestDisplayInfo,
       context: Context,
-    ): List<com.android.credentialmanager.createflow.EnabledProviderInfo> {
+    ): List<EnabledProviderInfo> {
       // TODO: get from the actual service info
       val packageManager = context.packageManager
 
@@ -216,7 +217,7 @@
           name = it.providerFlattenedComponentName,
           displayName = pkgInfo.applicationInfo.loadLabel(packageManager).toString(),
           createOptions = toCreationOptionInfoList(
-            it.providerFlattenedComponentName, it.saveEntries, requestDisplayInfo, context),
+            it.providerFlattenedComponentName, it.saveEntries, context),
           remoteEntry = toRemoteInfo(it.providerFlattenedComponentName, it.remoteEntry),
         )
       }
@@ -225,14 +226,14 @@
     fun toDisabledProviderList(
       providerDataList: List<DisabledProviderData>?,
       context: Context,
-    ): List<com.android.credentialmanager.createflow.DisabledProviderInfo>? {
+    ): List<DisabledProviderInfo>? {
       // TODO: get from the actual service info
       val packageManager = context.packageManager
       return providerDataList?.map {
         val pkgInfo = packageManager
           .getPackageInfo(it.providerFlattenedComponentName,
             PackageManager.PackageInfoFlags.of(0))
-        com.android.credentialmanager.createflow.DisabledProviderInfo(
+        DisabledProviderInfo(
           icon = pkgInfo.applicationInfo.loadIcon(packageManager)!!,
           name = it.providerFlattenedComponentName,
           displayName = pkgInfo.applicationInfo.loadLabel(packageManager).toString(),
@@ -292,14 +293,15 @@
     fun toCreateCredentialUiState(
       enabledProviders: List<EnabledProviderInfo>,
       disabledProviders: List<DisabledProviderInfo>?,
+      defaultProviderId: String?,
       requestDisplayInfo: RequestDisplayInfo,
       isOnPasskeyIntroStateAlready: Boolean,
+      isPasskeyFirstUse: Boolean,
     ): CreateCredentialUiState {
       var createOptionSize = 0
       var lastSeenProviderWithNonEmptyCreateOptions: EnabledProviderInfo? = null
       var remoteEntry: RemoteInfo? = null
       var defaultProvider: EnabledProviderInfo? = null
-      val defaultProviderId = UserConfigRepo.getInstance().getDefaultProviderId()
       enabledProviders.forEach {
           enabledProvider ->
         if (defaultProviderId != null) {
@@ -319,13 +321,18 @@
         enabledProviders = enabledProviders,
         disabledProviders = disabledProviders,
         toCreateScreenState(
-          createOptionSize, isOnPasskeyIntroStateAlready,
-          requestDisplayInfo, defaultProvider, remoteEntry),
+          /*createOptionSize=*/createOptionSize,
+          /*isOnPasskeyIntroStateAlready=*/isOnPasskeyIntroStateAlready,
+          /*requestDisplayInfo=*/requestDisplayInfo,
+          /*defaultProvider=*/defaultProvider, /*remoteEntry=*/remoteEntry,
+          /*isPasskeyFirstUse=*/isPasskeyFirstUse),
         requestDisplayInfo,
-        isOnPasskeyIntroStateAlready,
+        defaultProvider != null,
         toActiveEntry(
-          /*defaultProvider=*/defaultProvider, createOptionSize,
-          lastSeenProviderWithNonEmptyCreateOptions, remoteEntry),
+          /*defaultProvider=*/defaultProvider,
+          /*createOptionSize=*/createOptionSize,
+          /*lastSeenProviderWithNonEmptyCreateOptions=*/lastSeenProviderWithNonEmptyCreateOptions,
+          /*remoteEntry=*/remoteEntry),
       )
     }
 
@@ -335,9 +342,10 @@
       requestDisplayInfo: RequestDisplayInfo,
       defaultProvider: EnabledProviderInfo?,
       remoteEntry: RemoteInfo?,
+      isPasskeyFirstUse: Boolean,
     ): CreateScreenState {
       return if (
-        UserConfigRepo.getInstance().getIsFirstUse() && requestDisplayInfo
+        isPasskeyFirstUse && requestDisplayInfo
           .type == TYPE_PUBLIC_KEY_CREDENTIAL && !isOnPasskeyIntroStateAlready) {
         CreateScreenState.PASSKEY_INTRO
       } else if (
@@ -379,11 +387,11 @@
     private fun toCreationOptionInfoList(
       providerId: String,
       creationEntries: List<Entry>,
-      requestDisplayInfo: RequestDisplayInfo,
       context: Context,
     ): List<CreateOptionInfo> {
       return creationEntries.map {
-        val saveEntryUi = SaveEntryUi.fromSlice(it.slice)
+        // TODO: handle NPE gracefully
+        val createEntry = CreateEntry.fromSlice(it.slice)!!
 
         return@map CreateOptionInfo(
           // TODO: remove fallbacks
@@ -392,13 +400,15 @@
           entrySubkey = it.subkey,
           pendingIntent = it.pendingIntent,
           fillInIntent = it.frameworkExtrasIntent,
-          userProviderDisplayName = saveEntryUi.userProviderAccountName as String,
-          profileIcon = saveEntryUi.profileIcon?.loadDrawable(context)
-            ?: requestDisplayInfo.typeIcon,
-          passwordCount = saveEntryUi.passwordCount ?: 0,
-          passkeyCount = saveEntryUi.passkeyCount ?: 0,
-          totalCredentialCount = saveEntryUi.totalCredentialCount ?: 0,
-          lastUsedTimeMillis = saveEntryUi.lastUsedTimeMillis ?: 0,
+          userProviderDisplayName = createEntry.accountName.toString(),
+          profileIcon = createEntry.icon?.loadDrawable(context),
+          passwordCount = CredentialCountInformation.getPasswordCount(
+                  createEntry.credentialCountInformationList) ?: 0,
+          passkeyCount = CredentialCountInformation.getPasskeyCount(
+                  createEntry.credentialCountInformationList) ?: 0,
+          totalCredentialCount = CredentialCountInformation.getTotalCount(
+                  createEntry.credentialCountInformationList) ?: 0,
+          lastUsedTimeMillis = createEntry.lastUsedTimeMillis ?: 0,
         )
       }
     }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/UserConfigRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/UserConfigRepo.kt
index 5e77663..021dcab 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/UserConfigRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/UserConfigRepo.kt
@@ -32,7 +32,7 @@
         }
     }
 
-    fun setIsFirstUse(
+    fun setIsPasskeyFirstUse(
         isFirstUse: Boolean
     ) {
         sharedPreferences.edit().apply {
@@ -45,7 +45,7 @@
         return sharedPreferences.getString(DEFAULT_PROVIDER, null)
     }
 
-    fun getIsFirstUse(): Boolean {
+    fun getIsPasskeyFirstUse(): Boolean {
         return sharedPreferences.getBoolean(IS_PASSKEY_FIRST_USE, true)
     }
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/CancelButton.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt
similarity index 95%
rename from packages/CredentialManager/src/com/android/credentialmanager/common/ui/CancelButton.kt
rename to packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt
index 80764b5..d0271ab 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/CancelButton.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt
@@ -23,7 +23,7 @@
 import androidx.compose.runtime.Composable
 
 @Composable
-fun CancelButton(text: String, onClick: () -> Unit) {
+fun ActionButton(text: String, onClick: () -> Unit) {
     TextButton(
         onClick = onClick,
         colors = ButtonDefaults.textButtonColors(
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 38e2caa..3d23613 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -14,14 +14,11 @@
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.material3.ButtonDefaults
 import androidx.compose.material3.Divider
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
 import androidx.compose.material3.TopAppBar
 import androidx.compose.material3.TopAppBarDefaults
 import androidx.compose.material.icons.Icons
@@ -43,7 +40,7 @@
 import com.android.credentialmanager.common.material.ModalBottomSheetLayout
 import com.android.credentialmanager.common.material.ModalBottomSheetValue
 import com.android.credentialmanager.common.material.rememberModalBottomSheetState
-import com.android.credentialmanager.common.ui.CancelButton
+import com.android.credentialmanager.common.ui.ActionButton
 import com.android.credentialmanager.common.ui.ConfirmButton
 import com.android.credentialmanager.common.ui.Entry
 import com.android.credentialmanager.common.ui.TextOnSurface
@@ -79,28 +76,30 @@
                         requestDisplayInfo = uiState.requestDisplayInfo,
                         enabledProviderList = uiState.enabledProviders,
                         disabledProviderList = uiState.disabledProviders,
-                        onCancel = viewModel::onCancel,
                         onOptionSelected = viewModel::onEntrySelectedFromFirstUseScreen,
                         onDisabledPasswordManagerSelected =
                         viewModel::onDisabledPasswordManagerSelected,
-                        onRemoteEntrySelected = viewModel::onEntrySelected,
+                        onMoreOptionsSelected = viewModel::onMoreOptionsSelectedOnProviderSelection,
                     )
                     CreateScreenState.CREATION_OPTION_SELECTION -> CreationSelectionCard(
                         requestDisplayInfo = uiState.requestDisplayInfo,
                         enabledProviderList = uiState.enabledProviders,
                         providerInfo = uiState.activeEntry?.activeProvider!!,
                         createOptionInfo = uiState.activeEntry.activeEntryInfo as CreateOptionInfo,
-                        showActiveEntryOnly = uiState.showActiveEntryOnly,
                         onOptionSelected = viewModel::onEntrySelected,
                         onConfirm = viewModel::onConfirmEntrySelected,
-                        onCancel = viewModel::onCancel,
-                        onMoreOptionsSelected = viewModel::onMoreOptionsSelected,
+                        onMoreOptionsSelected = viewModel::onMoreOptionsSelectedOnCreationSelection,
                     )
                     CreateScreenState.MORE_OPTIONS_SELECTION -> MoreOptionsSelectionCard(
                         requestDisplayInfo = uiState.requestDisplayInfo,
                         enabledProviderList = uiState.enabledProviders,
                         disabledProviderList = uiState.disabledProviders,
-                        onBackButtonSelected = viewModel::onBackButtonSelected,
+                        hasDefaultProvider = uiState.hasDefaultProvider,
+                        isFromProviderSelection = uiState.isFromProviderSelection!!,
+                        onBackProviderSelectionButtonSelected =
+                        viewModel::onBackProviderSelectionButtonSelected,
+                        onBackCreationSelectionButtonSelected =
+                        viewModel::onBackCreationSelectionButtonSelected,
                         onOptionSelected = viewModel::onEntrySelectedFromMoreOptionScreen,
                         onDisabledPasswordManagerSelected =
                         viewModel::onDisabledPasswordManagerSelected,
@@ -172,7 +171,7 @@
                 TextSecondary(
                     text = stringResource(R.string.passkey_creation_intro_body_password),
                     style = MaterialTheme.typography.bodyMedium,
-                    modifier = Modifier.padding(start = 16.dp),
+                    modifier = Modifier.padding(start = 16.dp, end = 4.dp),
                 )
             }
             Divider(
@@ -192,7 +191,7 @@
                 TextSecondary(
                     text = stringResource(R.string.passkey_creation_intro_body_fingerprint),
                     style = MaterialTheme.typography.bodyMedium,
-                    modifier = Modifier.padding(start = 16.dp),
+                    modifier = Modifier.padding(start = 16.dp, end = 4.dp),
                 )
             }
             Divider(
@@ -212,7 +211,7 @@
                 TextSecondary(
                     text = stringResource(R.string.passkey_creation_intro_body_device),
                     style = MaterialTheme.typography.bodyMedium,
-                    modifier = Modifier.padding(start = 16.dp),
+                    modifier = Modifier.padding(start = 16.dp, end = 4.dp),
                 )
             }
             Divider(
@@ -223,7 +222,7 @@
                 horizontalArrangement = Arrangement.SpaceBetween,
                 modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
             ) {
-                CancelButton(
+                ActionButton(
                     stringResource(R.string.string_cancel),
                     onClick = onCancel
                 )
@@ -249,8 +248,7 @@
     disabledProviderList: List<DisabledProviderInfo>?,
     onOptionSelected: (ActiveEntry) -> Unit,
     onDisabledPasswordManagerSelected: () -> Unit,
-    onCancel: () -> Unit,
-    onRemoteEntrySelected: (EntryInfo) -> Unit,
+    onMoreOptionsSelected: () -> Unit,
 ) {
     ContainerCard() {
         Column() {
@@ -301,124 +299,7 @@
                         enabledProviderInfo.createOptions.forEach { createOptionInfo ->
                             item {
                                 MoreOptionsInfoRow(
-                                    providerInfo = enabledProviderInfo,
-                                    createOptionInfo = createOptionInfo,
-                                    onOptionSelected = {
-                                        onOptionSelected(
-                                            ActiveEntry(
-                                                enabledProviderInfo,
-                                                createOptionInfo
-                                            )
-                                        )
-                                    })
-                            }
-                        }
-                    }
-                    if (disabledProviderList != null && disabledProviderList.isNotEmpty()) {
-                        item {
-                            MoreOptionsDisabledProvidersRow(
-                                disabledProviders = disabledProviderList,
-                                onDisabledPasswordManagerSelected =
-                                onDisabledPasswordManagerSelected,
-                            )
-                        }
-                    }
-                }
-            }
-            // TODO: handle the error situation that if multiple remoteInfos exists
-            enabledProviderList.forEach { enabledProvider ->
-                if (enabledProvider.remoteEntry != null) {
-                    TextButton(
-                        onClick = {
-                            onRemoteEntrySelected(enabledProvider.remoteEntry!!)
-                        },
-                        modifier = Modifier
-                            .padding(horizontal = 24.dp)
-                            .align(alignment = Alignment.CenterHorizontally),
-                        colors = ButtonDefaults.textButtonColors(
-                            contentColor = MaterialTheme.colorScheme.primary,
-                        )
-                    ) {
-                        Text(
-                            text = stringResource(R.string.string_save_to_another_device),
-                            textAlign = TextAlign.Center,
-                        )
-                    }
-                }
-            }
-            Divider(
-                thickness = 24.dp,
-                color = Color.Transparent
-            )
-            Row(
-                horizontalArrangement = Arrangement.Start,
-                modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
-            ) {
-                CancelButton(stringResource(R.string.string_cancel), onCancel)
-            }
-            Divider(
-                thickness = 18.dp,
-                color = Color.Transparent,
-                modifier = Modifier.padding(bottom = 16.dp)
-            )
-        }
-    }
-}
-
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-fun MoreOptionsSelectionCard(
-    requestDisplayInfo: RequestDisplayInfo,
-    enabledProviderList: List<EnabledProviderInfo>,
-    disabledProviderList: List<DisabledProviderInfo>?,
-    onBackButtonSelected: () -> Unit,
-    onOptionSelected: (ActiveEntry) -> Unit,
-    onDisabledPasswordManagerSelected: () -> Unit,
-    onRemoteEntrySelected: (EntryInfo) -> Unit,
-) {
-    ContainerCard() {
-        Column() {
-            TopAppBar(
-                title = {
-                    TextOnSurface(
-                        text = when (requestDisplayInfo.type) {
-                            TYPE_PUBLIC_KEY_CREDENTIAL ->
-                                stringResource(R.string.create_passkey_in_title)
-                            TYPE_PASSWORD_CREDENTIAL ->
-                                stringResource(R.string.save_password_to_title)
-                            else -> stringResource(R.string.save_sign_in_to_title)
-                        },
-                        style = MaterialTheme.typography.titleMedium,
-                    )
-                },
-                navigationIcon = {
-                    IconButton(onClick = onBackButtonSelected) {
-                        Icon(
-                            Icons.Filled.ArrowBack,
-                            stringResource(R.string.accessibility_back_arrow_button)
-                        )
-                    }
-                },
-                colors = TopAppBarDefaults.smallTopAppBarColors
-                    (containerColor = Color.Transparent),
-            )
-            Divider(
-                thickness = 8.dp,
-                color = Color.Transparent
-            )
-            ContainerCard(
-                shape = MaterialTheme.shapes.medium,
-                modifier = Modifier
-                    .padding(horizontal = 24.dp)
-                    .align(alignment = Alignment.CenterHorizontally)
-            ) {
-                LazyColumn(
-                    verticalArrangement = Arrangement.spacedBy(2.dp)
-                ) {
-                    enabledProviderList.forEach { enabledProviderInfo ->
-                        enabledProviderInfo.createOptions.forEach { createOptionInfo ->
-                            item {
-                                MoreOptionsInfoRow(
+                                    requestDisplayInfo = requestDisplayInfo,
                                     providerInfo = enabledProviderInfo,
                                     createOptionInfo = createOptionInfo,
                                     onOptionSelected = {
@@ -439,6 +320,124 @@
                             onDisabledPasswordManagerSelected,
                         )
                     }
+                }
+            }
+            Divider(
+                thickness = 24.dp,
+                color = Color.Transparent
+            )
+            // TODO: handle the error situation that if multiple remoteInfos exists
+            enabledProviderList.forEach { enabledProvider ->
+                if (enabledProvider.remoteEntry != null) {
+                    Row(
+                        horizontalArrangement = Arrangement.Start,
+                        modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
+                    ) {
+                        ActionButton(
+                            stringResource(R.string.string_more_options),
+                            onMoreOptionsSelected
+                        )
+                    }
+                }
+            }
+            Divider(
+                thickness = 18.dp,
+                color = Color.Transparent,
+                modifier = Modifier.padding(bottom = 16.dp)
+            )
+        }
+    }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun MoreOptionsSelectionCard(
+    requestDisplayInfo: RequestDisplayInfo,
+    enabledProviderList: List<EnabledProviderInfo>,
+    disabledProviderList: List<DisabledProviderInfo>?,
+    hasDefaultProvider: Boolean,
+    isFromProviderSelection: Boolean,
+    onBackProviderSelectionButtonSelected: () -> Unit,
+    onBackCreationSelectionButtonSelected: () -> Unit,
+    onOptionSelected: (ActiveEntry) -> Unit,
+    onDisabledPasswordManagerSelected: () -> Unit,
+    onRemoteEntrySelected: (EntryInfo) -> Unit,
+) {
+    ContainerCard() {
+        Column() {
+            TopAppBar(
+                title = {
+                    TextOnSurface(
+                        text =
+                        stringResource(
+                            R.string.save_credential_to_title,
+                            when (requestDisplayInfo.type) {
+                                TYPE_PUBLIC_KEY_CREDENTIAL ->
+                                    stringResource(R.string.passkey)
+                                TYPE_PASSWORD_CREDENTIAL ->
+                                    stringResource(R.string.password)
+                                else -> stringResource(R.string.sign_in_info)
+                            }),
+                        style = MaterialTheme.typography.titleMedium,
+                    )
+                },
+                navigationIcon = {
+                    IconButton(
+                        onClick =
+                        if (isFromProviderSelection)
+                            onBackProviderSelectionButtonSelected
+                        else onBackCreationSelectionButtonSelected
+                    ) {
+                        Icon(
+                            Icons.Filled.ArrowBack,
+                            stringResource(R.string.accessibility_back_arrow_button)
+                        )
+                    }
+                },
+                colors = TopAppBarDefaults.smallTopAppBarColors
+                    (containerColor = Color.Transparent),
+                modifier = Modifier.padding(top = 12.dp)
+            )
+            Divider(
+                thickness = 8.dp,
+                color = Color.Transparent
+            )
+            ContainerCard(
+                shape = MaterialTheme.shapes.medium,
+                modifier = Modifier
+                    .padding(horizontal = 24.dp)
+                    .align(alignment = Alignment.CenterHorizontally)
+            ) {
+                LazyColumn(
+                    verticalArrangement = Arrangement.spacedBy(2.dp)
+                ) {
+                    if (hasDefaultProvider) {
+                        enabledProviderList.forEach { enabledProviderInfo ->
+                            enabledProviderInfo.createOptions.forEach { createOptionInfo ->
+                                item {
+                                    MoreOptionsInfoRow(
+                                        requestDisplayInfo = requestDisplayInfo,
+                                        providerInfo = enabledProviderInfo,
+                                        createOptionInfo = createOptionInfo,
+                                        onOptionSelected = {
+                                            onOptionSelected(
+                                                ActiveEntry(
+                                                    enabledProviderInfo,
+                                                    createOptionInfo
+                                                )
+                                            )
+                                        })
+                                }
+                            }
+                        }
+                        item {
+                            MoreOptionsDisabledProvidersRow(
+                                disabledProviders = disabledProviderList,
+                                onDisabledPasswordManagerSelected =
+                                onDisabledPasswordManagerSelected,
+                            )
+                        }
+                    }
                     // TODO: handle the error situation that if multiple remoteInfos exists
                     enabledProviderList.forEach {
                         if (it.remoteEntry != null) {
@@ -453,7 +452,7 @@
                 }
             }
             Divider(
-                thickness = 18.dp,
+                thickness = 8.dp,
                 color = Color.Transparent,
                 modifier = Modifier.padding(bottom = 40.dp)
             )
@@ -496,7 +495,7 @@
                 horizontalArrangement = Arrangement.SpaceBetween,
                 modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
             ) {
-                CancelButton(
+                ActionButton(
                     stringResource(R.string.use_once),
                     onClick = onUseOnceSelected
                 )
@@ -521,34 +520,42 @@
     enabledProviderList: List<EnabledProviderInfo>,
     providerInfo: EnabledProviderInfo,
     createOptionInfo: CreateOptionInfo,
-    showActiveEntryOnly: Boolean,
     onOptionSelected: (EntryInfo) -> Unit,
     onConfirm: () -> Unit,
-    onCancel: () -> Unit,
     onMoreOptionsSelected: () -> Unit,
 ) {
     ContainerCard() {
         Column() {
+            Divider(
+                thickness = 24.dp,
+                color = Color.Transparent
+            )
             Icon(
                 bitmap = providerInfo.icon.toBitmap().asImageBitmap(),
                 contentDescription = null,
                 tint = Color.Unspecified,
-                modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
-                    .padding(all = 24.dp).size(32.dp)
+                modifier = Modifier.align(alignment = Alignment.CenterHorizontally).size(32.dp)
+            )
+            TextSecondary(
+                text = providerInfo.displayName,
+                style = MaterialTheme.typography.titleLarge,
+                modifier = Modifier.padding(vertical = 10.dp)
+                    .align(alignment = Alignment.CenterHorizontally),
+                textAlign = TextAlign.Center,
             )
             TextOnSurface(
                 text = when (requestDisplayInfo.type) {
                     TYPE_PUBLIC_KEY_CREDENTIAL -> stringResource(
                         R.string.choose_create_option_passkey_title,
-                        providerInfo.displayName
+                        requestDisplayInfo.appDomainName
                     )
                     TYPE_PASSWORD_CREDENTIAL -> stringResource(
                         R.string.choose_create_option_password_title,
-                        providerInfo.displayName
+                        requestDisplayInfo.appDomainName
                     )
                     else -> stringResource(
                         R.string.choose_create_option_sign_in_title,
-                        providerInfo.displayName
+                        requestDisplayInfo.appDomainName
                     )
                 },
                 style = MaterialTheme.typography.titleMedium,
@@ -556,6 +563,51 @@
                     .align(alignment = Alignment.CenterHorizontally),
                 textAlign = TextAlign.Center,
             )
+            ContainerCard(
+                shape = MaterialTheme.shapes.medium,
+                modifier = Modifier
+                    .padding(all = 24.dp)
+                    .align(alignment = Alignment.CenterHorizontally),
+            ) {
+                PrimaryCreateOptionRow(
+                    requestDisplayInfo = requestDisplayInfo,
+                    entryInfo = createOptionInfo,
+                    onOptionSelected = onOptionSelected
+                )
+            }
+            var shouldShowMoreOptionsButton = false
+            var createOptionsSize = 0
+            var remoteEntry: RemoteInfo? = null
+            enabledProviderList.forEach { enabledProvider ->
+                if (enabledProvider.remoteEntry != null) {
+                    remoteEntry = enabledProvider.remoteEntry
+                }
+                createOptionsSize += enabledProvider.createOptions.size
+            }
+            if (createOptionsSize > 1 || remoteEntry != null) {
+                shouldShowMoreOptionsButton = true
+            }
+            Row(
+                horizontalArrangement =
+                if (shouldShowMoreOptionsButton) Arrangement.SpaceBetween else Arrangement.End,
+                modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
+            ) {
+                if (shouldShowMoreOptionsButton) {
+                    ActionButton(
+                        stringResource(R.string.string_more_options),
+                        onClick = onMoreOptionsSelected
+                    )
+                }
+                ConfirmButton(
+                    stringResource(R.string.string_continue),
+                    onClick = onConfirm
+                )
+            }
+            Divider(
+                thickness = 1.dp,
+                color = Color.LightGray,
+                modifier = Modifier.padding(start = 24.dp, end = 24.dp, top = 18.dp)
+            )
             if (createOptionInfo.userProviderDisplayName != null) {
                 TextSecondary(
                     text = stringResource(
@@ -570,88 +622,8 @@
                         createOptionInfo.userProviderDisplayName
                     ),
                     style = MaterialTheme.typography.bodyLarge,
-                    modifier = Modifier.padding(all = 24.dp)
-                        .align(alignment = Alignment.CenterHorizontally),
-                )
-            }
-            ContainerCard(
-                shape = MaterialTheme.shapes.medium,
-                modifier = Modifier
-                    .padding(horizontal = 24.dp)
-                    .align(alignment = Alignment.CenterHorizontally),
-            ) {
-                PrimaryCreateOptionRow(
-                    requestDisplayInfo = requestDisplayInfo,
-                    entryInfo = createOptionInfo,
-                    onOptionSelected = onOptionSelected
-                )
-            }
-            if (!showActiveEntryOnly) {
-                var createOptionsSize = 0
-                enabledProviderList.forEach { enabledProvider ->
-                    createOptionsSize += enabledProvider.createOptions.size
-                }
-                if (createOptionsSize > 1) {
-                    TextButton(
-                        onClick = onMoreOptionsSelected,
-                        modifier = Modifier
-                            .padding(horizontal = 24.dp)
-                            .align(alignment = Alignment.CenterHorizontally),
-                        colors = ButtonDefaults.textButtonColors(
-                            contentColor = MaterialTheme.colorScheme.primary,
-                        ),
-                    ) {
-                        Text(
-                            text =
-                            when (requestDisplayInfo.type) {
-                                TYPE_PUBLIC_KEY_CREDENTIAL ->
-                                    stringResource(R.string.string_create_in_another_place)
-                                else -> stringResource(R.string.string_save_to_another_place)
-                            },
-                            textAlign = TextAlign.Center,
-                        )
-                    }
-                } else if (
-                    requestDisplayInfo.type == TYPE_PUBLIC_KEY_CREDENTIAL
-                ) {
-                    // TODO: handle the error situation that if multiple remoteInfos exists
-                    enabledProviderList.forEach { enabledProvider ->
-                        if (enabledProvider.remoteEntry != null) {
-                            TextButton(
-                                onClick = {
-                                    onOptionSelected(enabledProvider.remoteEntry!!)
-                                },
-                                modifier = Modifier
-                                    .padding(horizontal = 24.dp)
-                                    .align(alignment = Alignment.CenterHorizontally),
-                                colors = ButtonDefaults.textButtonColors(
-                                    contentColor = MaterialTheme.colorScheme.primary,
-                                ),
-                            ) {
-                                Text(
-                                    text = stringResource(R.string.string_use_another_device),
-                                    textAlign = TextAlign.Center,
-                                )
-                            }
-                        }
-                    }
-                }
-            }
-            Divider(
-                thickness = 24.dp,
-                color = Color.Transparent
-            )
-            Row(
-                horizontalArrangement = Arrangement.SpaceBetween,
-                modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
-            ) {
-                CancelButton(
-                    stringResource(R.string.string_cancel),
-                    onClick = onCancel
-                )
-                ConfirmButton(
-                    stringResource(R.string.string_continue),
-                    onClick = onConfirm
+                    modifier = Modifier.padding(
+                        start = 24.dp, top = 8.dp, bottom = 18.dp, end = 24.dp)
                 )
             }
             Divider(
@@ -712,7 +684,7 @@
                 horizontalArrangement = Arrangement.SpaceBetween,
                 modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
             ) {
-                CancelButton(
+                ActionButton(
                     stringResource(R.string.string_cancel),
                     onClick = onCancel
                 )
@@ -740,16 +712,20 @@
     Entry(
         onClick = { onOptionSelected(entryInfo) },
         icon = {
-            Icon(
-                bitmap = if (entryInfo is CreateOptionInfo) {
-                    entryInfo.profileIcon.toBitmap().asImageBitmap()
-                } else {
-                    requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()
-                },
-                contentDescription = null,
-                tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
-                modifier = Modifier.padding(start = 10.dp).size(32.dp)
-            )
+            if (entryInfo is CreateOptionInfo && entryInfo.profileIcon != null) {
+                Image(
+                    bitmap = entryInfo.profileIcon.toBitmap().asImageBitmap(),
+                    contentDescription = null,
+                    modifier = Modifier.padding(start = 10.dp).size(32.dp),
+                )
+            } else {
+                Icon(
+                    bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap(),
+                    contentDescription = null,
+                    tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
+                    modifier = Modifier.padding(start = 10.dp).size(32.dp),
+                )
+            }
         },
         label = {
             Column() {
@@ -763,9 +739,9 @@
                         )
                         TextSecondary(
                             text = if (requestDisplayInfo.subtitle != null) {
-                                stringResource(
+                                requestDisplayInfo.subtitle + " • " + stringResource(
                                     R.string.passkey_before_subtitle
-                                ) + " - " + requestDisplayInfo.subtitle
+                                )
                             } else {
                                 stringResource(R.string.passkey_before_subtitle)
                             },
@@ -787,11 +763,25 @@
                         )
                     }
                     else -> {
-                        TextOnSurfaceVariant(
-                            text = requestDisplayInfo.title,
-                            style = MaterialTheme.typography.titleLarge,
-                            modifier = Modifier.padding(top = 16.dp, bottom = 16.dp, start = 5.dp),
-                        )
+                        if (requestDisplayInfo.subtitle != null) {
+                            TextOnSurfaceVariant(
+                                text = requestDisplayInfo.title,
+                                style = MaterialTheme.typography.titleLarge,
+                                modifier = Modifier.padding(top = 16.dp, start = 5.dp),
+                            )
+                            TextOnSurfaceVariant(
+                                text = requestDisplayInfo.subtitle,
+                                style = MaterialTheme.typography.bodyMedium,
+                                modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
+                            )
+                        } else {
+                            TextOnSurfaceVariant(
+                                text = requestDisplayInfo.title,
+                                style = MaterialTheme.typography.titleLarge,
+                                modifier = Modifier.padding(
+                                    top = 16.dp, bottom = 16.dp, start = 5.dp),
+                            )
+                        }
                     }
                 }
             }
@@ -802,6 +792,7 @@
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun MoreOptionsInfoRow(
+    requestDisplayInfo: RequestDisplayInfo,
     providerInfo: EnabledProviderInfo,
     createOptionInfo: CreateOptionInfo,
     onOptionSelected: () -> Unit
@@ -826,50 +817,67 @@
                     TextSecondary(
                         text = createOptionInfo.userProviderDisplayName,
                         style = MaterialTheme.typography.bodyMedium,
-                        // TODO: update the logic here for the case there is only total count
-                        modifier = if (
-                            createOptionInfo.passwordCount != null ||
-                            createOptionInfo.passkeyCount != null
-                        ) Modifier.padding(start = 5.dp) else Modifier
-                            .padding(bottom = 16.dp, start = 5.dp),
+                        modifier = Modifier.padding(start = 5.dp),
                     )
                 }
-                if (createOptionInfo.passwordCount != null &&
-                    createOptionInfo.passkeyCount != null
-                ) {
-                    TextSecondary(
-                        text =
-                        stringResource(
-                            R.string.more_options_usage_passwords_passkeys,
-                            createOptionInfo.passwordCount,
-                            createOptionInfo.passkeyCount
-                        ),
-                        style = MaterialTheme.typography.bodyMedium,
-                        modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
-                    )
-                } else if (createOptionInfo.passwordCount != null) {
-                    TextSecondary(
-                        text =
-                        stringResource(
-                            R.string.more_options_usage_passwords,
-                            createOptionInfo.passwordCount
-                        ),
-                        style = MaterialTheme.typography.bodyMedium,
-                        modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
-                    )
-                } else if (createOptionInfo.passkeyCount != null) {
-                    TextSecondary(
-                        text =
-                        stringResource(
-                            R.string.more_options_usage_passkeys,
-                            createOptionInfo.passkeyCount
-                        ),
-                        style = MaterialTheme.typography.bodyMedium,
-                        modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
-                    )
-                } else if (createOptionInfo.totalCredentialCount != null) {
-                    // TODO: Handle the case when there is total count
-                    // but no passwords and passkeys after design is set
+                if (requestDisplayInfo.type == TYPE_PUBLIC_KEY_CREDENTIAL ||
+                    requestDisplayInfo.type == TYPE_PASSWORD_CREDENTIAL) {
+                    if (createOptionInfo.passwordCount != null &&
+                        createOptionInfo.passkeyCount != null
+                    ) {
+                        TextSecondary(
+                            text =
+                            stringResource(
+                                R.string.more_options_usage_passwords_passkeys,
+                                createOptionInfo.passwordCount,
+                                createOptionInfo.passkeyCount
+                            ),
+                            style = MaterialTheme.typography.bodyMedium,
+                            modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
+                        )
+                    } else if (createOptionInfo.passwordCount != null) {
+                        TextSecondary(
+                            text =
+                            stringResource(
+                                R.string.more_options_usage_passwords,
+                                createOptionInfo.passwordCount
+                            ),
+                            style = MaterialTheme.typography.bodyMedium,
+                            modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
+                        )
+                    } else if (createOptionInfo.passkeyCount != null) {
+                        TextSecondary(
+                            text =
+                            stringResource(
+                                R.string.more_options_usage_passkeys,
+                                createOptionInfo.passkeyCount
+                            ),
+                            style = MaterialTheme.typography.bodyMedium,
+                            modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
+                        )
+                    } else {
+                        Divider(
+                            thickness = 16.dp,
+                            color = Color.Transparent,
+                        )
+                    }
+                } else {
+                    if (createOptionInfo.totalCredentialCount != null) {
+                        TextSecondary(
+                            text =
+                            stringResource(
+                                R.string.more_options_usage_credentials,
+                                createOptionInfo.totalCredentialCount
+                            ),
+                            style = MaterialTheme.typography.bodyMedium,
+                            modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
+                        )
+                    } else {
+                        Divider(
+                            thickness = 16.dp,
+                            color = Color.Transparent,
+                        )
+                    }
                 }
             }
         }
@@ -901,7 +909,7 @@
                     )
                     // TODO: Update the subtitle once design is confirmed
                     TextSecondary(
-                        text = disabledProviders.joinToString(separator = ", ") { it.displayName },
+                        text = disabledProviders.joinToString(separator = " • ") { it.displayName },
                         style = MaterialTheme.typography.bodyMedium,
                         modifier = Modifier.padding(bottom = 16.dp, start = 5.dp),
                     )
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
index 9d029dff..7b9e113 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialViewModel.kt
@@ -39,18 +39,39 @@
   val disabledProviders: List<DisabledProviderInfo>? = null,
   val currentScreenState: CreateScreenState,
   val requestDisplayInfo: RequestDisplayInfo,
-  val showActiveEntryOnly: Boolean,
+  // Should not change with the real time update of default provider, only determine whether we're
+  // showing provider selection page at the beginning
+  val hasDefaultProvider: Boolean,
   val activeEntry: ActiveEntry? = null,
   val selectedEntry: EntryInfo? = null,
   val hidden: Boolean = false,
   val providerActivityPending: Boolean = false,
+  val isFromProviderSelection: Boolean? = null,
 )
 
 class CreateCredentialViewModel(
-  credManRepo: CredentialManagerRepo = CredentialManagerRepo.getInstance()
+  credManRepo: CredentialManagerRepo = CredentialManagerRepo.getInstance(),
+  userConfigRepo: UserConfigRepo = UserConfigRepo.getInstance()
 ) : ViewModel() {
 
-  var uiState by mutableStateOf(credManRepo.createCredentialInitialUiState())
+  var providerEnableListUiState = credManRepo.getCreateProviderEnableListInitialUiState()
+
+  var providerDisableListUiState = credManRepo.getCreateProviderDisableListInitialUiState()
+
+  var requestDisplayInfoUiState = credManRepo.getCreateRequestDisplayInfoInitialUiState()
+
+  var defaultProviderId = userConfigRepo.getDefaultProviderId()
+
+  var isPasskeyFirstUse = userConfigRepo.getIsPasskeyFirstUse()
+
+  var uiState by mutableStateOf(
+    CreateFlowUtils.toCreateCredentialUiState(
+      providerEnableListUiState,
+      providerDisableListUiState,
+      defaultProviderId,
+      requestDisplayInfoUiState,
+      false,
+      isPasskeyFirstUse))
     private set
 
   val dialogResult: MutableLiveData<DialogResult> by lazy {
@@ -63,9 +84,9 @@
 
   fun onConfirmIntro() {
     uiState = CreateFlowUtils.toCreateCredentialUiState(
-      uiState.enabledProviders, uiState.disabledProviders,
-      uiState.requestDisplayInfo, true)
-    UserConfigRepo.getInstance().setIsFirstUse(false)
+      providerEnableListUiState, providerDisableListUiState, defaultProviderId,
+      requestDisplayInfoUiState, true, isPasskeyFirstUse)
+    UserConfigRepo.getInstance().setIsPasskeyFirstUse(false)
   }
 
   fun getProviderInfoByName(providerName: String): EnabledProviderInfo {
@@ -74,22 +95,35 @@
     }
   }
 
-  fun onMoreOptionsSelected() {
+  fun onMoreOptionsSelectedOnProviderSelection() {
     uiState = uiState.copy(
       currentScreenState = CreateScreenState.MORE_OPTIONS_SELECTION,
+      isFromProviderSelection = true
     )
   }
 
-  fun onBackButtonSelected() {
+  fun onMoreOptionsSelectedOnCreationSelection() {
     uiState = uiState.copy(
-        currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
+      currentScreenState = CreateScreenState.MORE_OPTIONS_SELECTION,
+      isFromProviderSelection = false
+    )
+  }
+
+  fun onBackProviderSelectionButtonSelected() {
+    uiState = uiState.copy(
+        currentScreenState = CreateScreenState.PROVIDER_SELECTION,
+    )
+  }
+
+  fun onBackCreationSelectionButtonSelected() {
+    uiState = uiState.copy(
+      currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
     )
   }
 
   fun onEntrySelectedFromMoreOptionScreen(activeEntry: ActiveEntry) {
     uiState = uiState.copy(
       currentScreenState = CreateScreenState.MORE_OPTIONS_ROW_INTRO,
-      showActiveEntryOnly = false,
       activeEntry = activeEntry
     )
   }
@@ -97,7 +131,6 @@
   fun onEntrySelectedFromFirstUseScreen(activeEntry: ActiveEntry) {
     uiState = uiState.copy(
       currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
-      showActiveEntryOnly = true,
       activeEntry = activeEntry
     )
     val providerId = uiState.activeEntry?.activeProvider?.name
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
index fda0b97..58db36c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -55,7 +55,7 @@
   pendingIntent: PendingIntent?,
   fillInIntent: Intent?,
   val userProviderDisplayName: String?,
-  val profileIcon: Drawable,
+  val profileIcon: Drawable?,
   val passwordCount: Int?,
   val passkeyCount: Int?,
   val totalCredentialCount: Int?,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 619f5a3..5e7f1e0 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -62,7 +62,7 @@
 import com.android.credentialmanager.common.material.ModalBottomSheetLayout
 import com.android.credentialmanager.common.material.ModalBottomSheetValue
 import com.android.credentialmanager.common.material.rememberModalBottomSheetState
-import com.android.credentialmanager.common.ui.CancelButton
+import com.android.credentialmanager.common.ui.ActionButton
 import com.android.credentialmanager.common.ui.Entry
 import com.android.credentialmanager.common.ui.TextOnSurface
 import com.android.credentialmanager.common.ui.TextSecondary
@@ -96,7 +96,6 @@
                             requestDisplayInfo = uiState.requestDisplayInfo,
                             providerDisplayInfo = uiState.providerDisplayInfo,
                             onEntrySelected = viewModel::onEntrySelected,
-                            onCancel = viewModel::onCancel,
                             onMoreOptionSelected = viewModel::onMoreOptionSelected,
                         )
                     } else {
@@ -135,7 +134,6 @@
     requestDisplayInfo: RequestDisplayInfo,
     providerDisplayInfo: ProviderDisplayInfo,
     onEntrySelected: (EntryInfo) -> Unit,
-    onCancel: () -> Unit,
     onMoreOptionSelected: () -> Unit,
 ) {
     val sortedUserNameToCredentialEntryList =
@@ -181,9 +179,6 @@
                             onEntrySelected = onEntrySelected,
                         )
                     }
-                    item {
-                        SignInAnotherWayRow(onSelect = onMoreOptionSelected)
-                    }
                 }
             }
             Divider(
@@ -194,7 +189,9 @@
                 horizontalArrangement = Arrangement.SpaceBetween,
                 modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
             ) {
-                CancelButton(stringResource(R.string.get_dialog_button_label_no_thanks), onCancel)
+                ActionButton(
+                    stringResource(R.string.get_dialog_use_saved_passkey_for),
+                    onMoreOptionSelected)
             }
             Divider(
                 thickness = 18.dp,
@@ -425,13 +422,22 @@
     Entry(
         onClick = { onEntrySelected(credentialEntryInfo) },
         icon = {
-            Icon(
-                modifier = Modifier.padding(start = 10.dp).size(32.dp),
-                bitmap = credentialEntryInfo.icon.toBitmap().asImageBitmap(),
-                // TODO: add description.
-                contentDescription = "",
-                tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
-            )
+            if (credentialEntryInfo.icon != null) {
+                Image(
+                    modifier = Modifier.padding(start = 10.dp).size(32.dp),
+                    bitmap = credentialEntryInfo.icon.toBitmap().asImageBitmap(),
+                    // TODO: add description.
+                    contentDescription = "",
+                )
+            } else {
+                Icon(
+                    modifier = Modifier.padding(start = 10.dp).size(32.dp),
+                    painter = painterResource(R.drawable.ic_other_sign_in),
+                    // TODO: add description.
+                    contentDescription = "",
+                    tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant
+                )
+            }
         },
         label = {
             Column() {
@@ -544,49 +550,35 @@
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
-fun SignInAnotherWayRow(onSelect: () -> Unit) {
-    Entry(
-        onClick = onSelect,
-        label = {
-            TextOnSurfaceVariant(
-                text = stringResource(R.string.get_dialog_use_saved_passkey_for),
-                style = MaterialTheme.typography.titleLarge,
-                modifier = Modifier.padding(vertical = 16.dp)
-            )
-        }
-    )
-}
-
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
 fun SnackBarScreen(
     onClick: (Boolean) -> Unit,
     onCancel: () -> Unit,
 ) {
     // TODO: Change the height, width and position according to the design
-    Snackbar (
-        modifier = Modifier.padding(horizontal = 80.dp).padding(top = 700.dp),
+    Snackbar(
+        modifier = Modifier.padding(horizontal = 40.dp).padding(top = 700.dp),
         shape = EntryShape.FullMediumRoundedCorner,
         containerColor = LocalAndroidColorScheme.current.colorBackground,
         contentColor = LocalAndroidColorScheme.current.colorAccentPrimaryVariant,
-    ) {
-        Row(
-            horizontalArrangement = Arrangement.SpaceBetween,
-            verticalAlignment = Alignment.CenterVertically,
-        ) {
+        action = {
             TextButton(
-                onClick = {onClick(true)},
+                onClick = { onClick(true) },
             ) {
-                Text(text = stringResource(R.string.get_dialog_use_saved_passkey_for))
+                Text(text = stringResource(R.string.snackbar_action))
             }
+        },
+        dismissAction = {
             IconButton(onClick = onCancel) {
                 Icon(
                     Icons.Filled.Close,
                     contentDescription = stringResource(
                         R.string.accessibility_close_button
-                    )
+                    ),
+                    tint = LocalAndroidColorScheme.current.colorAccentTertiary
                 )
             }
-        }
+        },
+    ) {
+        Text(text = stringResource(R.string.get_dialog_use_saved_passkey_for))
     }
 }
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
index 3a2a738..60939b5 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -67,7 +67,7 @@
   val credentialTypeDisplayName: String,
   val userName: String,
   val displayName: String?,
-  val icon: Drawable,
+  val icon: Drawable?,
   val lastUsedTimeMillis: Long?,
 ) : EntryInfo(providerId, entryKey, entrySubkey, pendingIntent, fillInIntent)
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PasswordCredential.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PasswordCredential.kt
new file mode 100644
index 0000000..1658858
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/developer/PasswordCredential.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.jetpack.developer
+
+import android.os.Bundle
+
+class PasswordCredential constructor(
+        val id: String,
+        val password: String,
+) : Credential(android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL, toBundle(id, password)) {
+
+    init {
+        require(password.isNotEmpty()) { "password should not be empty" }
+    }
+
+    /** @hide */
+    companion object {
+
+        const val BUNDLE_KEY_ID = "androidx.credentials.BUNDLE_KEY_ID"
+        const val BUNDLE_KEY_PASSWORD = "androidx.credentials.BUNDLE_KEY_PASSWORD"
+
+        @JvmStatic
+        internal fun toBundle(id: String, password: String): Bundle {
+            val bundle = Bundle()
+            bundle.putString(BUNDLE_KEY_ID, id)
+            bundle.putString(BUNDLE_KEY_PASSWORD, password)
+            return bundle
+        }
+
+        @JvmStatic
+        internal fun createFrom(data: Bundle): PasswordCredential {
+            try {
+                val id = data.getString(BUNDLE_KEY_ID)
+                val password = data.getString(BUNDLE_KEY_PASSWORD)
+                return PasswordCredential(id!!, password!!)
+            } catch (e: Exception) {
+                throw FrameworkClassParsingException()
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/Action.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/Action.kt
new file mode 100644
index 0000000..1abf911
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/Action.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.jetpack.provider
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.app.slice.Slice
+import android.app.slice.SliceSpec
+import android.net.Uri
+import android.util.Log
+import java.util.Collections
+
+/**
+ * UI representation for a credential entry used during the get credential flow.
+ *
+ * TODO: move to jetpack.
+ */
+class Action constructor(
+        val title: CharSequence,
+        val subTitle: CharSequence?,
+        val pendingIntent: PendingIntent?,
+) {
+
+  init {
+    require(title.isNotEmpty()) { "title must not be empty" }
+  }
+
+  companion object {
+    private const val TAG = "Action"
+    internal const val SLICE_HINT_TITLE =
+            "androidx.credentials.provider.action.HINT_ACTION_TITLE"
+    internal const val SLICE_HINT_SUBTITLE =
+            "androidx.credentials.provider.action.HINT_ACTION_SUBTEXT"
+    internal const val SLICE_HINT_PENDING_INTENT =
+            "androidx.credentials.provider.action.SLICE_HINT_PENDING_INTENT"
+
+    @JvmStatic
+    fun toSlice(action: Action): Slice {
+      // TODO("Put the right spec and version value")
+      val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("type", 1))
+              .addText(action.title, /*subType=*/null,
+                      listOf(SLICE_HINT_TITLE))
+              .addText(action.subTitle, /*subType=*/null,
+                      listOf(SLICE_HINT_SUBTITLE))
+      if (action.pendingIntent != null) {
+        sliceBuilder.addAction(action.pendingIntent,
+                Slice.Builder(sliceBuilder)
+                        .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))
+                        .build(),
+                /*subType=*/null)
+      }
+      return sliceBuilder.build()
+    }
+
+    /**
+     * Returns an instance of [Action] derived from a [Slice] object.
+     *
+     * @param slice the [Slice] object constructed through [toSlice]
+     */
+    @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
+    @JvmStatic
+    fun fromSlice(slice: Slice): Action? {
+      // TODO("Put the right spec and version value")
+      var title: CharSequence = ""
+      var subTitle: CharSequence? = null
+      var pendingIntent: PendingIntent? = null
+
+      slice.items.forEach {
+        if (it.hasHint(SLICE_HINT_TITLE)) {
+          title = it.text
+        } else if (it.hasHint(SLICE_HINT_SUBTITLE)) {
+          subTitle = it.text
+        } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
+          pendingIntent = it.action
+        }
+      }
+
+      return try {
+        Action(title, subTitle, pendingIntent)
+      } catch (e: Exception) {
+        Log.i(TAG, "fromSlice failed with: " + e.message)
+        null
+      }
+    }
+  }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/ActionUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/ActionUi.kt
deleted file mode 100644
index 19c5c2d..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/ActionUi.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.jetpack.provider
-
-import android.app.slice.Slice
-import android.credentials.ui.Entry
-
-/**
- * UI representation for a credential entry used during the get credential flow.
- *
- * TODO: move to jetpack.
- */
-class ActionUi(
-  val text: CharSequence,
-  val subtext: CharSequence?,
-) {
-  companion object {
-    fun fromSlice(slice: Slice): ActionUi {
-      var text: CharSequence? = null
-      var subtext: CharSequence? = null
-
-      val items = slice.items
-      items.forEach {
-        if (it.hasHint(Entry.HINT_ACTION_TITLE)) {
-          text = it.text
-        } else if (it.hasHint(Entry.HINT_ACTION_SUBTEXT)) {
-          subtext = it.text
-        }
-      }
-      // TODO: fail NPE more elegantly.
-      return ActionUi(text!!, subtext)
-    }
-  }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CreateEntry.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CreateEntry.kt
new file mode 100644
index 0000000..bed02f8
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CreateEntry.kt
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.jetpack.provider
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.app.slice.Slice
+import android.app.slice.SliceSpec
+import android.graphics.drawable.Icon
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+import java.util.Collections
+
+/**
+ * UI representation for a save entry used during the create credential flow.
+ *
+ * TODO: move to jetpack.
+ */
+class CreateEntry internal constructor(
+        val accountName: CharSequence,
+        val pendingIntent: PendingIntent?,
+        val icon: Icon?,
+        val lastUsedTimeMillis: Long,
+        val credentialCountInformationList: List<CredentialCountInformation>
+) {
+
+  init {
+    require(accountName.isNotEmpty()) { "accountName must not be empty" }
+  }
+
+  /**
+   * A builder for [CreateEntry]
+   *
+   * @property accountName the name of the account where the credential will be registered
+   * @property pendingIntent the [PendingIntent] that will be fired when the user selects
+   * this entry
+   *
+   * @hide
+   */
+  class Builder constructor(
+          private val accountName: CharSequence,
+          private val pendingIntent: PendingIntent? = null
+  ) {
+
+    private var credentialCountInformationList: MutableList<CredentialCountInformation> =
+            mutableListOf()
+    private var icon: Icon? = null
+    private var lastUsedTimeMillis: Long = 0
+
+    /** Adds a [CredentialCountInformation] denoting a given credential
+     * type and the count of credentials that the provider has stored for that
+     * credential type.
+     *
+     * This information will be displayed on the [CreateEntry] to help the user
+     * make a choice.
+     */
+    @Suppress("MissingGetterMatchingBuilder")
+    fun addCredentialCountInformation(info: CredentialCountInformation): Builder {
+      credentialCountInformationList.add(info)
+      return this
+    }
+
+    /** Sets a list of [CredentialCountInformation]. Each item in the list denotes a given
+     * credential type and the count of credentials that the provider has stored of that
+     * credential type.
+     *
+     * This information will be displayed on the [CreateEntry] to help the user
+     * make a choice.
+     */
+    fun setCredentialCountInformationList(infoList: List<CredentialCountInformation>): Builder {
+      credentialCountInformationList = infoList as MutableList<CredentialCountInformation>
+      return this
+    }
+
+    /** Sets an icon to be displayed with the entry on the UI */
+    fun setIcon(icon: Icon?): Builder {
+      this.icon = icon
+      return this
+    }
+
+    /** Sets the last time this account was used */
+    fun setLastUsedTimeMillis(lastUsedTimeMillis: Long): Builder {
+      this.lastUsedTimeMillis = lastUsedTimeMillis
+      return this
+    }
+
+    /**
+     * Builds an instance of [CreateEntry]
+     *
+     * @throws IllegalArgumentException If [accountName] is empty
+     */
+    fun build(): CreateEntry {
+      return CreateEntry(accountName, pendingIntent, icon, lastUsedTimeMillis,
+              credentialCountInformationList)
+    }
+  }
+
+  companion object {
+    private const val TAG = "CreateEntry"
+    internal const val SLICE_HINT_ACCOUNT_NAME =
+            "androidx.credentials.provider.createEntry.SLICE_HINT_USER_PROVIDER_ACCOUNT_NAME"
+    internal const val SLICE_HINT_ICON =
+            "androidx.credentials.provider.createEntry.SLICE_HINT_PROFILE_ICON"
+    internal const val SLICE_HINT_CREDENTIAL_COUNT_INFORMATION =
+            "androidx.credentials.provider.createEntry.SLICE_HINT_CREDENTIAL_COUNT_INFORMATION"
+    internal const val SLICE_HINT_LAST_USED_TIME_MILLIS =
+            "androidx.credentials.provider.createEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
+    internal const val SLICE_HINT_PENDING_INTENT =
+            "androidx.credentials.provider.createEntry.SLICE_HINT_PENDING_INTENT"
+
+    @JvmStatic
+    fun toSlice(createEntry: CreateEntry): Slice {
+      // TODO("Use the right type and revision")
+      val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec("type", 1))
+      sliceBuilder.addText(createEntry.accountName, /*subType=*/null,
+              listOf(SLICE_HINT_ACCOUNT_NAME))
+              .addLong(createEntry.lastUsedTimeMillis, /*subType=*/null, listOf(
+                      SLICE_HINT_LAST_USED_TIME_MILLIS))
+      if (createEntry.icon != null) {
+        sliceBuilder.addIcon(createEntry.icon, /*subType=*/null,
+                listOf(SLICE_HINT_ICON))
+      }
+
+      val credentialCountBundle = convertCredentialCountInfoToBundle(
+              createEntry.credentialCountInformationList)
+      if (credentialCountBundle != null) {
+        sliceBuilder.addBundle(convertCredentialCountInfoToBundle(
+                createEntry.credentialCountInformationList), null, listOf(
+                SLICE_HINT_CREDENTIAL_COUNT_INFORMATION))
+      }
+      if (createEntry.pendingIntent != null) {
+        sliceBuilder.addAction(createEntry.pendingIntent,
+                Slice.Builder(sliceBuilder)
+                        .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))
+                        .build(),
+                /*subType=*/null)
+      }
+      return sliceBuilder.build()
+    }
+
+    /**
+     * Returns an instance of [CreateEntry] derived from a [Slice] object.
+     *
+     * @param slice the [Slice] object constructed through [toSlice]
+     */
+    @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
+    @JvmStatic
+    fun fromSlice(slice: Slice): CreateEntry? {
+      // TODO("Put the right spec and version value")
+      var accountName: CharSequence = ""
+      var icon: Icon? = null
+      var pendingIntent: PendingIntent? = null
+      var credentialCountInfo: List<CredentialCountInformation> = listOf()
+      var lastUsedTimeMillis: Long = 0
+
+      slice.items.forEach {
+        if (it.hasHint(SLICE_HINT_ACCOUNT_NAME)) {
+          accountName = it.text
+        } else if (it.hasHint(SLICE_HINT_ICON)) {
+          icon = it.icon
+        } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
+          pendingIntent = it.action
+        } else if (it.hasHint(SLICE_HINT_CREDENTIAL_COUNT_INFORMATION)) {
+          credentialCountInfo = convertBundleToCredentialCountInfo(it.bundle)
+        } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {
+          lastUsedTimeMillis = it.long
+        }
+      }
+
+      return try {
+        CreateEntry(accountName, pendingIntent, icon,
+                lastUsedTimeMillis, credentialCountInfo)
+      } catch (e: Exception) {
+        Log.i(TAG, "fromSlice failed with: " + e.message)
+        null
+      }
+    }
+
+    @JvmStatic
+    internal fun convertBundleToCredentialCountInfo(bundle: Bundle?):
+            List<CredentialCountInformation> {
+      val credentialCountList = ArrayList<CredentialCountInformation>()
+      if (bundle == null) {
+        return credentialCountList
+      }
+      bundle.keySet().forEach {
+        try {
+          credentialCountList.add(
+                  CredentialCountInformation(it, bundle.getInt(it)))
+        } catch (e: Exception) {
+          Log.i(TAG, "Issue unpacking credential count info bundle: " + e.message)
+        }
+      }
+      return credentialCountList
+    }
+
+    @JvmStatic
+    internal fun convertCredentialCountInfoToBundle(
+            credentialCountInformationList: List<CredentialCountInformation>
+    ): Bundle? {
+      if (credentialCountInformationList.isEmpty()) {
+        return null
+      }
+      val bundle = Bundle()
+      credentialCountInformationList.forEach {
+        bundle.putInt(it.type, it.count)
+      }
+      return bundle
+    }
+  }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialCountInformation.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialCountInformation.kt
new file mode 100644
index 0000000..aa77b74
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialCountInformation.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.jetpack.provider
+
+import android.credentials.Credential
+import com.android.credentialmanager.jetpack.developer.PublicKeyCredential
+
+class CredentialCountInformation constructor(
+        val type: String,
+        val count: Int
+) {
+    companion object {
+        @JvmStatic
+        fun createPasswordCountInformation(count: Int): CredentialCountInformation {
+            return CredentialCountInformation(Credential.TYPE_PASSWORD_CREDENTIAL, count)
+        }
+
+        @JvmStatic
+        fun getPasswordCount(infos: List<CredentialCountInformation>): Int? {
+            return getCountForType(infos, Credential.TYPE_PASSWORD_CREDENTIAL)
+        }
+
+        @JvmStatic
+        fun createPublicKeyCountInformation(count: Int): CredentialCountInformation {
+            return CredentialCountInformation(PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL, count)
+        }
+
+        @JvmStatic
+        fun getPasskeyCount(infos: List<CredentialCountInformation>): Int? {
+            return getCountForType(infos, PublicKeyCredential.TYPE_PUBLIC_KEY_CREDENTIAL)
+        }
+
+        @JvmStatic
+        fun createTotalCountInformation(count: Int): CredentialCountInformation {
+            return CredentialCountInformation("TOTAL_COUNT", count)
+        }
+
+        @JvmStatic
+        fun getTotalCount(infos: List<CredentialCountInformation>): Int? {
+            return getCountForType(infos, "TOTAL_COUNT")
+        }
+
+        private fun getCountForType(infos: List<CredentialCountInformation>, type: String): Int? {
+            return infos.firstOrNull { info -> info.type == type }?.count
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntry.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntry.kt
new file mode 100644
index 0000000..61a104b
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntry.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.credentialmanager.jetpack.provider
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.app.slice.Slice
+import android.app.slice.SliceSpec
+import android.graphics.drawable.Icon
+import android.net.Uri
+import android.util.Log
+import java.util.Collections
+
+/**
+ * UI representation for a credential entry used during the get credential flow.
+ *
+ * TODO: move to jetpack.
+ */
+open class CredentialEntry constructor(
+        // TODO("Add credential type display name for both CredentialEntry & CreateEntry")
+        val type: String,
+        val typeDisplayName: CharSequence,
+        val username: CharSequence,
+        val displayName: CharSequence?,
+        val pendingIntent: PendingIntent?,
+        // TODO("Consider using Instant or other strongly typed time data type")
+        val lastUsedTimeMillis: Long,
+        val icon: Icon?,
+        var autoSelectAllowed: Boolean
+) {
+  init {
+    require(type.isNotEmpty()) { "type must not be empty" }
+    require(username.isNotEmpty()) { "type must not be empty" }
+  }
+
+  companion object {
+    private const val TAG = "CredentialEntry"
+    internal const val SLICE_HINT_TYPE_DISPLAY_NAME =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
+    internal const val SLICE_HINT_USERNAME =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_USER_NAME"
+    internal const val SLICE_HINT_DISPLAYNAME =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_CREDENTIAL_TYPE_DISPLAY_NAME"
+    internal const val SLICE_HINT_LAST_USED_TIME_MILLIS =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
+    internal const val SLICE_HINT_ICON =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_PROFILE_ICON"
+    internal const val SLICE_HINT_PENDING_INTENT =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_PENDING_INTENT"
+    internal const val SLICE_HINT_AUTO_ALLOWED =
+            "androidx.credentials.provider.credentialEntry.SLICE_HINT_AUTO_ALLOWED"
+    internal const val AUTO_SELECT_TRUE_STRING = "true"
+    internal const val AUTO_SELECT_FALSE_STRING = "false"
+
+    @JvmStatic
+    internal fun toSlice(credentialEntry: CredentialEntry): Slice {
+      // TODO("Put the right revision value")
+      val autoSelectAllowed = if (credentialEntry.autoSelectAllowed) {
+        AUTO_SELECT_TRUE_STRING
+      } else {
+        AUTO_SELECT_FALSE_STRING
+      }
+      val sliceBuilder = Slice.Builder(Uri.EMPTY, SliceSpec(
+              credentialEntry.type, 1))
+              .addText(credentialEntry.typeDisplayName, /*subType=*/null,
+                      listOf(SLICE_HINT_TYPE_DISPLAY_NAME))
+              .addText(credentialEntry.username, /*subType=*/null,
+                      listOf(SLICE_HINT_USERNAME))
+              .addText(credentialEntry.displayName, /*subType=*/null,
+                      listOf(SLICE_HINT_DISPLAYNAME))
+              .addLong(credentialEntry.lastUsedTimeMillis, /*subType=*/null,
+                      listOf(SLICE_HINT_LAST_USED_TIME_MILLIS))
+              .addText(autoSelectAllowed, /*subType=*/null,
+                      listOf(SLICE_HINT_AUTO_ALLOWED))
+      if (credentialEntry.icon != null) {
+        sliceBuilder.addIcon(credentialEntry.icon, /*subType=*/null,
+                listOf(SLICE_HINT_ICON))
+      }
+      if (credentialEntry.pendingIntent != null) {
+        sliceBuilder.addAction(credentialEntry.pendingIntent,
+                Slice.Builder(sliceBuilder)
+                        .addHints(Collections.singletonList(SLICE_HINT_PENDING_INTENT))
+                        .build(),
+                /*subType=*/null)
+      }
+      return sliceBuilder.build()
+    }
+
+    /**
+     * Returns an instance of [CredentialEntry] derived from a [Slice] object.
+     *
+     * @param slice the [Slice] object constructed through [toSlice]
+     */
+    @SuppressLint("WrongConstant") // custom conversion between jetpack and framework
+    @JvmStatic
+    fun fromSlice(slice: Slice): CredentialEntry? {
+      var typeDisplayName: CharSequence? = null
+      var username: CharSequence? = null
+      var displayName: CharSequence? = null
+      var icon: Icon? = null
+      var pendingIntent: PendingIntent? = null
+      var lastUsedTimeMillis: Long = 0
+      var autoSelectAllowed = false
+
+      slice.items.forEach {
+        if (it.hasHint(SLICE_HINT_TYPE_DISPLAY_NAME)) {
+          typeDisplayName = it.text
+        } else if (it.hasHint(SLICE_HINT_USERNAME)) {
+          username = it.text
+        } else if (it.hasHint(SLICE_HINT_DISPLAYNAME)) {
+          displayName = it.text
+        } else if (it.hasHint(SLICE_HINT_ICON)) {
+          icon = it.icon
+        } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
+          pendingIntent = it.action
+        } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {
+          lastUsedTimeMillis = it.long
+        } else if (it.hasHint(SLICE_HINT_AUTO_ALLOWED)) {
+          val autoSelectValue = it.text
+          if (autoSelectValue == AUTO_SELECT_TRUE_STRING) {
+            autoSelectAllowed = true
+          }
+        }
+      }
+
+      return try {
+        CredentialEntry(slice.spec!!.type, typeDisplayName!!, username!!,
+                displayName, pendingIntent,
+                lastUsedTimeMillis, icon, autoSelectAllowed)
+      } catch (e: Exception) {
+        Log.i(TAG, "fromSlice failed with: " + e.message)
+        null
+      }
+    }
+  }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntryUi.kt
deleted file mode 100644
index 47b5af0..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/CredentialEntryUi.kt
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.jetpack.provider
-
-import android.app.PendingIntent
-import android.app.slice.Slice
-import android.graphics.drawable.Icon
-
-/**
- * UI representation for a credential entry used during the get credential flow.
- *
- * TODO: move to jetpack.
- */
-class CredentialEntryUi(
-  val credentialType: CharSequence,
-  val credentialTypeDisplayName: CharSequence,
-  val userName: CharSequence,
-  val userDisplayName: CharSequence?,
-  val entryIcon: Icon?,
-  val lastUsedTimeMillis: Long?,
-  // TODO: Remove note
-  val note: CharSequence?,
-) {
-  companion object {
-    // Copied over from jetpack
-    const val SLICE_HINT_TYPE_DISPLAY_NAME =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
-    const val SLICE_HINT_USERNAME =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_USER_NAME"
-    const val SLICE_HINT_DISPLAYNAME =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_CREDENTIAL_TYPE_DISPLAY_NAME"
-    const val SLICE_HINT_LAST_USED_TIME_MILLIS =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
-    const val SLICE_HINT_ICON =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_PROFILE_ICON"
-    const val SLICE_HINT_PENDING_INTENT =
-            "androidx.credentials.provider.credentialEntry.SLICE_HINT_PENDING_INTENT"
-
-    /**
-     * Returns an instance of [CredentialEntryUi] derived from a [Slice] object.
-     *
-     * @param slice the [Slice] object constructed through jetpack library
-     */
-    @JvmStatic
-    fun fromSlice(slice: Slice): CredentialEntryUi {
-      var username: CharSequence? = null
-      var displayName: CharSequence = ""
-      var icon: Icon? = null
-      var pendingIntent: PendingIntent? = null
-      var lastUsedTimeMillis: Long = 0
-      var note: CharSequence? = null
-      var typeDisplayName: CharSequence = ""
-
-      slice.items.forEach {
-        if (it.hasHint(SLICE_HINT_TYPE_DISPLAY_NAME)) {
-          typeDisplayName = it.text
-        } else if (it.hasHint(SLICE_HINT_USERNAME)) {
-          username = it.text
-        } else if (it.hasHint(SLICE_HINT_DISPLAYNAME)) {
-          displayName = it.text
-        } else if (it.hasHint(SLICE_HINT_ICON)) {
-          icon = it.icon
-        } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
-          pendingIntent = it.action
-        } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {
-          lastUsedTimeMillis = it.long
-        }
-      }
-      return CredentialEntryUi(
-              slice.spec!!.type, typeDisplayName, username!!, displayName, icon,
-              lastUsedTimeMillis, note,
-      )
-    }
-  }
-}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/SaveEntryUi.kt b/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/SaveEntryUi.kt
deleted file mode 100644
index 313f0f9..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/jetpack/provider/SaveEntryUi.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.jetpack.provider
-
-import android.app.PendingIntent
-import android.app.slice.Slice
-import android.graphics.drawable.Icon
-
-/**
- * UI representation for a save entry used during the create credential flow.
- *
- * TODO: move to jetpack.
- */
-class SaveEntryUi(
-  val userProviderAccountName: CharSequence?,
-  val credentialTypeIcon: Icon?,
-  val profileIcon: Icon?,
-  val passwordCount: Int?,
-  val passkeyCount: Int?,
-  val totalCredentialCount: Int?,
-  val lastUsedTimeMillis: Long?,
-) {
-  companion object {
-    const val SLICE_HINT_ACCOUNT_NAME =
-            "androidx.credentials.provider.createEntry.SLICE_HINT_USER_PROVIDER_ACCOUNT_NAME"
-    const val SLICE_HINT_ICON =
-            "androidx.credentials.provider.createEntry.SLICE_HINT_PROFILE_ICON"
-    const val SLICE_HINT_CREDENTIAL_COUNT_INFORMATION =
-            "androidx.credentials.provider.createEntry.SLICE_HINT_CREDENTIAL_COUNT_INFORMATION"
-    const val SLICE_HINT_LAST_USED_TIME_MILLIS =
-            "androidx.credentials.provider.createEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
-    const val SLICE_HINT_PENDING_INTENT =
-            "androidx.credentials.provider.createEntry.SLICE_HINT_PENDING_INTENT"
-
-    /**
-     * Returns an instance of [SaveEntryUi] derived from a [Slice] object.
-     *
-     * @param slice the [Slice] object constructed through the jetpack library
-     */
-    @JvmStatic
-    fun fromSlice(slice: Slice): SaveEntryUi {
-      var accountName: CharSequence? = null
-      var icon: Icon? = null
-      var pendingIntent: PendingIntent? = null
-      var lastUsedTimeMillis: Long = 0
-
-      slice.items.forEach {
-        if (it.hasHint(SLICE_HINT_ACCOUNT_NAME)) {
-          accountName = it.text
-        } else if (it.hasHint(SLICE_HINT_ICON)) {
-          icon = it.icon
-        } else if (it.hasHint(SLICE_HINT_PENDING_INTENT)) {
-          pendingIntent = it.action
-        } else if (it.hasHint(SLICE_HINT_LAST_USED_TIME_MILLIS)) {
-          lastUsedTimeMillis = it.long
-        }
-      }
-
-      return SaveEntryUi(
-              // TODO: Add count parsing
-              accountName!!, icon, icon,
-              0, 0, 0, lastUsedTimeMillis,
-      )
-    }
-  }
-}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreference.kt
new file mode 100644
index 0000000..b8db63c
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreference.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.widget.preference
+
+import com.android.settingslib.spa.framework.util.EntryHighlight
+import androidx.compose.material3.IconButton
+import androidx.compose.runtime.State
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.material3.Icon
+
+@Composable
+fun TwoTargetButtonPreference(
+        title: String,
+        summary: State<String>,
+        icon: @Composable (() -> Unit)? = null,
+        onClick: () -> Unit,
+        buttonIcon: ImageVector,
+        buttonIconDescription: String,
+        onButtonClick: () -> Unit
+) {
+    EntryHighlight {
+        TwoTargetPreference(
+                title = title,
+                summary = summary,
+                onClick = onClick,
+                icon = icon) {
+            IconButton(onClick = onButtonClick) {
+                Icon(imageVector = buttonIcon, contentDescription = buttonIconDescription)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreferenceTest.kt
new file mode 100644
index 0000000..3a2b445
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TwoTargetButtonPreferenceTest.kt
@@ -0,0 +1,73 @@
+package com.android.settingslib.spa.widget.preference
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Delete
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithContentDescription
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.framework.compose.toState
+import com.google.common.truth.Truth
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val TEST_MODEL_TITLE = "TwoTargetButtonPreference"
+private const val TEST_MODEL_SUMMARY = "TestSummary"
+private const val TEST_BUTTON_ICON_DESCRIPTION = "TestButtonIconDescription"
+private val TEST_BUTTON_ICON = Icons.Outlined.Delete
+
+@RunWith(AndroidJUnit4::class)
+class TwoTargetButtonPreferenceTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @Test
+    fun title_displayed() {
+        composeTestRule.setContent {
+            testTwoTargetButtonPreference()
+        }
+
+        composeTestRule.onNodeWithText(TEST_MODEL_TITLE).assertIsDisplayed()
+    }
+
+    @Test
+    fun clickable_label_canBeClicked() {
+        var clicked = false
+        composeTestRule.setContent {
+            testTwoTargetButtonPreference(onClick = { clicked = true })
+        }
+
+        composeTestRule.onNodeWithText(TEST_MODEL_TITLE).performClick()
+        Truth.assertThat(clicked).isTrue()
+    }
+
+    @Test
+    fun clickable_button_label_canBeClicked() {
+        var clicked = false
+        composeTestRule.setContent {
+            testTwoTargetButtonPreference(onButtonClick = { clicked = true })
+        }
+
+        composeTestRule.onNodeWithContentDescription(TEST_BUTTON_ICON_DESCRIPTION).performClick()
+        Truth.assertThat(clicked).isTrue()
+    }
+}
+
+@Composable
+private fun testTwoTargetButtonPreference(
+    onClick: () -> Unit = {},
+    onButtonClick: () -> Unit = {},
+) {
+    TwoTargetButtonPreference(
+        title = TEST_MODEL_TITLE,
+        summary = TEST_MODEL_SUMMARY.toState(),
+        onClick = onClick,
+        buttonIcon = TEST_BUTTON_ICON,
+        buttonIconDescription = TEST_BUTTON_ICON_DESCRIPTION,
+        onButtonClick = onButtonClick
+    )
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
index deec267..3ff1d89 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
@@ -60,6 +60,7 @@
     val listModel: AppListModel<T>,
     val state: AppListState,
     val header: @Composable () -> Unit,
+    val noItemMessage: String? = null,
     val bottomPadding: Dp,
 )
 
@@ -79,7 +80,7 @@
 ) {
     LogCompositions(TAG, config.userId.toString())
     val appListData = appListDataSupplier()
-    listModel.AppListWidget(appListData, header, bottomPadding)
+    listModel.AppListWidget(appListData, header, bottomPadding, noItemMessage)
 }
 
 @Composable
@@ -87,12 +88,14 @@
     appListData: State<AppListData<T>?>,
     header: @Composable () -> Unit,
     bottomPadding: Dp,
+    noItemMessage: String?
 ) {
     val timeMeasurer = rememberTimeMeasurer(TAG)
     appListData.value?.let { (list, option) ->
         timeMeasurer.logFirst("app list first loaded")
         if (list.isEmpty()) {
-            PlaceholderTitle(stringResource(R.string.no_applications))
+            header()
+            PlaceholderTitle(noItemMessage ?: stringResource(R.string.no_applications))
             return
         }
         LazyColumn(
@@ -151,4 +154,4 @@
     ) { viewModel.reloadApps() }
 
     return viewModel.appListDataFlow.collectAsState(null, Dispatchers.IO)
-}
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListButtonItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListButtonItem.kt
new file mode 100644
index 0000000..919793a
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListButtonItem.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.template.app
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.vector.ImageVector
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+import com.android.settingslib.spa.widget.preference.TwoTargetButtonPreference
+
+@Composable
+fun <T : AppRecord> AppListItemModel<T>.AppListButtonItem (
+    onClick: () -> Unit,
+    onButtonClick: () -> Unit,
+    buttonIcon: ImageVector,
+    buttonIconDescription: String,
+) {
+        TwoTargetButtonPreference(
+                title = label,
+                summary = [email protected],
+                icon = { AppIcon(record.app, SettingsDimension.appIconItemSize) },
+                onClick = onClick,
+                buttonIcon = buttonIcon,
+                buttonIconDescription = buttonIconDescription,
+                onButtonClick = onButtonClick
+        )
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
index 318bcd9..7d21d98 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
@@ -45,6 +45,7 @@
     listModel: AppListModel<T>,
     showInstantApps: Boolean = false,
     primaryUserOnly: Boolean = false,
+    noItemMessage: String? = null,
     moreOptions: @Composable MoreOptionsScope.() -> Unit = {},
     header: @Composable () -> Unit = {},
     appList: @Composable AppListInput<T>.() -> Unit = { AppList() },
@@ -77,6 +78,7 @@
                     ),
                     header = header,
                     bottomPadding = bottomPadding,
+                    noItemMessage = noItemMessage,
                 )
                 appList(appListInput)
             }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
index 4da47fd..db224be 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
@@ -66,6 +66,8 @@
             "com.android.settings.category.ia.battery_saver_settings";
     public static final String CATEGORY_SMART_BATTERY_SETTINGS =
             "com.android.settings.category.ia.smart_battery_settings";
+    public static final String CATEGORY_COMMUNAL_SETTINGS =
+            "com.android.settings.category.ia.communal";
 
     public static final Map<String, String> KEY_COMPAT_MAP;
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
index 340a6c7f..c9dc1ba 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java
@@ -60,8 +60,9 @@
         allKeys.add(CategoryKey.CATEGORY_GESTURES);
         allKeys.add(CategoryKey.CATEGORY_NIGHT_DISPLAY);
         allKeys.add(CategoryKey.CATEGORY_SMART_BATTERY_SETTINGS);
+        allKeys.add(CategoryKey.CATEGORY_COMMUNAL_SETTINGS);
         // DO NOT REMOVE ANYTHING ABOVE
 
-        assertThat(allKeys.size()).isEqualTo(19);
+        assertThat(allKeys.size()).isEqualTo(20);
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index ed8a457..ed1a0f3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -564,6 +564,11 @@
                 break;
             }
 
+            case Settings.CALL_METHOD_UNREGISTER_MONITOR_CALLBACK_CONFIG: {
+                clearMonitorCallback();
+                break;
+            }
+
             case Settings.CALL_METHOD_LIST_GLOBAL: {
                 Bundle result = new Bundle();
                 result.putStringArrayList(RESULT_SETTINGS_LIST,
@@ -2352,6 +2357,16 @@
         }
     }
 
+    private void clearMonitorCallback() {
+        getContext().enforceCallingOrSelfPermission(
+                Manifest.permission.MONITOR_DEVICE_CONFIG_ACCESS,
+                "Permission denial: registering for config access requires: "
+                        + Manifest.permission.MONITOR_DEVICE_CONFIG_ACCESS);
+        synchronized (mLock) {
+            mConfigMonitorCallback = null;
+        }
+    }
+
     private void reportDeviceConfigAccess(@Nullable String prefix) {
         if (prefix == null) {
             return;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b8ac384..af4d7d4 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -145,6 +145,7 @@
     <uses-permission android:name="android.permission.LOCATION_BYPASS" />
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
+    <uses-permission android:name="android.permission.MONITOR_DEVICE_CONFIG_ACCESS" />
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
     <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY" />
     <!-- Development tool permissions granted to the shell. -->
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 68679c79..6f7d20a 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -455,8 +455,7 @@
         intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash);
         intent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_NONCE, nonce);
         intent.putExtra(EXTRA_BUGREPORT, bugreportFileName);
-        context.sendBroadcastAsUser(intent, UserHandle.SYSTEM,
-                android.Manifest.permission.DUMP);
+        context.sendBroadcast(intent, android.Manifest.permission.DUMP);
     }
 
     /**
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/Expandable.kt
index 18534f4..d31ca51 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/animation/Expandable.kt
@@ -24,6 +24,7 @@
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
 import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.defaultMinSize
@@ -62,6 +63,7 @@
 import androidx.compose.ui.graphics.drawscope.Stroke
 import androidx.compose.ui.graphics.drawscope.scale
 import androidx.compose.ui.layout.boundsInRoot
+import androidx.compose.ui.layout.findRootCoordinates
 import androidx.compose.ui.layout.layout
 import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.platform.ComposeView
@@ -118,12 +120,14 @@
     contentColor: Color = contentColorFor(color),
     borderStroke: BorderStroke? = null,
     onClick: ((Expandable) -> Unit)? = null,
+    interactionSource: MutableInteractionSource? = null,
     content: @Composable (Expandable) -> Unit,
 ) {
     Expandable(
         rememberExpandableController(color, shape, contentColor, borderStroke),
         modifier,
         onClick,
+        interactionSource,
         content,
     )
 }
@@ -158,6 +162,7 @@
     controller: ExpandableController,
     modifier: Modifier = Modifier,
     onClick: ((Expandable) -> Unit)? = null,
+    interactionSource: MutableInteractionSource? = null,
     content: @Composable (Expandable) -> Unit,
 ) {
     val controller = controller as ExpandableControllerImpl
@@ -190,6 +195,18 @@
 
     var thisExpandableSize by remember { mutableStateOf(Size.Zero) }
 
+    /** Set the current element size as this Expandable size. */
+    fun Modifier.updateExpandableSize(): Modifier {
+        return this.onGloballyPositioned { coords ->
+            thisExpandableSize =
+                coords
+                    .findRootCoordinates()
+                    // Make sure that we report the actual size, and not the visual/clipped one.
+                    .localBoundingBoxOf(coords, clipBounds = false)
+                    .size
+        }
+    }
+
     // Make sure we don't read animatorState directly here to avoid recomposition every time the
     // state changes (i.e. every frame of the animation).
     val isAnimating by remember {
@@ -247,7 +264,7 @@
         controller.isDialogShowing.value -> {
             Box(
                 modifier
-                    .onGloballyPositioned { thisExpandableSize = it.boundsInRoot().size }
+                    .updateExpandableSize()
                     .then(minInteractiveSizeModifier)
                     .drawWithContent { /* Don't draw anything when the dialog is shown. */}
                     .onGloballyPositioned {
@@ -258,18 +275,25 @@
         else -> {
             val clickModifier =
                 if (onClick != null) {
-                    Modifier.clickable { onClick(controller.expandable) }
+                    if (interactionSource != null) {
+                        // If the caller provided an interaction source, then that means that they
+                        // will draw the click indication themselves.
+                        Modifier.clickable(interactionSource, indication = null) {
+                            onClick(controller.expandable)
+                        }
+                    } else {
+                        // If no interaction source is provided, we draw the default indication (a
+                        // ripple) and make sure it's clipped by the expandable shape.
+                        Modifier.clip(shape).clickable { onClick(controller.expandable) }
+                    }
                 } else {
                     Modifier
                 }
 
             Box(
                 modifier
-                    .onGloballyPositioned { thisExpandableSize = it.boundsInRoot().size }
+                    .updateExpandableSize()
                     .then(minInteractiveSizeModifier)
-                    // Note that clip() *must* be above the clickModifier to properly clip the
-                    // ripple.
-                    .clip(shape)
                     .then(clickModifier)
                     .background(color, shape)
                     .border(controller)
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt
index b8639e6..caa7e5f 100644
--- a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt
+++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/AndroidColorScheme.kt
@@ -65,10 +65,12 @@
     val colorForeground = getColor(context, R.attr.colorForeground)
     val colorForegroundInverse = getColor(context, R.attr.colorForegroundInverse)
 
-    private fun getColor(context: Context, attr: Int): Color {
-        val ta = context.obtainStyledAttributes(intArrayOf(attr))
-        @ColorInt val color = ta.getColor(0, 0)
-        ta.recycle()
-        return Color(color)
+    companion object {
+        fun getColor(context: Context, attr: Int): Color {
+            val ta = context.obtainStyledAttributes(intArrayOf(attr))
+            @ColorInt val color = ta.getColor(0, 0)
+            ta.recycle()
+            return Color(color)
+        }
     }
 }
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/Color.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/Color.kt
new file mode 100644
index 0000000..de47cce
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/theme/Color.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.compose.theme
+
+import android.annotation.AttrRes
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+
+/** Read the [Color] from the given [attribute]. */
+@Composable
+@ReadOnlyComposable
+fun colorAttr(@AttrRes attribute: Int): Color {
+    return AndroidColorScheme.getColor(LocalContext.current, attribute)
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/ContentDescription.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/ContentDescription.kt
new file mode 100644
index 0000000..4a5ad65
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/ContentDescription.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.ui.compose
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import com.android.systemui.common.shared.model.ContentDescription
+
+/** Returns the loaded [String] or `null` if there isn't one. */
+@Composable
+fun ContentDescription.load(): String? {
+    return when (this) {
+        is ContentDescription.Loaded -> description
+        is ContentDescription.Resource -> stringResource(res)
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Icon.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Icon.kt
new file mode 100644
index 0000000..6e83124
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/Icon.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.ui.compose
+
+import androidx.compose.material3.Icon
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.res.painterResource
+import androidx.core.graphics.drawable.toBitmap
+import com.android.systemui.common.shared.model.Icon
+
+/**
+ * Icon composable that draws [icon] using [tint].
+ *
+ * Note: You can use [Color.Unspecified] to disable the tint and keep the original icon colors.
+ */
+@Composable
+fun Icon(
+    icon: Icon,
+    modifier: Modifier = Modifier,
+    tint: Color = LocalContentColor.current,
+) {
+    val contentDescription = icon.contentDescription?.load()
+    when (icon) {
+        is Icon.Loaded -> {
+            Icon(icon.drawable.toBitmap().asImageBitmap(), contentDescription, modifier, tint)
+        }
+        is Icon.Resource -> Icon(painterResource(icon.res), contentDescription, modifier, tint)
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
index 2bf1937..2aac46e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
@@ -53,7 +53,6 @@
 import com.android.systemui.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.people.ui.viewmodel.PeopleTileViewModel
 import com.android.systemui.people.ui.viewmodel.PeopleViewModel
-import kotlinx.coroutines.flow.collect
 
 /**
  * Compose the screen associated to a [PeopleViewModel].
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
new file mode 100644
index 0000000..654b723
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.footer.ui.compose
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.LocalIndication
+import androidx.compose.foundation.indication
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.layout.layout
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.constrainHeight
+import androidx.compose.ui.unit.constrainWidth
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.em
+import androidx.compose.ui.unit.sp
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.compose.animation.Expandable
+import com.android.systemui.compose.modifiers.background
+import com.android.systemui.compose.theme.LocalAndroidColorScheme
+import com.android.systemui.compose.theme.colorAttr
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsButtonViewModel
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsForegroundServicesButtonViewModel
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsSecurityButtonViewModel
+import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
+import kotlinx.coroutines.launch
+
+/** The Quick Settings footer actions row. */
+@Composable
+fun FooterActions(
+    viewModel: FooterActionsViewModel,
+    qsVisibilityLifecycleOwner: LifecycleOwner,
+    modifier: Modifier = Modifier,
+) {
+    val context = LocalContext.current
+
+    // Collect visibility and alphas as soon as we are composed, even when not visible.
+    val isVisible by viewModel.isVisible.collectAsState()
+    val alpha by viewModel.alpha.collectAsState()
+    val backgroundAlpha = viewModel.backgroundAlpha.collectAsState()
+
+    var security by remember { mutableStateOf<FooterActionsSecurityButtonViewModel?>(null) }
+    var foregroundServices by remember {
+        mutableStateOf<FooterActionsForegroundServicesButtonViewModel?>(null)
+    }
+    var userSwitcher by remember { mutableStateOf<FooterActionsButtonViewModel?>(null) }
+
+    LaunchedEffect(
+        context,
+        qsVisibilityLifecycleOwner,
+        viewModel,
+        viewModel.security,
+        viewModel.foregroundServices,
+        viewModel.userSwitcher,
+    ) {
+        launch {
+            // Listen for dialog requests as soon as we are composed, even when not visible.
+            viewModel.observeDeviceMonitoringDialogRequests(context)
+        }
+
+        // Listen for model changes only when QS are visible.
+        qsVisibilityLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
+            launch { viewModel.security.collect { security = it } }
+            launch { viewModel.foregroundServices.collect { foregroundServices = it } }
+            launch { viewModel.userSwitcher.collect { userSwitcher = it } }
+        }
+    }
+
+    val backgroundColor = colorAttr(R.attr.underSurfaceColor)
+    val contentColor = LocalAndroidColorScheme.current.textColorPrimary
+    val backgroundTopRadius = dimensionResource(R.dimen.qs_corner_radius)
+    val backgroundModifier =
+        remember(
+            backgroundColor,
+            backgroundAlpha,
+            backgroundTopRadius,
+        ) {
+            Modifier.background(
+                backgroundColor,
+                backgroundAlpha::value,
+                RoundedCornerShape(topStart = backgroundTopRadius, topEnd = backgroundTopRadius),
+            )
+        }
+
+    Row(
+        modifier
+            .fillMaxWidth()
+            .graphicsLayer { this.alpha = alpha }
+            .drawWithContent {
+                if (isVisible) {
+                    drawContent()
+                }
+            }
+            .then(backgroundModifier)
+            .padding(
+                top = dimensionResource(R.dimen.qs_footer_actions_top_padding),
+                bottom = dimensionResource(R.dimen.qs_footer_actions_bottom_padding),
+            )
+            .layout { measurable, constraints ->
+                // All buttons have a 4dp padding to increase their touch size. To be consistent
+                // with the View implementation, we want to left-most and right-most buttons to be
+                // visually aligned with the left and right sides of this row. So we let this
+                // component be 2*4dp wider and then offset it by -4dp to the start.
+                val inset = 4.dp.roundToPx()
+                val additionalWidth = inset * 2
+                val newConstraints =
+                    if (constraints.hasBoundedWidth) {
+                        constraints.copy(maxWidth = constraints.maxWidth + additionalWidth)
+                    } else {
+                        constraints
+                    }
+                val placeable = measurable.measure(newConstraints)
+
+                val width = constraints.constrainWidth(placeable.width - additionalWidth)
+                val height = constraints.constrainHeight(placeable.height)
+                layout(width, height) { placeable.place(-inset, 0) }
+            },
+        verticalAlignment = Alignment.CenterVertically,
+    ) {
+        CompositionLocalProvider(
+            LocalContentColor provides contentColor,
+        ) {
+            if (security == null && foregroundServices == null) {
+                Spacer(Modifier.weight(1f))
+            }
+
+            security?.let { SecurityButton(it, Modifier.weight(1f)) }
+            foregroundServices?.let { ForegroundServicesButton(it) }
+            userSwitcher?.let { IconButton(it) }
+            IconButton(viewModel.settings)
+            viewModel.power?.let { IconButton(it) }
+        }
+    }
+}
+
+/** The security button. */
+@Composable
+private fun SecurityButton(
+    model: FooterActionsSecurityButtonViewModel,
+    modifier: Modifier = Modifier,
+) {
+    val onClick: ((Expandable) -> Unit)? =
+        model.onClick?.let { onClick ->
+            val context = LocalContext.current
+            { expandable -> onClick(context, expandable) }
+        }
+
+    TextButton(
+        model.icon,
+        model.text,
+        showNewDot = false,
+        onClick = onClick,
+        modifier,
+    )
+}
+
+/** The foreground services button. */
+@Composable
+private fun RowScope.ForegroundServicesButton(
+    model: FooterActionsForegroundServicesButtonViewModel,
+) {
+    if (model.displayText) {
+        TextButton(
+            Icon.Resource(R.drawable.ic_info_outline, contentDescription = null),
+            model.text,
+            showNewDot = model.hasNewChanges,
+            onClick = model.onClick,
+            Modifier.weight(1f),
+        )
+    } else {
+        NumberButton(
+            model.foregroundServicesCount,
+            showNewDot = model.hasNewChanges,
+            onClick = model.onClick,
+        )
+    }
+}
+
+/** A button with an icon. */
+@Composable
+private fun IconButton(
+    model: FooterActionsButtonViewModel,
+    modifier: Modifier = Modifier,
+) {
+    Expandable(
+        color = colorAttr(model.backgroundColor),
+        shape = CircleShape,
+        onClick = model.onClick,
+        modifier = modifier,
+    ) {
+        val tint = model.iconTint?.let { Color(it) } ?: Color.Unspecified
+        Icon(
+            model.icon,
+            tint = tint,
+            modifier = Modifier.size(20.dp),
+        )
+    }
+}
+
+/** A button with a number an an optional dot (to indicate new changes). */
+@Composable
+private fun NumberButton(
+    number: Int,
+    showNewDot: Boolean,
+    onClick: (Expandable) -> Unit,
+    modifier: Modifier = Modifier,
+) {
+    // By default Expandable will show a ripple above its content when clicked, and clip the content
+    // with the shape of the expandable. In this case we also want to show a "new changes dot"
+    // outside of the shape, so we can't clip. To work around that we can pass our own interaction
+    // source and draw the ripple indication ourselves above the text but below the "new changes
+    // dot".
+    val interactionSource = remember { MutableInteractionSource() }
+
+    Expandable(
+        color = colorAttr(R.attr.offStateColor),
+        shape = CircleShape,
+        onClick = onClick,
+        interactionSource = interactionSource,
+        modifier = modifier,
+    ) {
+        Box(Modifier.size(40.dp)) {
+            Box(
+                Modifier.fillMaxSize()
+                    .clip(CircleShape)
+                    .indication(
+                        interactionSource,
+                        LocalIndication.current,
+                    )
+            ) {
+                Text(
+                    number.toString(),
+                    modifier = Modifier.align(Alignment.Center),
+                    style = MaterialTheme.typography.bodyLarge,
+                    color = LocalAndroidColorScheme.current.textColorPrimary,
+                    // TODO(b/242040009): This should only use a standard text style instead and
+                    // should not override the text size.
+                    fontSize = 18.sp,
+                )
+            }
+
+            if (showNewDot) {
+                NewChangesDot(Modifier.align(Alignment.BottomEnd))
+            }
+        }
+    }
+}
+
+/** A dot that indicates new changes. */
+@Composable
+private fun NewChangesDot(modifier: Modifier = Modifier) {
+    val contentDescription = stringResource(R.string.fgs_dot_content_description)
+    val color = LocalAndroidColorScheme.current.colorAccentTertiary
+
+    Canvas(modifier.size(12.dp).semantics { this.contentDescription = contentDescription }) {
+        drawCircle(color)
+    }
+}
+
+/** A larger button with an icon, some text and an optional dot (to indicate new changes). */
+@Composable
+private fun TextButton(
+    icon: Icon,
+    text: String,
+    showNewDot: Boolean,
+    onClick: ((Expandable) -> Unit)?,
+    modifier: Modifier = Modifier,
+) {
+    Expandable(
+        shape = CircleShape,
+        color = colorAttr(R.attr.underSurfaceColor),
+        contentColor = LocalAndroidColorScheme.current.textColorSecondary,
+        borderStroke = BorderStroke(1.dp, LocalAndroidColorScheme.current.colorBackground),
+        modifier = modifier.padding(horizontal = 4.dp),
+        onClick = onClick,
+    ) {
+        Row(
+            Modifier.padding(horizontal = dimensionResource(R.dimen.qs_footer_padding)),
+            verticalAlignment = Alignment.CenterVertically,
+        ) {
+            Icon(icon, Modifier.padding(end = 12.dp).size(20.dp))
+
+            Text(
+                text,
+                Modifier.weight(1f),
+                style = MaterialTheme.typography.bodyMedium,
+                // TODO(b/242040009): Remove this letter spacing. We should only use the M3 text
+                // styles without modifying them.
+                letterSpacing = 0.01.em,
+                maxLines = 1,
+                overflow = TextOverflow.Ellipsis,
+            )
+
+            if (showNewDot) {
+                NewChangesDot(Modifier.padding(start = 8.dp))
+            }
+
+            if (onClick != null) {
+                Icon(
+                    painterResource(com.android.internal.R.drawable.ic_chevron_end),
+                    contentDescription = null,
+                    Modifier.padding(start = 8.dp).size(20.dp),
+                )
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
index 894bb5f..cf7d2c5 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
@@ -179,8 +179,9 @@
 
     @ProvidesInterface(version = Callbacks.VERSION)
     public interface Callbacks {
-        int VERSION = 1;
+        int VERSION = 2;
 
+        // requires version 1
         void onShowRequested(int reason, boolean keyguardLocked, int lockTaskModeState);
         void onDismissRequested(int reason);
         void onStateChanged(State state);
@@ -192,5 +193,7 @@
         void onShowSafetyWarning(int flags);
         void onAccessibilityModeChanged(Boolean showA11yStream);
         void onCaptionComponentStateChanged(Boolean isComponentEnabled, Boolean fromTooltip);
+        // requires version 2
+        void onShowCsdWarning(@AudioManager.CsdWarning int csdWarning, int durationMs);
     }
 }
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 561f7f1..1ef523b 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -746,27 +746,6 @@
     <!-- How long in milliseconds before full burn-in protection is achieved. -->
     <integer name="config_dreamOverlayMillisUntilFullJitter">240000</integer>
 
-    <!-- The duration in milliseconds of the y-translation animation when waking up from
-         the dream -->
-    <integer name="config_dreamOverlayOutTranslationYDurationMs">333</integer>
-    <!-- The delay in milliseconds of the y-translation animation when waking up from
-         the dream for the complications at the bottom of the screen -->
-    <integer name="config_dreamOverlayOutTranslationYDelayBottomMs">33</integer>
-    <!-- The delay in milliseconds of the y-translation animation when waking up from
-         the dream for the complications at the top of the screen -->
-    <integer name="config_dreamOverlayOutTranslationYDelayTopMs">117</integer>
-    <!-- The duration in milliseconds of the alpha animation when waking up from the dream -->
-    <integer name="config_dreamOverlayOutAlphaDurationMs">200</integer>
-    <!-- The delay in milliseconds of the alpha animation when waking up from the dream for the
-         complications at the top of the screen -->
-    <integer name="config_dreamOverlayOutAlphaDelayTopMs">217</integer>
-    <!-- The delay in milliseconds of the alpha animation when waking up from the dream for the
-         complications at the bottom of the screen -->
-    <integer name="config_dreamOverlayOutAlphaDelayBottomMs">133</integer>
-    <!-- The duration in milliseconds of the blur animation when waking up from
-         the dream -->
-    <integer name="config_dreamOverlayOutBlurDurationMs">250</integer>
-
     <integer name="complicationFadeOutMs">500</integer>
 
     <integer name="complicationFadeInMs">500</integer>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index baaef19..7da27b1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -103,7 +103,6 @@
 
     @Override
     public void reset() {
-        super.reset();
         // start fresh
         mDismissing = false;
         mView.resetPasswordText(false /* animate */, false /* announce */);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index b143c5b..d1c9a30 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -121,7 +121,6 @@
 
     @Override
     public void reset() {
-        mMessageAreaController.setMessage("", false);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index cb6ffbd..76c1158373b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -72,6 +72,7 @@
 import android.os.PowerExemptionManager;
 import android.os.PowerManager;
 import android.os.ServiceManager;
+import android.os.SystemUpdateManager;
 import android.os.UserManager;
 import android.os.Vibrator;
 import android.os.storage.StorageManager;
@@ -140,6 +141,12 @@
         return context.getSystemService(AlarmManager.class);
     }
 
+    @Provides
+    @Singleton
+    static Optional<SystemUpdateManager> provideSystemUpdateManager(Context context) {
+        return Optional.ofNullable(context.getSystemService(SystemUpdateManager.class));
+    }
+
     /** */
     @Provides
     public AmbientDisplayConfiguration provideAmbientDisplayConfiguration(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamCallbackController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamCallbackController.kt
new file mode 100644
index 0000000..ab4632b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamCallbackController.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.policy.CallbackController
+import javax.inject.Inject
+
+/** Dream-related callback information */
+@SysUISingleton
+class DreamCallbackController @Inject constructor() :
+    CallbackController<DreamCallbackController.DreamCallback> {
+
+    private val callbacks = mutableSetOf<DreamCallbackController.DreamCallback>()
+
+    override fun addCallback(callback: DreamCallbackController.DreamCallback) {
+        callbacks.add(callback)
+    }
+
+    override fun removeCallback(callback: DreamCallbackController.DreamCallback) {
+        callbacks.remove(callback)
+    }
+
+    fun onWakeUp() {
+        callbacks.forEach { it.onWakeUp() }
+    }
+
+    interface DreamCallback {
+        fun onWakeUp()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index 9b8ef71..abe9355 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -22,6 +22,9 @@
 import android.view.View
 import android.view.animation.Interpolator
 import androidx.core.animation.doOnEnd
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.dreams.complication.ComplicationHostViewController
 import com.android.systemui.dreams.complication.ComplicationLayoutParams
@@ -29,10 +32,20 @@
 import com.android.systemui.dreams.complication.ComplicationLayoutParams.POSITION_TOP
 import com.android.systemui.dreams.complication.ComplicationLayoutParams.Position
 import com.android.systemui.dreams.dagger.DreamOverlayModule
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel.Companion.DREAM_ANIMATION_DURATION
+import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.statusbar.BlurUtils
 import com.android.systemui.statusbar.CrossFadeHelper
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import com.android.systemui.util.concurrency.DelayableExecutor
 import javax.inject.Inject
 import javax.inject.Named
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.launch
 
 /** Controller for dream overlay animations. */
 class DreamOverlayAnimationsController
@@ -43,6 +56,8 @@
     private val mStatusBarViewController: DreamOverlayStatusBarViewController,
     private val mOverlayStateController: DreamOverlayStateController,
     @Named(DreamOverlayModule.DREAM_BLUR_RADIUS) private val mDreamBlurRadius: Int,
+    private val transitionViewModel: DreamingToLockscreenTransitionViewModel,
+    private val configController: ConfigurationController,
     @Named(DreamOverlayModule.DREAM_IN_BLUR_ANIMATION_DURATION)
     private val mDreamInBlurAnimDurationMs: Long,
     @Named(DreamOverlayModule.DREAM_IN_COMPLICATIONS_ANIMATION_DURATION)
@@ -51,22 +66,10 @@
     private val mDreamInTranslationYDistance: Int,
     @Named(DreamOverlayModule.DREAM_IN_TRANSLATION_Y_DURATION)
     private val mDreamInTranslationYDurationMs: Long,
-    @Named(DreamOverlayModule.DREAM_OUT_TRANSLATION_Y_DISTANCE)
-    private val mDreamOutTranslationYDistance: Int,
-    @Named(DreamOverlayModule.DREAM_OUT_TRANSLATION_Y_DURATION)
-    private val mDreamOutTranslationYDurationMs: Long,
-    @Named(DreamOverlayModule.DREAM_OUT_TRANSLATION_Y_DELAY_BOTTOM)
-    private val mDreamOutTranslationYDelayBottomMs: Long,
-    @Named(DreamOverlayModule.DREAM_OUT_TRANSLATION_Y_DELAY_TOP)
-    private val mDreamOutTranslationYDelayTopMs: Long,
-    @Named(DreamOverlayModule.DREAM_OUT_ALPHA_DURATION) private val mDreamOutAlphaDurationMs: Long,
-    @Named(DreamOverlayModule.DREAM_OUT_ALPHA_DELAY_BOTTOM)
-    private val mDreamOutAlphaDelayBottomMs: Long,
-    @Named(DreamOverlayModule.DREAM_OUT_ALPHA_DELAY_TOP) private val mDreamOutAlphaDelayTopMs: Long,
-    @Named(DreamOverlayModule.DREAM_OUT_BLUR_DURATION) private val mDreamOutBlurDurationMs: Long
 ) {
 
     private var mAnimator: Animator? = null
+    private lateinit var view: View
 
     /**
      * Store the current alphas at the various positions. This is so that we may resume an animation
@@ -76,9 +79,63 @@
 
     private var mCurrentBlurRadius: Float = 0f
 
+    fun init(view: View) {
+        this.view = view
+
+        view.repeatWhenAttached {
+            val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
+            val configCallback =
+                object : ConfigurationListener {
+                    override fun onDensityOrFontScaleChanged() {
+                        configurationBasedDimensions.value = loadFromResources(view)
+                    }
+                }
+
+            configController.addCallback(configCallback)
+
+            repeatOnLifecycle(Lifecycle.State.CREATED) {
+                /* Translation animations, when moving from DREAMING->LOCKSCREEN state */
+                launch {
+                    configurationBasedDimensions
+                        .flatMapLatest {
+                            transitionViewModel.dreamOverlayTranslationY(it.translationYPx)
+                        }
+                        .collect { px ->
+                            setElementsTranslationYAtPosition(
+                                px,
+                                ComplicationLayoutParams.POSITION_TOP
+                            )
+                            setElementsTranslationYAtPosition(
+                                px,
+                                ComplicationLayoutParams.POSITION_BOTTOM
+                            )
+                        }
+                }
+
+                /* Alpha animations, when moving from DREAMING->LOCKSCREEN state */
+                launch {
+                    transitionViewModel.dreamOverlayAlpha.collect { alpha ->
+                        setElementsAlphaAtPosition(
+                            alpha = alpha,
+                            position = ComplicationLayoutParams.POSITION_TOP,
+                            fadingOut = true,
+                        )
+                        setElementsAlphaAtPosition(
+                            alpha = alpha,
+                            position = ComplicationLayoutParams.POSITION_BOTTOM,
+                            fadingOut = true,
+                        )
+                    }
+                }
+            }
+
+            configController.removeCallback(configCallback)
+        }
+    }
+
     /** Starts the dream content and dream overlay entry animations. */
     @JvmOverloads
-    fun startEntryAnimations(view: View, animatorBuilder: () -> AnimatorSet = { AnimatorSet() }) {
+    fun startEntryAnimations(animatorBuilder: () -> AnimatorSet = { AnimatorSet() }) {
         cancelAnimations()
 
         mAnimator =
@@ -113,73 +170,9 @@
     }
 
     /** Starts the dream content and dream overlay exit animations. */
-    @JvmOverloads
-    fun startExitAnimations(
-        view: View,
-        doneCallback: () -> Unit,
-        animatorBuilder: () -> AnimatorSet = { AnimatorSet() }
-    ) {
+    fun wakeUp(doneCallback: Runnable, executor: DelayableExecutor) {
         cancelAnimations()
-
-        mAnimator =
-            animatorBuilder().apply {
-                playTogether(
-                    blurAnimator(
-                        view = view,
-                        // Start the blurring wherever the entry animation ended, in
-                        // case it was cancelled early.
-                        fromBlurRadius = mCurrentBlurRadius,
-                        toBlurRadius = mDreamBlurRadius.toFloat(),
-                        durationMs = mDreamOutBlurDurationMs,
-                        interpolator = Interpolators.EMPHASIZED_ACCELERATE
-                    ),
-                    translationYAnimator(
-                        from = 0f,
-                        to = mDreamOutTranslationYDistance.toFloat(),
-                        durationMs = mDreamOutTranslationYDurationMs,
-                        delayMs = mDreamOutTranslationYDelayBottomMs,
-                        positions = POSITION_BOTTOM,
-                        interpolator = Interpolators.EMPHASIZED_ACCELERATE
-                    ),
-                    translationYAnimator(
-                        from = 0f,
-                        to = mDreamOutTranslationYDistance.toFloat(),
-                        durationMs = mDreamOutTranslationYDurationMs,
-                        delayMs = mDreamOutTranslationYDelayTopMs,
-                        positions = POSITION_TOP,
-                        interpolator = Interpolators.EMPHASIZED_ACCELERATE
-                    ),
-                    alphaAnimator(
-                        from =
-                            mCurrentAlphaAtPosition.getOrDefault(
-                                key = POSITION_BOTTOM,
-                                defaultValue = 1f
-                            ),
-                        to = 0f,
-                        durationMs = mDreamOutAlphaDurationMs,
-                        delayMs = mDreamOutAlphaDelayBottomMs,
-                        positions = POSITION_BOTTOM
-                    ),
-                    alphaAnimator(
-                        from =
-                            mCurrentAlphaAtPosition.getOrDefault(
-                                key = POSITION_TOP,
-                                defaultValue = 1f
-                            ),
-                        to = 0f,
-                        durationMs = mDreamOutAlphaDurationMs,
-                        delayMs = mDreamOutAlphaDelayTopMs,
-                        positions = POSITION_TOP
-                    )
-                )
-                doOnEnd {
-                    mAnimator = null
-                    mOverlayStateController.setExitAnimationsRunning(false)
-                    doneCallback()
-                }
-                start()
-            }
-        mOverlayStateController.setExitAnimationsRunning(true)
+        executor.executeDelayed(doneCallback, DREAM_ANIMATION_DURATION.inWholeMilliseconds)
     }
 
     /** Cancels the dream content and dream overlay animations, if they're currently running. */
@@ -288,4 +281,15 @@
             mStatusBarViewController.setTranslationY(translationY)
         }
     }
+
+    private fun loadFromResources(view: View): ConfigurationBasedDimensions {
+        return ConfigurationBasedDimensions(
+            translationYPx =
+                view.resources.getDimensionPixelSize(R.dimen.dream_overlay_exit_y_offset),
+        )
+    }
+
+    private data class ConfigurationBasedDimensions(
+        val translationYPx: Int,
+    )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 9d7ad30..3106173 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -42,9 +42,9 @@
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.util.ViewController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 
 import java.util.Arrays;
-import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -170,6 +170,7 @@
     protected void onInit() {
         mStatusBarViewController.init();
         mComplicationHostViewController.init();
+        mDreamOverlayAnimationsController.init(mView);
     }
 
     @Override
@@ -184,7 +185,7 @@
 
         // Start dream entry animations. Skip animations for low light clock.
         if (!mStateController.isLowLightActive()) {
-            mDreamOverlayAnimationsController.startEntryAnimations(mView);
+            mDreamOverlayAnimationsController.startEntryAnimations();
         }
     }
 
@@ -261,10 +262,8 @@
      * @param onAnimationEnd Callback to trigger once animations are finished.
      * @param callbackExecutor Executor to execute the callback on.
      */
-    public void wakeUp(@NonNull Runnable onAnimationEnd, @NonNull Executor callbackExecutor) {
-        mDreamOverlayAnimationsController.startExitAnimations(mView, () -> {
-            callbackExecutor.execute(onAnimationEnd);
-            return null;
-        });
+    public void wakeUp(@NonNull Runnable onAnimationEnd,
+            @NonNull DelayableExecutor callbackExecutor) {
+        mDreamOverlayAnimationsController.wakeUp(onAnimationEnd, callbackExecutor);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 16b4f99..1763dd9 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -46,10 +46,10 @@
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
 import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
 import com.android.systemui.touch.TouchInsetManager;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 
 import java.util.Arrays;
 import java.util.HashSet;
-import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -66,10 +66,11 @@
     // The Context is used to construct the hosting constraint layout and child overlay views.
     private final Context mContext;
     // The Executor ensures actions and ui updates happen on the same thread.
-    private final Executor mExecutor;
+    private final DelayableExecutor mExecutor;
     // A controller for the dream overlay container view (which contains both the status bar and the
     // content area).
     private DreamOverlayContainerViewController mDreamOverlayContainerViewController;
+    private final DreamCallbackController mDreamCallbackController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Nullable
     private final ComponentName mLowLightDreamComponent;
@@ -137,8 +138,8 @@
     @Inject
     public DreamOverlayService(
             Context context,
-            @Main Executor executor,
             DreamOverlayLifecycleOwner lifecycleOwner,
+            @Main DelayableExecutor executor,
             WindowManager windowManager,
             ComplicationComponent.Factory complicationComponentFactory,
             com.android.systemui.dreams.dreamcomplication.dagger.ComplicationComponent.Factory
@@ -149,7 +150,8 @@
             UiEventLogger uiEventLogger,
             TouchInsetManager touchInsetManager,
             @Nullable @Named(LowLightDreamModule.LOW_LIGHT_DREAM_COMPONENT)
-                    ComponentName lowLightDreamComponent) {
+                    ComponentName lowLightDreamComponent,
+            DreamCallbackController dreamCallbackController) {
         mContext = context;
         mExecutor = executor;
         mWindowManager = windowManager;
@@ -158,6 +160,7 @@
         mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
         mStateController = stateController;
         mUiEventLogger = uiEventLogger;
+        mDreamCallbackController = dreamCallbackController;
 
         final ViewModelStore viewModelStore = new ViewModelStore();
         final Complication.Host host =
@@ -242,6 +245,7 @@
     public void onWakeUp(@NonNull Runnable onCompletedCallback) {
         mExecutor.execute(() -> {
             if (mDreamOverlayContainerViewController != null) {
+                mDreamCallbackController.onWakeUp();
                 mDreamOverlayContainerViewController.wakeUp(onCompletedCallback, mExecutor);
             }
         });
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 4aa46d4..51eefd6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -58,22 +58,6 @@
             "dream_in_complications_translation_y";
     public static final String DREAM_IN_TRANSLATION_Y_DURATION =
             "dream_in_complications_translation_y_duration";
-    public static final String DREAM_OUT_TRANSLATION_Y_DISTANCE =
-            "dream_out_complications_translation_y";
-    public static final String DREAM_OUT_TRANSLATION_Y_DURATION =
-            "dream_out_complications_translation_y_duration";
-    public static final String DREAM_OUT_TRANSLATION_Y_DELAY_BOTTOM =
-            "dream_out_complications_translation_y_delay_bottom";
-    public static final String DREAM_OUT_TRANSLATION_Y_DELAY_TOP =
-            "dream_out_complications_translation_y_delay_top";
-    public static final String DREAM_OUT_ALPHA_DURATION =
-            "dream_out_complications_alpha_duration";
-    public static final String DREAM_OUT_ALPHA_DELAY_BOTTOM =
-            "dream_out_complications_alpha_delay_bottom";
-    public static final String DREAM_OUT_ALPHA_DELAY_TOP =
-            "dream_out_complications_alpha_delay_top";
-    public static final String DREAM_OUT_BLUR_DURATION =
-            "dream_out_blur_duration";
 
     /** */
     @Provides
@@ -180,66 +164,6 @@
         return (long) resources.getInteger(R.integer.config_dreamOverlayInTranslationYDurationMs);
     }
 
-    /**
-     * Provides the number of pixels to translate complications when waking up from dream.
-     */
-    @Provides
-    @Named(DREAM_OUT_TRANSLATION_Y_DISTANCE)
-    @DreamOverlayComponent.DreamOverlayScope
-    static int providesDreamOutComplicationsTranslationY(@Main Resources resources) {
-        return resources.getDimensionPixelSize(R.dimen.dream_overlay_exit_y_offset);
-    }
-
-    @Provides
-    @Named(DREAM_OUT_TRANSLATION_Y_DURATION)
-    @DreamOverlayComponent.DreamOverlayScope
-    static long providesDreamOutComplicationsTranslationYDuration(@Main Resources resources) {
-        return (long) resources.getInteger(R.integer.config_dreamOverlayOutTranslationYDurationMs);
-    }
-
-    @Provides
-    @Named(DREAM_OUT_TRANSLATION_Y_DELAY_BOTTOM)
-    @DreamOverlayComponent.DreamOverlayScope
-    static long providesDreamOutComplicationsTranslationYDelayBottom(@Main Resources resources) {
-        return (long) resources.getInteger(
-                R.integer.config_dreamOverlayOutTranslationYDelayBottomMs);
-    }
-
-    @Provides
-    @Named(DREAM_OUT_TRANSLATION_Y_DELAY_TOP)
-    @DreamOverlayComponent.DreamOverlayScope
-    static long providesDreamOutComplicationsTranslationYDelayTop(@Main Resources resources) {
-        return (long) resources.getInteger(R.integer.config_dreamOverlayOutTranslationYDelayTopMs);
-    }
-
-    @Provides
-    @Named(DREAM_OUT_ALPHA_DURATION)
-    @DreamOverlayComponent.DreamOverlayScope
-    static long providesDreamOutComplicationsAlphaDuration(@Main Resources resources) {
-        return (long) resources.getInteger(R.integer.config_dreamOverlayOutAlphaDurationMs);
-    }
-
-    @Provides
-    @Named(DREAM_OUT_ALPHA_DELAY_BOTTOM)
-    @DreamOverlayComponent.DreamOverlayScope
-    static long providesDreamOutComplicationsAlphaDelayBottom(@Main Resources resources) {
-        return (long) resources.getInteger(R.integer.config_dreamOverlayOutAlphaDelayBottomMs);
-    }
-
-    @Provides
-    @Named(DREAM_OUT_ALPHA_DELAY_TOP)
-    @DreamOverlayComponent.DreamOverlayScope
-    static long providesDreamOutComplicationsAlphaDelayTop(@Main Resources resources) {
-        return (long) resources.getInteger(R.integer.config_dreamOverlayOutAlphaDelayTopMs);
-    }
-
-    @Provides
-    @Named(DREAM_OUT_BLUR_DURATION)
-    @DreamOverlayComponent.DreamOverlayScope
-    static long providesDreamOutBlurDuration(@Main Resources resources) {
-        return (long) resources.getInteger(R.integer.config_dreamOverlayOutBlurDurationMs);
-    }
-
     @Provides
     @DreamOverlayComponent.DreamOverlayScope
     static Lifecycle providesLifecycle(LifecycleOwner lifecycleOwner) {
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
index c982131..276a290 100644
--- a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
@@ -51,6 +51,11 @@
         registerDumpable(name, module, DumpPriority.CRITICAL)
     }
 
+    /** See [registerNormalDumpable]. */
+    fun registerNormalDumpable(module: Dumpable) {
+        registerNormalDumpable(module::class.java.simpleName, module)
+    }
+
     /**
      * Registers a dumpable to be called during the NORMAL section of the bug report.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 9dd41f0..d597455 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -33,6 +33,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.keyguard.domain.interactor.DreamingTransitionInteractor.TO_LOCKSCREEN_DURATION_MS;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -1232,8 +1233,7 @@
 
         mDreamOpenAnimationDuration = context.getResources().getInteger(
                 com.android.internal.R.integer.config_dreamOpenAnimationDuration);
-        mDreamCloseAnimationDuration = context.getResources().getInteger(
-                com.android.internal.R.integer.config_dreamCloseAnimationDuration);
+        mDreamCloseAnimationDuration = (int) TO_LOCKSCREEN_DURATION_MS;
     }
 
     public void userActivity() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 148792b..9a0fbbf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -29,6 +29,8 @@
 import com.android.systemui.doze.DozeMachine
 import com.android.systemui.doze.DozeTransitionCallback
 import com.android.systemui.doze.DozeTransitionListener
+import com.android.systemui.dreams.DreamCallbackController
+import com.android.systemui.dreams.DreamCallbackController.DreamCallback
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
@@ -47,6 +49,7 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.merge
 
 /** Defines interface for classes that encapsulate application state for the keyguard. */
 interface KeyguardRepository {
@@ -176,6 +179,7 @@
     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     private val dozeTransitionListener: DozeTransitionListener,
     private val authController: AuthController,
+    private val dreamCallbackController: DreamCallbackController,
 ) : KeyguardRepository {
     private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
     override val animateBottomAreaDozingTransitions =
@@ -276,22 +280,35 @@
             .distinctUntilChanged()
 
     override val isDreaming: Flow<Boolean> =
-        conflatedCallbackFlow {
-                val callback =
-                    object : KeyguardUpdateMonitorCallback() {
-                        override fun onDreamingStateChanged(isDreaming: Boolean) {
-                            trySendWithFailureLogging(isDreaming, TAG, "updated isDreaming")
+        merge(
+                conflatedCallbackFlow {
+                    val callback =
+                        object : KeyguardUpdateMonitorCallback() {
+                            override fun onDreamingStateChanged(isDreaming: Boolean) {
+                                trySendWithFailureLogging(isDreaming, TAG, "updated isDreaming")
+                            }
                         }
-                    }
-                keyguardUpdateMonitor.registerCallback(callback)
-                trySendWithFailureLogging(
-                    keyguardUpdateMonitor.isDreaming,
-                    TAG,
-                    "initial isDreaming",
-                )
+                    keyguardUpdateMonitor.registerCallback(callback)
+                    trySendWithFailureLogging(
+                        keyguardUpdateMonitor.isDreaming,
+                        TAG,
+                        "initial isDreaming",
+                    )
 
-                awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
-            }
+                    awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+                },
+                conflatedCallbackFlow {
+                    val callback =
+                        object : DreamCallback {
+                            override fun onWakeUp() {
+                                trySendWithFailureLogging(false, TAG, "updated isDreaming")
+                            }
+                        }
+                    dreamCallbackController.addCallback(callback)
+
+                    awaitClose { dreamCallbackController.removeCallback(callback) }
+                }
+            )
             .distinctUntilChanged()
 
     override val linearDozeAmount: Flow<Float> = conflatedCallbackFlow {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index 5bb586e..d72d7183b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -66,8 +66,8 @@
     }
 
     /**
-     * Begin a transition from one state to another. Will not start if another transition is in
-     * progress.
+     * Begin a transition from one state to another. Transitions are interruptible, and will issue a
+     * [TransitionStep] with state = [TransitionState.CANCELED] before beginning the next one.
      */
     fun startTransition(info: TransitionInfo): UUID?
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingTransitionInteractor.kt
index b73ce9e..4d60579 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingTransitionInteractor.kt
@@ -28,6 +28,8 @@
 import com.android.systemui.keyguard.shared.model.TransitionInfo
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.combine
@@ -110,7 +112,7 @@
                                 name,
                                 KeyguardState.DREAMING,
                                 KeyguardState.LOCKSCREEN,
-                                getAnimator(),
+                                getAnimator(TO_LOCKSCREEN_DURATION),
                             )
                         )
                     }
@@ -167,14 +169,16 @@
         }
     }
 
-    private fun getAnimator(): ValueAnimator {
+    private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator {
         return ValueAnimator().apply {
             setInterpolator(Interpolators.LINEAR)
-            setDuration(TRANSITION_DURATION_MS)
+            setDuration(duration.inWholeMilliseconds)
         }
     }
 
     companion object {
-        private const val TRANSITION_DURATION_MS = 500L
+        private val DEFAULT_DURATION = 500.milliseconds
+        val TO_LOCKSCREEN_DURATION = 1183.milliseconds
+        @JvmField val TO_LOCKSCREEN_DURATION_MS = TO_LOCKSCREEN_DURATION.inWholeMilliseconds
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 54a4f49..3b9d6f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -19,12 +19,15 @@
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.AnimationParams
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import javax.inject.Inject
+import kotlin.time.Duration
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
@@ -43,6 +46,10 @@
     /** LOCKSCREEN->AOD transition information. */
     val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
 
+    /** DREAMING->LOCKSCREEN transition information. */
+    val dreamingToLockscreenTransition: Flow<TransitionStep> =
+        repository.transition(DREAMING, LOCKSCREEN)
+
     /** (any)->AOD transition information */
     val anyStateToAodTransition: Flow<TransitionStep> =
         repository.transitions.filter { step -> step.to == KeyguardState.AOD }
@@ -72,4 +79,21 @@
     /* The last completed [KeyguardState] transition */
     val finishedKeyguardState: Flow<KeyguardState> =
         finishedKeyguardTransitionStep.map { step -> step.to }
+
+    /**
+     * Transitions will occur over a [totalDuration] with [TransitionStep]s being emitted in the
+     * range of [0, 1]. View animations should begin and end within a subset of this range. This
+     * function maps the [startTime] and [duration] into [0, 1], when this subset is valid.
+     */
+    fun transitionStepAnimation(
+        flow: Flow<TransitionStep>,
+        params: AnimationParams,
+        totalDuration: Duration,
+    ): Flow<Float> {
+        val start = (params.startTime / totalDuration).toFloat()
+        val chunks = (totalDuration / params.duration).toFloat()
+        return flow
+            .map { step -> (step.value - start) * chunks }
+            .filter { value -> value >= 0f && value <= 1f }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt
new file mode 100644
index 0000000..67733e9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/AnimationParams.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.keyguard.shared.model
+
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.milliseconds
+
+/** Animation parameters */
+data class AnimationParams(
+    val startTime: Duration = 0.milliseconds,
+    val duration: Duration,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index 3d5985c5..f772b17 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -105,6 +105,14 @@
                     }
 
                     launch {
+                        viewModel.showWithFullExpansion.collect { model ->
+                            hostViewController.resetSecurityContainer()
+                            hostViewController.showPromptReason(model.promptReason)
+                            hostViewController.onResume()
+                        }
+                    }
+
+                    launch {
                         viewModel.hide.collect {
                             hostViewController.cancelDismissAction()
                             hostViewController.cleanUp()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
new file mode 100644
index 0000000..9b36e8c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.DreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.AnimationParams
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/**
+ * Breaks down DREAMING->LOCKSCREEN transition into discrete steps for corresponding views to
+ * consume.
+ */
+@SysUISingleton
+class DreamingToLockscreenTransitionViewModel
+@Inject
+constructor(
+    private val interactor: KeyguardTransitionInteractor,
+) {
+
+    /** Dream overlay y-translation on exit */
+    fun dreamOverlayTranslationY(translatePx: Int): Flow<Float> {
+        return flowForAnimation(DREAM_OVERLAY_TRANSLATION_Y).map { value ->
+            EMPHASIZED_ACCELERATE.getInterpolation(value) * translatePx
+        }
+    }
+    /** Dream overlay views alpha - fade out */
+    val dreamOverlayAlpha: Flow<Float> = flowForAnimation(DREAM_OVERLAY_ALPHA).map { 1f - it }
+
+    /** Dream background alpha - fade out */
+    val dreamBackgroundAlpha: Flow<Float> = flowForAnimation(DREAM_BACKGROUND_ALPHA).map { 1f - it }
+
+    /** Lockscreen views y-translation */
+    fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
+        return flowForAnimation(LOCKSCREEN_TRANSLATION_Y).map { it * translatePx }
+    }
+
+    /** Lockscreen views alpha */
+    val lockscreenAlpha: Flow<Float> = flowForAnimation(LOCKSCREEN_ALPHA)
+
+    private fun flowForAnimation(params: AnimationParams): Flow<Float> {
+        return interactor.transitionStepAnimation(
+            interactor.dreamingToLockscreenTransition,
+            params,
+            totalDuration = TO_LOCKSCREEN_DURATION
+        )
+    }
+
+    companion object {
+        /* Length of time before ending the dream activity, in order to start unoccluding */
+        val DREAM_ANIMATION_DURATION = 250.milliseconds
+
+        val DREAM_OVERLAY_TRANSLATION_Y = AnimationParams(duration = 500.milliseconds)
+        val DREAM_OVERLAY_ALPHA = AnimationParams(duration = 250.milliseconds)
+        val DREAM_BACKGROUND_ALPHA = AnimationParams(duration = 250.milliseconds)
+        val LOCKSCREEN_TRANSLATION_Y = AnimationParams(duration = TO_LOCKSCREEN_DURATION)
+        val LOCKSCREEN_ALPHA =
+            AnimationParams(startTime = 233.milliseconds, duration = 250.milliseconds)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
index 737c35d..e5d4e49 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
@@ -22,8 +22,10 @@
 import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
 import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
 import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
+import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
 
 /** Models UI state for the lock screen bouncer; handles user input. */
@@ -42,6 +44,10 @@
     /** Observe whether bouncer is showing. */
     val show: Flow<KeyguardBouncerModel> = interactor.show
 
+    /** Observe visible expansion when bouncer is showing. */
+    val showWithFullExpansion: Flow<KeyguardBouncerModel> =
+        interactor.show.filter { it.expansionAmount == EXPANSION_VISIBLE }
+
     /** Observe whether bouncer is hiding. */
     val hide: Flow<Unit> = interactor.hide
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 7b9d0b4..358534c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.common.ui.binder.TintedIconViewBinder
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
 import com.android.systemui.media.taptotransfer.common.MediaTttIcon
 import com.android.systemui.media.taptotransfer.common.MediaTttLogger
@@ -69,6 +70,7 @@
         mainExecutor: DelayableExecutor,
         accessibilityManager: AccessibilityManager,
         configurationController: ConfigurationController,
+        dumpManager: DumpManager,
         powerManager: PowerManager,
         @Main private val mainHandler: Handler,
         private val mediaTttFlags: MediaTttFlags,
@@ -83,6 +85,7 @@
         mainExecutor,
         accessibilityManager,
         configurationController,
+        dumpManager,
         powerManager,
         R.layout.media_ttt_chip_receiver,
         wakeLockBuilder,
@@ -162,6 +165,7 @@
     }
 
     override fun start() {
+        super.start()
         if (mediaTttFlags.isMediaTttEnabled()) {
             commandQueue.addCallback(commandQueueCallbacks)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
index 6db3c99..30f8124 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
@@ -237,7 +237,13 @@
             return
         }
 
-        buttonView.setBackgroundResource(model.background)
+        val backgroundResource =
+            when (model.backgroundColor) {
+                R.attr.offStateColor -> R.drawable.qs_footer_action_circle
+                com.android.internal.R.attr.colorAccent -> R.drawable.qs_footer_action_circle_color
+                else -> error("Unsupported icon background resource ${model.backgroundColor}")
+            }
+        buttonView.setBackgroundResource(backgroundResource)
         buttonView.setOnClickListener { model.onClick(Expandable.fromView(buttonView)) }
 
         val icon = model.icon
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
index 8d819da..2670787 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
@@ -16,7 +16,8 @@
 
 package com.android.systemui.qs.footer.ui.viewmodel
 
-import android.annotation.DrawableRes
+import android.annotation.AttrRes
+import android.annotation.ColorInt
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Icon
 
@@ -27,7 +28,7 @@
 data class FooterActionsButtonViewModel(
     val id: Int,
     val icon: Icon,
-    val iconTint: Int?,
-    @DrawableRes val background: Int,
+    @ColorInt val iconTint: Int?,
+    @AttrRes val backgroundColor: Int,
     val onClick: (Expandable) -> Unit,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index dee6fad..fbf32b3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.util.Log
+import android.view.ContextThemeWrapper
 import androidx.lifecycle.DefaultLifecycleObserver
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
@@ -49,12 +50,15 @@
 
 /** A ViewModel for the footer actions. */
 class FooterActionsViewModel(
-    @Application private val context: Context,
+    @Application appContext: Context,
     private val footerActionsInteractor: FooterActionsInteractor,
     private val falsingManager: FalsingManager,
     private val globalActionsDialogLite: GlobalActionsDialogLite,
     showPowerButton: Boolean,
 ) {
+    /** The context themed with the Quick Settings colors. */
+    private val context = ContextThemeWrapper(appContext, R.style.Theme_SystemUI_QuickSettings)
+
     /**
      * Whether the UI rendering this ViewModel should be visible. Note that even when this is false,
      * the UI should still participate to the layout it is included in (i.e. in the View world it
@@ -142,7 +146,7 @@
                 ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
             ),
             iconTint = null,
-            R.drawable.qs_footer_action_circle,
+            backgroundColor = R.attr.offStateColor,
             this::onSettingsButtonClicked,
         )
 
@@ -160,7 +164,7 @@
                         context,
                         com.android.internal.R.attr.textColorOnAccent,
                     ),
-                R.drawable.qs_footer_action_circle_color,
+                backgroundColor = com.android.internal.R.attr.colorAccent,
                 this::onPowerButtonClicked,
             )
         } else {
@@ -260,7 +264,7 @@
                     ),
                 ),
             iconTint = null,
-            background = R.drawable.qs_footer_action_circle,
+            backgroundColor = R.attr.offStateColor,
             onClick = this::onUserSwitcherClicked,
         )
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 8ceb46e..595b39a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -946,8 +946,7 @@
         if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
             if (mAlternateBouncerInteractor.isVisibleState()) {
                 return; // udfps affordance is highlighted, no need to show action to unlock
-            } else if (!mKeyguardUpdateMonitor.getIsFaceAuthenticated()
-                    && mKeyguardUpdateMonitor.isFaceEnrolled()) {
+            } else if (mKeyguardUpdateMonitor.isFaceEnrolled()) {
                 String message = mContext.getString(R.string.keyguard_retry);
                 mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState);
             }
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 5db95d7..ca1e397 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
@@ -146,7 +146,8 @@
     private static final int DELAY_BEFORE_SHADE_CLOSE = 200;
     private boolean mShadeNeedsToClose = false;
 
-    private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
+    @VisibleForTesting
+    static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
     private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
     private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
     /**
@@ -1326,8 +1327,11 @@
      * @param listenerNeedsAnimation does the listener need to animate?
      */
     private void updateStackPosition(boolean listenerNeedsAnimation) {
+        float topOverscrollAmount = mShouldUseSplitNotificationShade
+                ? getCurrentOverScrollAmount(true /* top */) : 0f;
         final float endTopPosition = mTopPadding + mExtraTopInsetForFullShadeTransition
                 + mAmbientState.getOverExpansion()
+                + topOverscrollAmount
                 - getCurrentOverScrollAmount(false /* top */);
         float fraction = mAmbientState.getExpansionFraction();
         // If we are on quick settings, we need to quickly hide it to show the bouncer to avoid an
@@ -2613,8 +2617,10 @@
             float bottomAmount = getCurrentOverScrollAmount(false);
             if (velocityY < 0 && topAmount > 0) {
                 setOwnScrollY(mOwnScrollY - (int) topAmount);
-                mDontReportNextOverScroll = true;
-                setOverScrollAmount(0, true, false);
+                if (!mShouldUseSplitNotificationShade) {
+                    mDontReportNextOverScroll = true;
+                    setOverScrollAmount(0, true, false);
+                }
                 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
                         * mOverflingDistance + topAmount;
             } else if (velocityY > 0 && bottomAmount > 0) {
@@ -2648,6 +2654,7 @@
         float topOverScroll = getCurrentOverScrollAmount(true);
         return mScrolledToTopOnFirstDown
                 && !mExpandedInThisMotion
+                && !mShouldUseSplitNotificationShade
                 && (initialVelocity > mMinimumVelocity
                 || (topOverScroll > mMinTopOverScrollToEscape && initialVelocity > 0));
     }
@@ -2713,7 +2720,7 @@
             return RUBBER_BAND_FACTOR_AFTER_EXPAND;
         } else if (mIsExpansionChanging || mPanelTracking) {
             return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
-        } else if (mScrolledToTopOnFirstDown) {
+        } else if (mScrolledToTopOnFirstDown && !mShouldUseSplitNotificationShade) {
             return 1.0f;
         }
         return RUBBER_BAND_FACTOR_NORMAL;
@@ -5705,7 +5712,8 @@
         }
     }
 
-    private void updateSplitNotificationShade() {
+    @VisibleForTesting
+    void updateSplitNotificationShade() {
         boolean split = LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
         if (split != mShouldUseSplitNotificationShade) {
             mShouldUseSplitNotificationShade = split;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt
index 1d00c33..a6b04e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectionModel.kt
@@ -41,6 +41,7 @@
 data class MobileConnectionModel(
     /** From [ServiceStateListener.onServiceStateChanged] */
     val isEmergencyOnly: Boolean = false,
+    val isRoaming: Boolean = false,
 
     /** From [SignalStrengthsListener.onSignalStrengthsChanged] */
     val isGsm: Boolean = false,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
index 2621f997..fc59f6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
@@ -50,4 +50,12 @@
      * [SubscriptionManager.getDefaultDataSubscriptionId]
      */
     val isDefaultDataSubscription: StateFlow<Boolean>
+
+    /**
+     * See [TelephonyManager.getCdmaEnhancedRoamingIndicatorDisplayNumber]. This bit only matters if
+     * the connection type is CDMA.
+     *
+     * True if the Enhanced Roaming Indicator (ERI) display number is not [TelephonyManager.ERI_OFF]
+     */
+    val cdmaRoaming: StateFlow<Boolean>
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index 1c08525..98b47e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -186,6 +186,7 @@
         connection.dataEnabled.value = true
         connection.isDefaultDataSubscription.value = state.dataType != null
 
+        connection.cdmaRoaming.value = state.roaming
         connection.connectionInfo.value = state.toMobileConnectionModel()
     }
 
@@ -229,6 +230,7 @@
     private fun Mobile.toMobileConnectionModel(): MobileConnectionModel {
         return MobileConnectionModel(
             isEmergencyOnly = false, // TODO(b/261029387): not yet supported
+            isRoaming = roaming,
             isGsm = false, // TODO(b/261029387): not yet supported
             cdmaLevel = level ?: 0,
             primaryLevel = level ?: 0,
@@ -260,4 +262,6 @@
     override val dataEnabled = MutableStateFlow(true)
 
     override val isDefaultDataSubscription = MutableStateFlow(true)
+
+    override val cdmaRoaming = MutableStateFlow(false)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoModeMobileConnectionDataSource.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoModeMobileConnectionDataSource.kt
index da55787..2cdbc19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoModeMobileConnectionDataSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoModeMobileConnectionDataSource.kt
@@ -98,6 +98,7 @@
         val inflateStrength = getString("inflate")?.toBoolean()
         val activity = getString("activity")?.toActivity()
         val carrierNetworkChange = getString("carriernetworkchange") == "show"
+        val roaming = getString("roam") == "show"
 
         return Mobile(
             level = level,
@@ -107,6 +108,7 @@
             inflateStrength = inflateStrength,
             activity = activity,
             carrierNetworkChange = carrierNetworkChange,
+            roaming = roaming,
         )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/model/FakeNetworkEventModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/model/FakeNetworkEventModel.kt
index 3f3acaf..b8543ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/model/FakeNetworkEventModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/model/FakeNetworkEventModel.kt
@@ -34,6 +34,7 @@
         val inflateStrength: Boolean?,
         @DataActivityType val activity: Int?,
         val carrierNetworkChange: Boolean,
+        val roaming: Boolean,
     ) : FakeNetworkEventModel
 
     data class MobileDisabled(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 15505fd..295e0dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -27,6 +27,7 @@
 import android.telephony.TelephonyDisplayInfo
 import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE
 import android.telephony.TelephonyManager
+import android.telephony.TelephonyManager.ERI_OFF
 import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.qualifiers.Application
@@ -95,7 +96,11 @@
                         TelephonyCallback.CarrierNetworkListener,
                         TelephonyCallback.DisplayInfoListener {
                         override fun onServiceStateChanged(serviceState: ServiceState) {
-                            state = state.copy(isEmergencyOnly = serviceState.isEmergencyOnly)
+                            state =
+                                state.copy(
+                                    isEmergencyOnly = serviceState.isEmergencyOnly,
+                                    isRoaming = serviceState.roaming,
+                                )
                             trySend(state)
                         }
 
@@ -208,6 +213,11 @@
             globalMobileDataSettingChangedEvent,
         )
 
+    override val cdmaRoaming: StateFlow<Boolean> =
+        telephonyPollingEvent
+            .mapLatest { telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber != ERI_OFF }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
     override val dataEnabled: StateFlow<Boolean> =
         telephonyPollingEvent
             .mapLatest { dataConnectionAllowed() }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index a26f28a..15b70f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -54,6 +54,13 @@
     /** True if this line of service is emergency-only */
     val isEmergencyOnly: StateFlow<Boolean>
 
+    /**
+     * True if this connection is considered roaming. The roaming bit can come from [ServiceState],
+     * or directly from the telephony manager's CDMA ERI number value. Note that we don't consider a
+     * connection to be roaming while carrier network change is active
+     */
+    val isRoaming: StateFlow<Boolean>
+
     /** Int describing the connection strength. 0-4 OR 1-5. See [numberOfLevels] */
     val level: StateFlow<Int>
 
@@ -95,6 +102,18 @@
             .mapLatest { it.isEmergencyOnly }
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
+    override val isRoaming: StateFlow<Boolean> =
+        combine(connectionInfo, connectionRepository.cdmaRoaming) { connection, cdmaRoaming ->
+                if (connection.carrierNetworkChangeActive) {
+                    false
+                } else if (connection.isGsm) {
+                    connection.isRoaming
+                } else {
+                    cdmaRoaming
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
     override val level: StateFlow<Int> =
         connectionInfo
             .mapLatest { connection ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index 67ea139..4455801 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -21,6 +21,7 @@
 import android.view.View.VISIBLE
 import android.view.ViewGroup
 import android.widget.ImageView
+import android.widget.Space
 import androidx.core.view.isVisible
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
@@ -29,7 +30,6 @@
 import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.launch
 
@@ -43,6 +43,8 @@
         val networkTypeView = view.requireViewById<ImageView>(R.id.mobile_type)
         val iconView = view.requireViewById<ImageView>(R.id.mobile_signal)
         val mobileDrawable = SignalDrawable(view.context).also { iconView.setImageDrawable(it) }
+        val roamingView = view.requireViewById<ImageView>(R.id.mobile_roaming)
+        val roamingSpace = view.requireViewById<Space>(R.id.mobile_roaming_space)
 
         view.isVisible = true
         iconView.isVisible = true
@@ -64,12 +66,21 @@
                     }
                 }
 
+                // Set the roaming indicator
+                launch {
+                    viewModel.roaming.distinctUntilChanged().collect { isRoaming ->
+                        roamingView.isVisible = isRoaming
+                        roamingSpace.isVisible = isRoaming
+                    }
+                }
+
                 // Set the tint
                 launch {
                     viewModel.tint.collect { tint ->
                         val tintList = ColorStateList.valueOf(tint)
                         iconView.imageTintList = tintList
                         networkTypeView.imageTintList = tintList
+                        roamingView.imageTintList = tintList
                     }
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index 8ebd718..f4d6111 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -87,5 +87,7 @@
             }
         }
 
+    val roaming: Flow<Boolean> = iconInteractor.isRoaming
+
     val tint: Flow<Int> = flowOf(Color.CYAN)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiActivityModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/DataActivityModel.kt
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiActivityModel.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/DataActivityModel.kt
index a4ca41c..9b41567 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiActivityModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/DataActivityModel.kt
@@ -14,20 +14,19 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.pipeline.wifi.shared.model
+package com.android.systemui.statusbar.pipeline.shared.data.model
 
 import com.android.systemui.log.table.Diffable
 import com.android.systemui.log.table.TableRowLogger
 
-/** Provides information on the current wifi activity. */
-data class WifiActivityModel(
-    /** True if the wifi has activity in (download). */
+/** Provides information about the current data activity direction */
+data class DataActivityModel(
+    /** True if the connection has activity in (download). */
     val hasActivityIn: Boolean,
-    /** True if the wifi has activity out (upload). */
+    /** True if the connection has activity out (upload). */
     val hasActivityOut: Boolean,
-) : Diffable<WifiActivityModel> {
-
-    override fun logDiffs(prevVal: WifiActivityModel, row: TableRowLogger) {
+) : Diffable<DataActivityModel> {
+    override fun logDiffs(prevVal: DataActivityModel, row: TableRowLogger) {
         if (prevVal.hasActivityIn != hasActivityIn) {
             row.logChange(COL_ACTIVITY_IN, hasActivityIn)
         }
@@ -42,6 +41,6 @@
     }
 }
 
-const val ACTIVITY_PREFIX = "wifiActivity"
+const val ACTIVITY_PREFIX = "dataActivity"
 private const val COL_ACTIVITY_IN = "in"
 private const val COL_ACTIVITY_OUT = "out"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
index 0c9c1cc..8144198 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
@@ -42,9 +42,8 @@
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
-import com.android.systemui.statusbar.pipeline.wifi.shared.model.ACTIVITY_PREFIX
-import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiActivityModel
 import java.util.concurrent.Executor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -74,7 +73,7 @@
     val wifiNetwork: StateFlow<WifiNetworkModel>
 
     /** Observable for the current wifi network activity. */
-    val wifiActivity: StateFlow<WifiActivityModel>
+    val wifiActivity: StateFlow<DataActivityModel>
 }
 
 /** Real implementation of [WifiRepository]. */
@@ -230,7 +229,7 @@
             initialValue = WIFI_NETWORK_DEFAULT
         )
 
-    override val wifiActivity: StateFlow<WifiActivityModel> =
+    override val wifiActivity: StateFlow<DataActivityModel> =
             if (wifiManager == null) {
                 Log.w(SB_LOGGING_TAG, "Null WifiManager; skipping activity callback")
                 flowOf(ACTIVITY_DEFAULT)
@@ -238,7 +237,7 @@
                 conflatedCallbackFlow {
                     val callback = TrafficStateCallback { state ->
                         logger.logInputChange("onTrafficStateChange", prettyPrintActivity(state))
-                        trySend(trafficStateToWifiActivityModel(state))
+                        trySend(trafficStateToDataActivityModel(state))
                     }
                     wifiManager.registerTrafficStateCallback(mainExecutor, callback)
                     awaitClose { wifiManager.unregisterTrafficStateCallback(callback) }
@@ -256,7 +255,9 @@
                 )
 
     companion object {
-        val ACTIVITY_DEFAULT = WifiActivityModel(hasActivityIn = false, hasActivityOut = false)
+        private const val ACTIVITY_PREFIX = "wifiActivity"
+
+        val ACTIVITY_DEFAULT = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
         // Start out with no known wifi network.
         // Note: [WifiStatusTracker] (the old implementation of connectivity logic) does do an
         // initial fetch to get a starting wifi network. But, it uses a deprecated API
@@ -265,8 +266,8 @@
         // NetworkCallback inside [wifiNetwork] for our wifi network information.
         val WIFI_NETWORK_DEFAULT = WifiNetworkModel.Inactive
 
-        private fun trafficStateToWifiActivityModel(state: Int): WifiActivityModel {
-            return WifiActivityModel(
+        private fun trafficStateToDataActivityModel(state: Int): DataActivityModel {
+            return DataActivityModel(
                 hasActivityIn = state == TrafficStateCallback.DATA_ACTIVITY_IN ||
                     state == TrafficStateCallback.DATA_ACTIVITY_INOUT,
                 hasActivityOut = state == TrafficStateCallback.DATA_ACTIVITY_OUT ||
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
index ec935fe..93041ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
@@ -19,10 +19,10 @@
 import android.net.wifi.WifiManager
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
-import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiActivityModel
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
@@ -50,8 +50,8 @@
     /** Our current wifi network. See [WifiNetworkModel]. */
     val wifiNetwork: Flow<WifiNetworkModel>
 
-    /** Our current wifi activity. See [WifiActivityModel]. */
-    val activity: StateFlow<WifiActivityModel>
+    /** Our current wifi activity. See [DataActivityModel]. */
+    val activity: StateFlow<DataActivityModel>
 
     /** True if we're configured to force-hide the wifi icon and false otherwise. */
     val isForceHidden: Flow<Boolean>
@@ -82,7 +82,7 @@
 
     override val wifiNetwork: Flow<WifiNetworkModel> = wifiRepository.wifiNetwork
 
-    override val activity: StateFlow<WifiActivityModel> = wifiRepository.wifiActivity
+    override val activity: StateFlow<DataActivityModel> = wifiRepository.wifiActivity
 
     override val isForceHidden: Flow<Boolean> = connectivityRepository.forceHiddenSlots.map {
         it.contains(ConnectivitySlot.WIFI)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index ec7ba65..07a7595 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -37,10 +37,10 @@
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logOutputChange
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
 import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
-import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiActivityModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
@@ -147,7 +147,7 @@
             )
 
     /** The wifi activity status. Null if we shouldn't display the activity status. */
-    private val activity: Flow<WifiActivityModel?> =
+    private val activity: Flow<DataActivityModel?> =
         if (!wifiConstants.shouldShowActivityConfig) {
             flowOf(null)
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusFirstUsageListener.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusFirstUsageListener.kt
new file mode 100644
index 0000000..154c6e2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusFirstUsageListener.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.stylus
+
+import android.content.Context
+import android.hardware.BatteryState
+import android.hardware.input.InputManager
+import android.os.Handler
+import android.util.Log
+import android.view.InputDevice
+import androidx.annotation.VisibleForTesting
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import java.util.concurrent.Executor
+import javax.inject.Inject
+
+/**
+ * A listener that detects when a stylus has first been used, by detecting 1) the presence of an
+ * internal SOURCE_STYLUS with a battery, or 2) any added SOURCE_STYLUS device with a bluetooth
+ * address.
+ */
+@SysUISingleton
+class StylusFirstUsageListener
+@Inject
+constructor(
+    private val context: Context,
+    private val inputManager: InputManager,
+    private val stylusManager: StylusManager,
+    private val featureFlags: FeatureFlags,
+    @Background private val executor: Executor,
+    @Background private val handler: Handler,
+) : CoreStartable, StylusManager.StylusCallback, InputManager.InputDeviceBatteryListener {
+
+    // Set must be only accessed from the background handler, which is the same handler that
+    // runs the StylusManager callbacks.
+    private val internalStylusDeviceIds: MutableSet<Int> = mutableSetOf()
+    @VisibleForTesting var hasStarted = false
+
+    override fun start() {
+        if (true) return // TODO(b/261826950): remove on main
+        if (hasStarted) return
+        if (!featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)) return
+        if (inputManager.isStylusEverUsed(context)) return
+        if (!hostDeviceSupportsStylusInput()) return
+
+        hasStarted = true
+        inputManager.inputDeviceIds.forEach(this::onStylusAdded)
+        stylusManager.registerCallback(this)
+        stylusManager.startListener()
+    }
+
+    override fun onStylusAdded(deviceId: Int) {
+        if (!hasStarted) return
+
+        val device = inputManager.getInputDevice(deviceId) ?: return
+        if (device.isExternal || !device.supportsSource(InputDevice.SOURCE_STYLUS)) return
+
+        try {
+            inputManager.addInputDeviceBatteryListener(deviceId, executor, this)
+            internalStylusDeviceIds += deviceId
+        } catch (e: SecurityException) {
+            Log.e(TAG, "$e: Failed to register battery listener for $deviceId ${device.name}.")
+        }
+    }
+
+    override fun onStylusRemoved(deviceId: Int) {
+        if (!hasStarted) return
+
+        if (!internalStylusDeviceIds.contains(deviceId)) return
+        try {
+            inputManager.removeInputDeviceBatteryListener(deviceId, this)
+            internalStylusDeviceIds.remove(deviceId)
+        } catch (e: SecurityException) {
+            Log.e(TAG, "$e: Failed to remove registered battery listener for $deviceId.")
+        }
+    }
+
+    override fun onStylusBluetoothConnected(deviceId: Int, btAddress: String) {
+        if (!hasStarted) return
+
+        onRemoteDeviceFound()
+    }
+
+    override fun onBatteryStateChanged(
+        deviceId: Int,
+        eventTimeMillis: Long,
+        batteryState: BatteryState
+    ) {
+        if (!hasStarted) return
+
+        if (batteryState.isPresent) {
+            onRemoteDeviceFound()
+        }
+    }
+
+    private fun onRemoteDeviceFound() {
+        inputManager.setStylusEverUsed(context, true)
+        cleanupListeners()
+    }
+
+    private fun cleanupListeners() {
+        stylusManager.unregisterCallback(this)
+        handler.post {
+            internalStylusDeviceIds.forEach {
+                inputManager.removeInputDeviceBatteryListener(it, this)
+            }
+        }
+    }
+
+    private fun hostDeviceSupportsStylusInput(): Boolean {
+        return inputManager.inputDeviceIds
+            .asSequence()
+            .mapNotNull { inputManager.getInputDevice(it) }
+            .any { it.supportsSource(InputDevice.SOURCE_STYLUS) && !it.isExternal }
+    }
+
+    companion object {
+        private val TAG = StylusFirstUsageListener::class.simpleName.orEmpty()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index db7315f..532fbaa 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -30,12 +30,16 @@
 import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS
 import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS
 import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT
+import androidx.annotation.CallSuper
 import com.android.systemui.CoreStartable
+import com.android.systemui.Dumpable
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.time.SystemClock
 import com.android.systemui.util.wakelock.WakeLock
+import java.io.PrintWriter
 
 /**
  * A generic controller that can temporarily display a new view in a new window.
@@ -69,11 +73,12 @@
     @Main private val mainExecutor: DelayableExecutor,
     private val accessibilityManager: AccessibilityManager,
     private val configurationController: ConfigurationController,
+    private val dumpManager: DumpManager,
     private val powerManager: PowerManager,
     @LayoutRes private val viewLayoutRes: Int,
     private val wakeLockBuilder: WakeLock.Builder,
     private val systemClock: SystemClock,
-) : CoreStartable {
+) : CoreStartable, Dumpable {
     /**
      * Window layout params that will be used as a starting point for the [windowLayoutParams] of
      * all subclasses.
@@ -109,6 +114,11 @@
         return activeViews.getOrNull(0)
     }
 
+    @CallSuper
+    override fun start() {
+        dumpManager.registerNormalDumpable(this)
+    }
+
     /**
      * Displays the view with the provided [newInfo].
      *
@@ -382,6 +392,19 @@
         }
     }
 
+    @Synchronized
+    @CallSuper
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("Current time millis: ${systemClock.currentTimeMillis()}")
+        pw.println("Active views size: ${activeViews.size}")
+        activeViews.forEachIndexed { index, displayInfo ->
+            pw.println("View[$index]:")
+            pw.println("  info=${displayInfo.info}")
+            pw.println("  hasView=${displayInfo.view != null}")
+            pw.println("  timeExpiration=${displayInfo.timeExpirationMillis}")
+        }
+    }
+
     /**
      * A method implemented by subclasses to update [currentView] based on [newInfo].
      */
@@ -445,8 +468,6 @@
          */
         var cancelViewTimeout: Runnable?,
     )
-
-    // TODO(b/258019006): Add a dump method that dumps the currently active views.
 }
 
 private const val REMOVAL_REASON_TIMEOUT = "TIMEOUT"
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index f37ef82..fd2c705 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -41,6 +41,7 @@
 import com.android.systemui.common.ui.binder.TintedIconViewBinder
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -75,6 +76,7 @@
         @Main mainExecutor: DelayableExecutor,
         accessibilityManager: AccessibilityManager,
         configurationController: ConfigurationController,
+        dumpManager: DumpManager,
         powerManager: PowerManager,
         private val falsingManager: FalsingManager,
         private val falsingCollector: FalsingCollector,
@@ -89,6 +91,7 @@
         mainExecutor,
         accessibilityManager,
         configurationController,
+        dumpManager,
         powerManager,
         R.layout.chipbar,
         wakeLockBuilder,
@@ -225,8 +228,6 @@
         return requireViewById(R.id.chipbar_inner)
     }
 
-    override fun start() {}
-
     override fun getTouchableRegion(view: View, outRect: Rect) {
         viewUtil.setRectToViewWindowLocation(view, outRect)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java b/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
new file mode 100644
index 0000000..35af444
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.annotation.StringRes;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.os.CountDownTimer;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.WindowManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+
+/**
+ * A class that implements the four Computed Sound Dose-related warnings defined in {@link AudioManager}:
+ * <ul>
+ *     <li>{@link AudioManager#CSD_WARNING_DOSE_REACHED_1X}</li>
+ *     <li>{@link AudioManager#CSD_WARNING_DOSE_REPEATED_5X}</li>
+ *     <li>{@link AudioManager#CSD_WARNING_ACCUMULATION_START}</li>
+ *     <li>{@link AudioManager#CSD_WARNING_MOMENTARY_EXPOSURE}</li>
+ * </ul>
+ * Rather than basing volume safety messages on a fixed volume index, the CSD feature derives its
+ * warnings from the computation of the "sound dose". The dose computation is based on a
+ * frequency-dependent analysis of the audio signal which estimates how loud and potentially harmful
+ * the signal content is. This is combined with the volume attenuation/amplification applied to it
+ * and integrated over time to derive the dose exposure over a 7 day rolling window.
+ * <p>The UI behaviors implemented in this class are defined in IEC 62368 in "Safeguards against
+ * acoustic energy sources". The events that trigger those warnings originate in SoundDoseHelper
+ * which runs in the "audio" system_server service (see
+ * frameworks/base/services/core/java/com/android/server/audio/AudioService.java for the
+ * communication between the audio framework and the volume controller, and
+ * frameworks/base/services/core/java/com/android/server/audio/SoundDoseHelper.java for the
+ * communication between the native audio framework that implements the dose computation and the
+ * audio service.
+ */
+public abstract class CsdWarningDialog extends SystemUIDialog
+        implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
+
+    private static final String TAG = Util.logTag(CsdWarningDialog.class);
+
+    private static final int KEY_CONFIRM_ALLOWED_AFTER_MS = 1000; // milliseconds
+    // time after which action is taken when the user hasn't ack'd or dismissed the dialog
+    private static final int NO_ACTION_TIMEOUT_MS = 5000;
+
+    private final Context mContext;
+    private final AudioManager mAudioManager;
+    private final @AudioManager.CsdWarning int mCsdWarning;
+    private final Object mTimerLock = new Object();
+    /**
+     * Timer to keep track of how long the user has before an action (here volume reduction) is
+     * taken on their behalf.
+     */
+    @GuardedBy("mTimerLock")
+    private final CountDownTimer mNoUserActionTimer;
+
+    private long mShowTime;
+
+    public CsdWarningDialog(@AudioManager.CsdWarning int csdWarning, Context context,
+            AudioManager audioManager) {
+        super(context);
+        mCsdWarning = csdWarning;
+        mContext = context;
+        mAudioManager = audioManager;
+        getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+        setShowForAllUsers(true);
+        setMessage(mContext.getString(getStringForWarning(csdWarning)));
+        setButton(DialogInterface.BUTTON_POSITIVE,
+                mContext.getString(com.android.internal.R.string.yes), this);
+        setButton(DialogInterface.BUTTON_NEGATIVE,
+                mContext.getString(com.android.internal.R.string.no), this);
+        setOnDismissListener(this);
+
+        final IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+        context.registerReceiver(mReceiver, filter,
+                Context.RECEIVER_EXPORTED_UNAUDITED);
+
+        if (csdWarning == AudioManager.CSD_WARNING_DOSE_REACHED_1X) {
+            mNoUserActionTimer = new CountDownTimer(NO_ACTION_TIMEOUT_MS, NO_ACTION_TIMEOUT_MS) {
+                @Override
+                public void onTick(long millisUntilFinished) { }
+
+                @Override
+                public void onFinish() {
+                    if (mCsdWarning == AudioManager.CSD_WARNING_DOSE_REACHED_1X) {
+                        // unlike on the 5x dose repeat, level is only reduced to RS1
+                        // when the warning is not acknowledged quick enough
+                        mAudioManager.lowerVolumeToRs1();
+                    }
+                }
+            };
+        } else {
+            mNoUserActionTimer = null;
+        }
+    }
+
+    protected abstract void cleanUp();
+
+    // NOT overriding onKeyDown as we're not allowing a dismissal on any key other than
+    // VOLUME_DOWN, and for this, we don't need to track if it's the start of a new
+    // key down -> up sequence
+    //@Override
+    //public boolean onKeyDown(int keyCode, KeyEvent event) {
+    //    return super.onKeyDown(keyCode, event);
+    //}
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        // never allow to raise volume
+        if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
+            return true;
+        }
+        // VOLUME_DOWN will dismiss the dialog
+        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+                && (System.currentTimeMillis() - mShowTime) > KEY_CONFIRM_ALLOWED_AFTER_MS) {
+            Log.i(TAG, "Confirmed CSD exposure warning via VOLUME_DOWN");
+            dismiss();
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        if (which == DialogInterface.BUTTON_POSITIVE) {
+            Log.d(TAG, "OK pressed for CSD warning " + mCsdWarning);
+            dismiss();
+
+        }
+        if (D.BUG) Log.d(TAG, "on click " + which);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mShowTime = System.currentTimeMillis();
+        synchronized (mTimerLock) {
+            if (mNoUserActionTimer != null) {
+                new Thread(() -> {
+                    synchronized (mTimerLock) {
+                        mNoUserActionTimer.start();
+                    }
+                }).start();
+            }
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        synchronized (mTimerLock) {
+            if (mNoUserActionTimer != null) {
+                mNoUserActionTimer.cancel();
+            }
+        }
+    }
+
+    @Override
+    public void onDismiss(DialogInterface unused) {
+        if (mCsdWarning == AudioManager.CSD_WARNING_DOSE_REPEATED_5X) {
+            // level is always reduced to RS1 beyond the 5x dose
+            mAudioManager.lowerVolumeToRs1();
+        }
+        try {
+            mContext.unregisterReceiver(mReceiver);
+        } catch (IllegalArgumentException e) {
+            // Don't crash if the receiver has already been unregistered.
+            Log.e(TAG, "Error unregistering broadcast receiver", e);
+        }
+        cleanUp();
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
+                if (D.BUG) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
+                cancel();
+                cleanUp();
+            }
+        }
+    };
+
+    private @StringRes int getStringForWarning(@AudioManager.CsdWarning int csdWarning) {
+        switch (csdWarning) {
+            case AudioManager.CSD_WARNING_DOSE_REACHED_1X:
+                return com.android.internal.R.string.csd_dose_reached_warning;
+            case AudioManager.CSD_WARNING_DOSE_REPEATED_5X:
+                return com.android.internal.R.string.csd_dose_repeat_warning;
+            case AudioManager.CSD_WARNING_MOMENTARY_EXPOSURE:
+                return com.android.internal.R.string.csd_momentary_exposure_warning;
+            case AudioManager.CSD_WARNING_ACCUMULATION_START:
+                return com.android.internal.R.string.csd_entering_RS2_warning;
+        }
+        Log.e(TAG, "Invalid CSD warning event " + csdWarning, new Exception());
+        return com.android.internal.R.string.csd_dose_reached_warning;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
index 369552f..fc0033d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Events.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -97,6 +97,7 @@
     public static final int DISMISS_STREAM_GONE = 7;
     public static final int DISMISS_REASON_OUTPUT_CHOOSER = 8;
     public static final int DISMISS_REASON_USB_OVERHEAD_ALARM_CHANGED = 9;
+    public static final int DISMISS_REASON_CSD_WARNING_TIMEOUT = 10;
     public static final String[] DISMISS_REASONS = {
             "unknown",
             "touch_outside",
@@ -107,7 +108,8 @@
             "done_clicked",
             "a11y_stream_changed",
             "output_chooser",
-            "usb_temperature_below_threshold"
+            "usb_temperature_below_threshold",
+            "csd_warning_timeout"
     };
 
     public static final int SHOW_REASON_UNKNOWN = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 9349966..2fc8b03 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -410,6 +410,10 @@
         }
     }
 
+    private void onShowCsdWarningW(@AudioManager.CsdWarning int csdWarning, int durationMs) {
+            mCallbacks.onShowCsdWarning(csdWarning, durationMs);
+    }
+
     private void onGetCaptionsComponentStateW(boolean fromTooltip) {
         mCallbacks.onCaptionComponentStateChanged(
                 mCaptioningManager.isSystemAudioCaptioningUiEnabled(), fromTooltip);
@@ -707,6 +711,27 @@
             mWorker.obtainMessage(W.SHOW_SAFETY_WARNING, flags, 0).sendToTarget();
         }
 
+        /**
+         * Display a sound-dose related warning.
+         * This method will never be called if the CSD (Computed Sound Dose) feature is
+         * not enabled. See com.android.android.server.audio.SoundDoseHelper for the state of
+         * the feature.
+         * @param warning the type of warning to display, values are one of
+         *        {@link android.media.AudioManager#CSD_WARNING_DOSE_REACHED_1X},
+         *        {@link android.media.AudioManager#CSD_WARNING_DOSE_REPEATED_5X},
+         *        {@link android.media.AudioManager#CSD_WARNING_MOMENTARY_EXPOSURE},
+         *        {@link android.media.AudioManager#CSD_WARNING_ACCUMULATION_START}.
+         * @param displayDurationMs the time expressed in milliseconds after which the dialog will be
+         *        automatically dismissed, or -1 if there is no automatic timeout.
+         */
+        @Override
+        public void displayCsdWarning(int csdWarning, int displayDurationMs) throws RemoteException
+        {
+            if (D.BUG) Log.d(TAG, "displayCsdWarning durMs=" + displayDurationMs);
+            mWorker.obtainMessage(W.SHOW_CSD_WARNING, csdWarning, displayDurationMs)
+                    .sendToTarget();
+        }
+
         @Override
         public void volumeChanged(int streamType, int flags) throws RemoteException {
             if (D.BUG) Log.d(TAG, "volumeChanged " + AudioSystem.streamToString(streamType)
@@ -769,6 +794,7 @@
         private static final int SHOW_SAFETY_WARNING = 14;
         private static final int ACCESSIBILITY_MODE_CHANGED = 15;
         private static final int GET_CAPTIONS_COMPONENT_STATE = 16;
+        private static final int SHOW_CSD_WARNING = 17;
 
         W(Looper looper) {
             super(looper);
@@ -794,6 +820,8 @@
                 case GET_CAPTIONS_COMPONENT_STATE:
                     onGetCaptionsComponentStateW((Boolean) msg.obj); break;
                 case ACCESSIBILITY_MODE_CHANGED: onAccessibilityModeChanged((Boolean) msg.obj);
+                    break;
+                case SHOW_CSD_WARNING: onShowCsdWarningW(msg.arg1, msg.arg2); break;
             }
         }
     }
@@ -925,6 +953,21 @@
         }
 
         @Override
+        public void onShowCsdWarning(int csdWarning, int durationMs) {
+            if (Callbacks.VERSION < 2) {
+                return;
+            }
+            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+                entry.getValue().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        entry.getKey().onShowCsdWarning(csdWarning, durationMs);
+                    }
+                });
+            }
+        }
+
+        @Override
         public void onAccessibilityModeChanged(Boolean showA11yStream) {
             boolean show = showA11yStream != null && showA11yStream;
             for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 1d640db..7d23399 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -109,6 +109,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
@@ -272,7 +273,10 @@
     private boolean mAutomute = VolumePrefs.DEFAULT_ENABLE_AUTOMUTE;
     private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
     private State mState;
+    @GuardedBy("mSafetyWarningLock")
     private SafetyWarningDialog mSafetyWarning;
+    @GuardedBy("mSafetyWarningLock")
+    private CsdWarningDialog mCsdDialog;
     private boolean mHovering = false;
     private final boolean mShowActiveStreamOnly;
     private boolean mConfigChanged = false;
@@ -1476,6 +1480,23 @@
                 AccessibilityManager.FLAG_CONTENT_CONTROLS);
     }
 
+    protected void scheduleCsdTimeoutH(int timeoutMs) {
+        mHandler.removeMessages(H.CSD_TIMEOUT);
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(H.CSD_TIMEOUT,
+                Events.DISMISS_REASON_CSD_WARNING_TIMEOUT, 0), timeoutMs);
+        Log.i(TAG, "scheduleCsdTimeoutH " + timeoutMs + "ms " + Debug.getCaller());
+        mController.userActivity();
+    }
+
+    private void onCsdTimeoutH() {
+        synchronized (mSafetyWarningLock) {
+            if (mCsdDialog == null) {
+                return;
+            }
+            mCsdDialog.dismiss();
+        }
+    }
+
     protected void dismissH(int reason) {
         Trace.beginSection("VolumeDialogImpl#dismissH");
 
@@ -2060,6 +2081,30 @@
         rescheduleTimeoutH();
     }
 
+    private void showCsdWarningH(int csdWarning, int durationMs) {
+        synchronized (mSafetyWarningLock) {
+            if (mCsdDialog != null) {
+                return;
+            }
+            mCsdDialog = new CsdWarningDialog(csdWarning,
+                    mContext, mController.getAudioManager()) {
+                @Override
+                protected void cleanUp() {
+                    synchronized (mSafetyWarningLock) {
+                        mCsdDialog = null;
+                    }
+                    recheckH(null);
+                }
+            };
+            mCsdDialog.show();
+        }
+        recheckH(null);
+        if (durationMs > 0) {
+            scheduleCsdTimeoutH(durationMs);
+        }
+        rescheduleTimeoutH();
+    }
+
     private String getStreamLabelH(StreamState ss) {
         if (ss == null) {
             return "";
@@ -2239,6 +2284,11 @@
         }
 
         @Override
+        public void onShowCsdWarning(int csdWarning, int durationMs) {
+            showCsdWarningH(csdWarning, durationMs);
+        }
+
+        @Override
         public void onAccessibilityModeChanged(Boolean showA11yStream) {
             mShowA11yStream = showA11yStream == null ? false : showA11yStream;
             VolumeRow activeRow = getActiveRow();
@@ -2265,6 +2315,7 @@
         private static final int SET_STREAM_IMPORTANT = 5;
         private static final int RESCHEDULE_TIMEOUT = 6;
         private static final int STATE_CHANGED = 7;
+        private static final int CSD_TIMEOUT = 8;
 
         public H() {
             super(Looper.getMainLooper());
@@ -2281,6 +2332,7 @@
                 case SET_STREAM_IMPORTANT: setStreamImportantH(msg.arg1, msg.arg2 != 0); break;
                 case RESCHEDULE_TIMEOUT: rescheduleTimeoutH(); break;
                 case STATE_CHANGED: onStateChangedH(mState); break;
+                case CSD_TIMEOUT: onCsdTimeoutH(); break;
             }
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index fa9bab2..1059543 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -150,10 +150,4 @@
                 getContext().getResources().getString(R.string.kg_prompt_reason_restart_password),
                 false);
     }
-
-    @Test
-    public void testReset() {
-        mKeyguardAbsKeyInputViewController.reset();
-        verify(mKeyguardMessageAreaController).setMessage("", false);
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamCallbackControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamCallbackControllerTest.kt
new file mode 100644
index 0000000..003efbf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamCallbackControllerTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.dreams
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DreamCallbackControllerTest : SysuiTestCase() {
+
+    @Mock private lateinit var callback: DreamCallbackController.DreamCallback
+
+    private lateinit var underTest: DreamCallbackController
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        underTest = DreamCallbackController()
+    }
+
+    @Test
+    fun testOnWakeUpInvokesCallback() {
+        underTest.addCallback(callback)
+        underTest.onWakeUp()
+        verify(callback).onWakeUp()
+
+        // Adding twice should not invoke twice
+        reset(callback)
+        underTest.addCallback(callback)
+        underTest.onWakeUp()
+        verify(callback, times(1)).onWakeUp()
+
+        // After remove, no call to callback
+        reset(callback)
+        underTest.removeCallback(callback)
+        underTest.onWakeUp()
+        verify(callback, never()).onWakeUp()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
index 8e689cf..6c23254 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
@@ -1,18 +1,25 @@
 package com.android.systemui.dreams
 
-import android.animation.Animator
 import android.animation.AnimatorSet
 import android.testing.AndroidTestingRunner
+import android.view.View
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dreams.complication.ComplicationHostViewController
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
 import com.android.systemui.statusbar.BlurUtils
-import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.eq
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
@@ -28,14 +35,6 @@
         private const val DREAM_IN_COMPLICATIONS_ANIMATION_DURATION = 3L
         private const val DREAM_IN_TRANSLATION_Y_DISTANCE = 6
         private const val DREAM_IN_TRANSLATION_Y_DURATION = 7L
-        private const val DREAM_OUT_TRANSLATION_Y_DISTANCE = 6
-        private const val DREAM_OUT_TRANSLATION_Y_DURATION = 7L
-        private const val DREAM_OUT_TRANSLATION_Y_DELAY_BOTTOM = 8L
-        private const val DREAM_OUT_TRANSLATION_Y_DELAY_TOP = 9L
-        private const val DREAM_OUT_ALPHA_DURATION = 10L
-        private const val DREAM_OUT_ALPHA_DELAY_BOTTOM = 11L
-        private const val DREAM_OUT_ALPHA_DELAY_TOP = 12L
-        private const val DREAM_OUT_BLUR_DURATION = 13L
     }
 
     @Mock private lateinit var mockAnimator: AnimatorSet
@@ -43,6 +42,8 @@
     @Mock private lateinit var hostViewController: ComplicationHostViewController
     @Mock private lateinit var statusBarViewController: DreamOverlayStatusBarViewController
     @Mock private lateinit var stateController: DreamOverlayStateController
+    @Mock private lateinit var configController: ConfigurationController
+    @Mock private lateinit var transitionViewModel: DreamingToLockscreenTransitionViewModel
     private lateinit var controller: DreamOverlayAnimationsController
 
     @Before
@@ -55,71 +56,48 @@
                 statusBarViewController,
                 stateController,
                 DREAM_BLUR_RADIUS,
+                transitionViewModel,
+                configController,
                 DREAM_IN_BLUR_ANIMATION_DURATION,
                 DREAM_IN_COMPLICATIONS_ANIMATION_DURATION,
                 DREAM_IN_TRANSLATION_Y_DISTANCE,
                 DREAM_IN_TRANSLATION_Y_DURATION,
-                DREAM_OUT_TRANSLATION_Y_DISTANCE,
-                DREAM_OUT_TRANSLATION_Y_DURATION,
-                DREAM_OUT_TRANSLATION_Y_DELAY_BOTTOM,
-                DREAM_OUT_TRANSLATION_Y_DELAY_TOP,
-                DREAM_OUT_ALPHA_DURATION,
-                DREAM_OUT_ALPHA_DELAY_BOTTOM,
-                DREAM_OUT_ALPHA_DELAY_TOP,
-                DREAM_OUT_BLUR_DURATION
             )
+
+        val mockView: View = mock()
+        whenever(mockView.resources).thenReturn(mContext.resources)
+
+        runBlocking(Dispatchers.Main.immediate) { controller.init(mockView) }
     }
 
     @Test
-    fun testExitAnimationOnEnd() {
-        val mockCallback: () -> Unit = mock()
+    fun testWakeUpCallsExecutor() {
+        val mockExecutor: DelayableExecutor = mock()
+        val mockCallback: Runnable = mock()
 
-        controller.startExitAnimations(
-            view = mock(),
+        controller.wakeUp(
             doneCallback = mockCallback,
-            animatorBuilder = { mockAnimator }
+            executor = mockExecutor,
         )
 
-        val captor = argumentCaptor<Animator.AnimatorListener>()
-        verify(mockAnimator).addListener(captor.capture())
-        val listener = captor.value
-
-        verify(mockCallback, never()).invoke()
-        listener.onAnimationEnd(mockAnimator)
-        verify(mockCallback, times(1)).invoke()
+        verify(mockExecutor).executeDelayed(eq(mockCallback), anyLong())
     }
 
     @Test
-    fun testCancellation() {
-        controller.startExitAnimations(
-            view = mock(),
-            doneCallback = mock(),
-            animatorBuilder = { mockAnimator }
-        )
-
-        verify(mockAnimator, never()).cancel()
-        controller.cancelAnimations()
-        verify(mockAnimator, times(1)).cancel()
-    }
-
-    @Test
-    fun testExitAfterStartWillCancel() {
+    fun testWakeUpAfterStartWillCancel() {
         val mockStartAnimator: AnimatorSet = mock()
-        val mockExitAnimator: AnimatorSet = mock()
 
-        controller.startEntryAnimations(view = mock(), animatorBuilder = { mockStartAnimator })
+        controller.startEntryAnimations(animatorBuilder = { mockStartAnimator })
 
         verify(mockStartAnimator, never()).cancel()
 
-        controller.startExitAnimations(
-            view = mock(),
+        controller.wakeUp(
             doneCallback = mock(),
-            animatorBuilder = { mockExitAnimator }
+            executor = mock(),
         )
 
         // Verify that we cancelled the start animator in favor of the exit
         // animator.
         verify(mockStartAnimator, times(1)).cancel()
-        verify(mockExitAnimator, never()).cancel()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index 73c226d..2799a25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -203,7 +203,7 @@
 
         mController.onViewAttached();
 
-        verify(mAnimationsController).startEntryAnimations(mDreamOverlayContainerView);
+        verify(mAnimationsController).startEntryAnimations();
         verify(mAnimationsController, never()).cancelAnimations();
     }
 
@@ -213,7 +213,7 @@
 
         mController.onViewAttached();
 
-        verify(mAnimationsController, never()).startEntryAnimations(mDreamOverlayContainerView);
+        verify(mAnimationsController, never()).startEntryAnimations();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 430575c..52663ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -139,6 +139,9 @@
     @Mock
     UiEventLogger mUiEventLogger;
 
+    @Mock
+    DreamCallbackController mDreamCallbackController;
+
     @Captor
     ArgumentCaptor<View> mViewCaptor;
 
@@ -172,8 +175,8 @@
 
         mService = new DreamOverlayService(
                 mContext,
-                mMainExecutor,
                 mLifecycleOwner,
+                mMainExecutor,
                 mWindowManager,
                 mComplicationComponentFactory,
                 mDreamComplicationComponentFactory,
@@ -182,7 +185,8 @@
                 mKeyguardUpdateMonitor,
                 mUiEventLogger,
                 mTouchInsetManager,
-                LOW_LIGHT_COMPONENT);
+                LOW_LIGHT_COMPONENT,
+                mDreamCallbackController);
     }
 
     @Test
@@ -394,6 +398,7 @@
         mService.onWakeUp(callback);
         mMainExecutor.runAllReady();
         verify(mDreamOverlayContainerViewController).wakeUp(callback, mMainExecutor);
+        verify(mDreamCallbackController).onWakeUp();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 5deac19..563d44d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -28,6 +28,8 @@
 import com.android.systemui.doze.DozeMachine
 import com.android.systemui.doze.DozeTransitionCallback
 import com.android.systemui.doze.DozeTransitionListener
+import com.android.systemui.dreams.DreamCallbackController
+import com.android.systemui.dreams.DreamCallbackController.DreamCallback
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
@@ -66,6 +68,7 @@
     @Mock private lateinit var dozeTransitionListener: DozeTransitionListener
     @Mock private lateinit var authController: AuthController
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+    @Mock private lateinit var dreamCallbackController: DreamCallbackController
 
     private lateinit var underTest: KeyguardRepositoryImpl
 
@@ -83,6 +86,7 @@
                 keyguardUpdateMonitor,
                 dozeTransitionListener,
                 authController,
+                dreamCallbackController,
             )
     }
 
@@ -318,7 +322,7 @@
         }
 
     @Test
-    fun isDreaming() =
+    fun isDreamingFromKeyguardUpdateMonitor() =
         runTest(UnconfinedTestDispatcher()) {
             whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false)
             var latest: Boolean? = null
@@ -339,6 +343,26 @@
         }
 
     @Test
+    fun isDreamingFromDreamCallbackController() =
+        runTest(UnconfinedTestDispatcher()) {
+            whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(true)
+            var latest: Boolean? = null
+            val job = underTest.isDreaming.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isTrue()
+
+            val listener =
+                withArgCaptor<DreamCallbackController.DreamCallback> {
+                    verify(dreamCallbackController).addCallback(capture())
+                }
+
+            listener.onWakeUp()
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
     fun biometricUnlockState() =
         runTest(UnconfinedTestDispatcher()) {
             val values = mutableListOf<BiometricUnlockModel>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
new file mode 100644
index 0000000..81950f1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.DreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.AnimationParams
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel.Companion.DREAM_OVERLAY_ALPHA
+import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel.Companion.DREAM_OVERLAY_TRANSLATION_Y
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
+    private lateinit var underTest: DreamingToLockscreenTransitionViewModel
+    private lateinit var repository: FakeKeyguardTransitionRepository
+
+    @Before
+    fun setUp() {
+        repository = FakeKeyguardTransitionRepository()
+        val interactor = KeyguardTransitionInteractor(repository)
+        underTest = DreamingToLockscreenTransitionViewModel(interactor)
+    }
+
+    @Test
+    fun dreamOverlayTranslationY() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<Float>()
+
+            val pixels = 100
+            val job =
+                underTest.dreamOverlayTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
+
+            repository.sendTransitionStep(step(0f))
+            repository.sendTransitionStep(step(0.3f))
+            repository.sendTransitionStep(step(0.5f))
+            repository.sendTransitionStep(step(1f))
+
+            // Only two values should be present, since the dream overlay runs for a small fraction
+            // of
+            // the overall animation time
+            assertThat(values.size).isEqualTo(2)
+            assertThat(values[0])
+                .isEqualTo(
+                    EMPHASIZED_ACCELERATE.getInterpolation(
+                        animValue(0f, DREAM_OVERLAY_TRANSLATION_Y)
+                    ) * pixels
+                )
+            assertThat(values[1])
+                .isEqualTo(
+                    EMPHASIZED_ACCELERATE.getInterpolation(
+                        animValue(0.3f, DREAM_OVERLAY_TRANSLATION_Y)
+                    ) * pixels
+                )
+
+            job.cancel()
+        }
+
+    @Test
+    fun dreamOverlayFadeOut() =
+        runTest(UnconfinedTestDispatcher()) {
+            val values = mutableListOf<Float>()
+
+            val job = underTest.dreamOverlayAlpha.onEach { values.add(it) }.launchIn(this)
+
+            repository.sendTransitionStep(step(0f))
+            repository.sendTransitionStep(step(0.1f))
+            repository.sendTransitionStep(step(0.5f))
+            repository.sendTransitionStep(step(1f))
+
+            // Only two values should be present, since the dream overlay runs for a small fraction
+            // of
+            // the overall animation time
+            assertThat(values.size).isEqualTo(2)
+            assertThat(values[0]).isEqualTo(1f - animValue(0f, DREAM_OVERLAY_ALPHA))
+            assertThat(values[1]).isEqualTo(1f - animValue(0.1f, DREAM_OVERLAY_ALPHA))
+
+            job.cancel()
+        }
+
+    private fun animValue(stepValue: Float, params: AnimationParams): Float {
+        val totalDuration = TO_LOCKSCREEN_DURATION
+        val startValue = (params.startTime / totalDuration).toFloat()
+
+        val multiplier = (totalDuration / params.duration).toFloat()
+        return (stepValue - startValue) * multiplier
+    }
+
+    private fun step(value: Float): TransitionStep {
+        return TransitionStep(
+            from = KeyguardState.DREAMING,
+            to = KeyguardState.LOCKSCREEN,
+            value = value,
+            transitionState = TransitionState.RUNNING,
+            ownerName = "DreamingToLockscreenTransitionViewModelTest"
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
index bad3f03..07a3109 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
@@ -22,6 +22,7 @@
 import android.view.ViewGroup
 import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
 import com.android.systemui.media.taptotransfer.common.MediaTttLogger
 import com.android.systemui.statusbar.CommandQueue
@@ -39,6 +40,7 @@
     mainExecutor: DelayableExecutor,
     accessibilityManager: AccessibilityManager,
     configurationController: ConfigurationController,
+    dumpManager: DumpManager,
     powerManager: PowerManager,
     mainHandler: Handler,
     mediaTttFlags: MediaTttFlags,
@@ -55,6 +57,7 @@
         mainExecutor,
         accessibilityManager,
         configurationController,
+        dumpManager,
         powerManager,
         mainHandler,
         mediaTttFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index ef0bfb7..03ba3d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -34,6 +34,7 @@
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
 import com.android.systemui.media.taptotransfer.common.MediaTttLogger
 import com.android.systemui.statusbar.CommandQueue
@@ -73,6 +74,8 @@
     @Mock
     private lateinit var configurationController: ConfigurationController
     @Mock
+    private lateinit var dumpManager: DumpManager
+    @Mock
     private lateinit var mediaTttFlags: MediaTttFlags
     @Mock
     private lateinit var powerManager: PowerManager
@@ -122,6 +125,7 @@
             fakeExecutor,
             accessibilityManager,
             configurationController,
+            dumpManager,
             powerManager,
             Handler.getMain(),
             mediaTttFlags,
@@ -150,6 +154,7 @@
             FakeExecutor(FakeSystemClock()),
             accessibilityManager,
             configurationController,
+            dumpManager,
             powerManager,
             Handler.getMain(),
             mediaTttFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index b03a545..4cc12c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.common.shared.model.Text.Companion.loadText
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.taptotransfer.MediaTttFlags
 import com.android.systemui.media.taptotransfer.common.MediaTttLogger
 import com.android.systemui.plugins.FalsingManager
@@ -81,6 +82,7 @@
     @Mock private lateinit var applicationInfo: ApplicationInfo
     @Mock private lateinit var commandQueue: CommandQueue
     @Mock private lateinit var configurationController: ConfigurationController
+    @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var falsingManager: FalsingManager
     @Mock private lateinit var falsingCollector: FalsingCollector
     @Mock private lateinit var chipbarLogger: ChipbarLogger
@@ -137,6 +139,7 @@
                 fakeExecutor,
                 accessibilityManager,
                 configurationController,
+                dumpManager,
                 powerManager,
                 falsingManager,
                 falsingCollector,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index 01411c9..0b9fbd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -84,7 +84,7 @@
                     ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
                 )
             )
-        assertThat(settings.background).isEqualTo(R.drawable.qs_footer_action_circle)
+        assertThat(settings.backgroundColor).isEqualTo(R.attr.offStateColor)
         assertThat(settings.iconTint).isNull()
     }
 
@@ -105,7 +105,7 @@
                     ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
                 )
             )
-        assertThat(power.background).isEqualTo(R.drawable.qs_footer_action_circle_color)
+        assertThat(power.backgroundColor).isEqualTo(com.android.internal.R.attr.colorAccent)
         assertThat(power.iconTint)
             .isEqualTo(
                 Utils.getColorAttrDefaultColor(
@@ -170,7 +170,7 @@
         assertThat(userSwitcher).isNotNull()
         assertThat(userSwitcher!!.icon)
             .isEqualTo(Icon.Loaded(picture, ContentDescription.Loaded("Signed in as foo")))
-        assertThat(userSwitcher.background).isEqualTo(R.drawable.qs_footer_action_circle)
+        assertThat(userSwitcher.backgroundColor).isEqualTo(R.attr.offStateColor)
 
         // Change the current user name.
         userSwitcherControllerWrapper.currentUserName = "bar"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 0dac9a2..942b6ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -675,7 +675,6 @@
         createController();
         String message = mContext.getString(R.string.keyguard_retry);
         when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
-        when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(false);
         when(mKeyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true);
 
         mController.setVisible(true);
@@ -686,21 +685,6 @@
     }
 
     @Test
-    public void transientIndication_swipeUpToRetry_faceAuthenticated() {
-        createController();
-        String message = mContext.getString(R.string.keyguard_retry);
-        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
-        when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
-        when(mKeyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true);
-
-        mController.setVisible(true);
-        mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
-                "A message", BiometricSourceType.FACE);
-
-        verify(mStatusBarKeyguardViewManager, never()).setKeyguardMessage(eq(message), any());
-    }
-
-    @Test
     public void faceErrorTimeout_whenFingerprintEnrolled_doesNotShowMessage() {
         createController();
         when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 07ea630..7622549 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -20,6 +20,7 @@
 
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.RUBBER_BAND_FACTOR_NORMAL;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -47,6 +48,7 @@
 import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.testing.TestableResources;
 import android.util.MathUtils;
 import android.view.MotionEvent;
 import android.view.View;
@@ -99,6 +101,7 @@
     private NotificationStackScrollLayout mStackScroller;  // Normally test this
     private NotificationStackScrollLayout mStackScrollerInternal;  // See explanation below
     private AmbientState mAmbientState;
+    private TestableResources mTestableResources;
 
     @Rule public MockitoRule mockito = MockitoJUnit.rule();
     @Mock private CentralSurfaces mCentralSurfaces;
@@ -123,6 +126,7 @@
     @UiThreadTest
     public void setUp() throws Exception {
         allowTestableLooperAsMainThread();
+        mTestableResources = mContext.getOrCreateTestableResources();
 
         // Interact with real instance of AmbientState.
         mAmbientState = spy(new AmbientState(
@@ -394,7 +398,7 @@
     @Test
     @UiThreadTest
     public void testSetExpandedHeight_withSplitShade_doesntInterpolateStackHeight() {
-        mContext.getOrCreateTestableResources()
+        mTestableResources
                 .addOverride(R.bool.config_use_split_notification_shade, /* value= */ true);
         final int[] expectedStackHeight = {0};
 
@@ -405,12 +409,12 @@
                     .isEqualTo(expectedStackHeight[0]);
         });
 
-        mContext.getOrCreateTestableResources()
+        mTestableResources
                 .addOverride(R.bool.config_use_split_notification_shade, /* value= */ false);
         expectedStackHeight[0] = 0;
         mStackScroller.setExpandedHeight(100f);
 
-        mContext.getOrCreateTestableResources()
+        mTestableResources
                 .addOverride(R.bool.config_use_split_notification_shade, /* value= */ true);
         expectedStackHeight[0] = 100;
         mStackScroller.setExpandedHeight(100f);
@@ -781,6 +785,39 @@
         assertEquals(mAmbientState.getScrollY(), 0);
     }
 
+    @Test
+    public void testSplitShade_hasTopOverscroll() {
+        mTestableResources
+                .addOverride(R.bool.config_use_split_notification_shade, /* value= */ true);
+        mStackScroller.updateSplitNotificationShade();
+        mAmbientState.setExpansionFraction(1f);
+
+        int topOverscrollPixels = 100;
+        mStackScroller.setOverScrolledPixels(topOverscrollPixels, true, false);
+
+        float expectedTopOverscrollAmount = topOverscrollPixels * RUBBER_BAND_FACTOR_NORMAL;
+        assertEquals(expectedTopOverscrollAmount, mStackScroller.getCurrentOverScrollAmount(true));
+        assertEquals(expectedTopOverscrollAmount, mAmbientState.getStackY());
+    }
+
+    @Test
+    public void testNormalShade_hasNoTopOverscroll() {
+        mTestableResources
+                .addOverride(R.bool.config_use_split_notification_shade, /* value= */ false);
+        mStackScroller.updateSplitNotificationShade();
+        mAmbientState.setExpansionFraction(1f);
+
+        int topOverscrollPixels = 100;
+        mStackScroller.setOverScrolledPixels(topOverscrollPixels, true, false);
+
+        float expectedTopOverscrollAmount = topOverscrollPixels * RUBBER_BAND_FACTOR_NORMAL;
+        assertEquals(expectedTopOverscrollAmount, mStackScroller.getCurrentOverScrollAmount(true));
+        // When not in split shade mode, then the overscroll effect is handled in
+        // NotificationPanelViewController and not in NotificationStackScrollLayout. Therefore
+        // mAmbientState must have stackY set to 0
+        assertEquals(0f, mAmbientState.getStackY());
+    }
+
     private void setBarStateForTest(int state) {
         // Can't inject this through the listener or we end up on the actual implementation
         // rather than the mock because the spy just coppied the anonymous inner /shruggie.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
index 5265ec6..7b9929d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
@@ -30,6 +30,8 @@
     private val _isDefaultDataSubscription = MutableStateFlow(true)
     override val isDefaultDataSubscription = _isDefaultDataSubscription
 
+    override val cdmaRoaming = MutableStateFlow(false)
+
     fun setConnectionInfo(model: MobileConnectionModel) {
         _connectionInfo.value = model
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
index e943de2..b2423da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt
@@ -95,6 +95,7 @@
                     inflateStrength = testCase.inflateStrength,
                     activity = testCase.activity,
                     carrierNetworkChange = testCase.carrierNetworkChange,
+                    roaming = testCase.roaming,
                 )
 
             fakeNetworkEventFlow.value = networkModel
@@ -116,6 +117,7 @@
                 assertThat(connectionInfo.dataActivityDirection).isEqualTo(model.activity)
                 assertThat(connectionInfo.carrierNetworkChangeActive)
                     .isEqualTo(model.carrierNetworkChange)
+                assertThat(connectionInfo.isRoaming).isEqualTo(model.roaming)
 
                 // TODO(b/261029387): check these once we start handling them
                 assertThat(connectionInfo.isEmergencyOnly).isFalse()
@@ -138,6 +140,7 @@
         val inflateStrength: Boolean,
         @Annotation.DataActivityType val activity: Int,
         val carrierNetworkChange: Boolean,
+        val roaming: Boolean,
     ) {
         override fun toString(): String {
             return "INPUT(level=$level, " +
@@ -146,7 +149,8 @@
                 "carrierId=$carrierId, " +
                 "inflateStrength=$inflateStrength, " +
                 "activity=$activity, " +
-                "carrierNetworkChange=$carrierNetworkChange)"
+                "carrierNetworkChange=$carrierNetworkChange, " +
+                "roaming=$roaming)"
         }
 
         // Convenience for iterating test data and creating new cases
@@ -158,6 +162,7 @@
             inflateStrength: Boolean? = null,
             @Annotation.DataActivityType activity: Int? = null,
             carrierNetworkChange: Boolean? = null,
+            roaming: Boolean? = null,
         ): TestCase =
             TestCase(
                 level = level ?: this.level,
@@ -166,7 +171,8 @@
                 carrierId = carrierId ?: this.carrierId,
                 inflateStrength = inflateStrength ?: this.inflateStrength,
                 activity = activity ?: this.activity,
-                carrierNetworkChange = carrierNetworkChange ?: this.carrierNetworkChange
+                carrierNetworkChange = carrierNetworkChange ?: this.carrierNetworkChange,
+                roaming = roaming ?: this.roaming,
             )
     }
 
@@ -193,6 +199,8 @@
                 TelephonyManager.DATA_ACTIVITY_INOUT
             )
         private val carrierNetworkChange = booleanList
+        // false first so the base case doesn't have roaming set (more common)
+        private val roaming = listOf(false, true)
 
         @Parameters(name = "{0}") @JvmStatic fun data() = testData()
 
@@ -226,7 +234,8 @@
                     carrierIds.first(),
                     inflateStrength.first(),
                     activity.first(),
-                    carrierNetworkChange.first()
+                    carrierNetworkChange.first(),
+                    roaming.first(),
                 )
 
             val tail =
@@ -237,6 +246,7 @@
                         inflateStrength.map { baseCase.modifiedBy(inflateStrength = it) },
                         activity.map { baseCase.modifiedBy(activity = it) },
                         carrierNetworkChange.map { baseCase.modifiedBy(carrierNetworkChange = it) },
+                        roaming.map { baseCase.modifiedBy(roaming = it) }
                     )
                     .flatten()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
index 32d0410..e4f29e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepositoryTest.kt
@@ -292,6 +292,7 @@
                 assertThat(connectionInfo.dataActivityDirection).isEqualTo(model.activity)
                 assertThat(connectionInfo.carrierNetworkChangeActive)
                     .isEqualTo(model.carrierNetworkChange)
+                assertThat(connectionInfo.isRoaming).isEqualTo(model.roaming)
 
                 // TODO(b/261029387) check these once we start handling them
                 assertThat(connectionInfo.isEmergencyOnly).isFalse()
@@ -313,6 +314,7 @@
     inflateStrength: Boolean? = false,
     activity: Int? = null,
     carrierNetworkChange: Boolean = false,
+    roaming: Boolean = false,
 ): FakeNetworkEventModel =
     FakeNetworkEventModel.Mobile(
         level = level,
@@ -322,4 +324,5 @@
         inflateStrength = inflateStrength,
         activity = activity,
         carrierNetworkChange = carrierNetworkChange,
+        roaming = roaming,
     )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 1fc9c60..0b3e5b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -32,6 +32,8 @@
 import android.telephony.TelephonyManager.DATA_DISCONNECTED
 import android.telephony.TelephonyManager.DATA_DISCONNECTING
 import android.telephony.TelephonyManager.DATA_UNKNOWN
+import android.telephony.TelephonyManager.ERI_OFF
+import android.telephony.TelephonyManager.ERI_ON
 import android.telephony.TelephonyManager.NETWORK_TYPE_LTE
 import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
 import androidx.test.filters.SmallTest
@@ -402,6 +404,61 @@
             job.cancel()
         }
 
+    @Test
+    fun `roaming - cdma - queries telephony manager`() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            // Start the telephony collection job so that cdmaRoaming starts updating
+            val telephonyJob = underTest.connectionInfo.launchIn(this)
+            val job = underTest.cdmaRoaming.onEach { latest = it }.launchIn(this)
+
+            val cb = getTelephonyCallbackForType<ServiceStateListener>()
+
+            val serviceState = ServiceState()
+            serviceState.roaming = false
+
+            // CDMA roaming is off, GSM roaming is off
+            whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_OFF)
+            cb.onServiceStateChanged(serviceState)
+
+            assertThat(latest).isFalse()
+
+            // CDMA roaming is off, GSM roaming is on
+            whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_ON)
+            cb.onServiceStateChanged(serviceState)
+
+            assertThat(latest).isTrue()
+
+            telephonyJob.cancel()
+            job.cancel()
+        }
+
+    @Test
+    fun `roaming - gsm - queries service state`() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.connectionInfo.onEach { latest = it.isRoaming }.launchIn(this)
+
+            val serviceState = ServiceState()
+            serviceState.roaming = false
+
+            val cb = getTelephonyCallbackForType<ServiceStateListener>()
+
+            // CDMA roaming is off, GSM roaming is off
+            whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_OFF)
+            cb.onServiceStateChanged(serviceState)
+
+            assertThat(latest).isFalse()
+
+            // CDMA roaming is off, GSM roaming is on
+            serviceState.roaming = true
+            cb.onServiceStateChanged(serviceState)
+
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
     private fun getTelephonyCallbacks(): List<TelephonyCallback> {
         val callbackCaptor = argumentCaptor<TelephonyCallback>()
         Mockito.verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
index 1ff1636a..0e2c38e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
@@ -30,6 +30,8 @@
     private val _isEmergencyOnly = MutableStateFlow(false)
     override val isEmergencyOnly = _isEmergencyOnly
 
+    override val isRoaming = MutableStateFlow(false)
+
     private val _isFailedConnection = MutableStateFlow(false)
     override val isDefaultConnectionFailed = _isFailedConnection
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index 2281e89b..9b6f6df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -298,6 +298,100 @@
             job.cancel()
         }
 
+    @Test
+    fun `roaming - is gsm - uses connection model`() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
+
+            connectionRepository.cdmaRoaming.value = true
+            connectionRepository.setConnectionInfo(
+                MobileConnectionModel(
+                    isGsm = true,
+                    isRoaming = false,
+                )
+            )
+            yield()
+
+            assertThat(latest).isFalse()
+
+            connectionRepository.setConnectionInfo(
+                MobileConnectionModel(
+                    isGsm = true,
+                    isRoaming = true,
+                )
+            )
+            yield()
+
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun `roaming - is cdma - uses cdma roaming bit`() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
+
+            connectionRepository.cdmaRoaming.value = false
+            connectionRepository.setConnectionInfo(
+                MobileConnectionModel(
+                    isGsm = false,
+                    isRoaming = true,
+                )
+            )
+            yield()
+
+            assertThat(latest).isFalse()
+
+            connectionRepository.cdmaRoaming.value = true
+            connectionRepository.setConnectionInfo(
+                MobileConnectionModel(
+                    isGsm = false,
+                    isRoaming = false,
+                )
+            )
+            yield()
+
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun `roaming - false while carrierNetworkChangeActive`() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
+
+            connectionRepository.cdmaRoaming.value = true
+            connectionRepository.setConnectionInfo(
+                MobileConnectionModel(
+                    isGsm = false,
+                    isRoaming = true,
+                    carrierNetworkChangeActive = true,
+                )
+            )
+            yield()
+
+            assertThat(latest).isFalse()
+
+            connectionRepository.cdmaRoaming.value = true
+            connectionRepository.setConnectionInfo(
+                MobileConnectionModel(
+                    isGsm = true,
+                    isRoaming = true,
+                    carrierNetworkChangeActive = true,
+                )
+            )
+            yield()
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
     companion object {
         private val IMMEDIATE = Dispatchers.Main.immediate
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index f2533a9..2c8f0a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -234,6 +234,22 @@
             job.cancel()
         }
 
+    @Test
+    fun roaming() =
+        runBlocking(IMMEDIATE) {
+            interactor.isRoaming.value = true
+            var latest: Boolean? = null
+            val job = underTest.roaming.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isTrue()
+
+            interactor.isRoaming.value = false
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
     /** Convenience constructor for these tests */
     private fun defaultSignal(
         level: Int = 1,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
index 2f18ce3..4e15b4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
@@ -16,9 +16,9 @@
 
 package com.android.systemui.statusbar.pipeline.wifi.data.repository
 
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
-import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiActivityModel
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 
@@ -35,7 +35,7 @@
     override val wifiNetwork: StateFlow<WifiNetworkModel> = _wifiNetwork
 
     private val _wifiActivity = MutableStateFlow(ACTIVITY_DEFAULT)
-    override val wifiActivity: StateFlow<WifiActivityModel> = _wifiActivity
+    override val wifiActivity: StateFlow<DataActivityModel> = _wifiActivity
 
     fun setIsWifiEnabled(enabled: Boolean) {
         _isWifiEnabled.value = enabled
@@ -49,7 +49,7 @@
         _wifiNetwork.value = wifiNetworkModel
     }
 
-    fun setWifiActivity(activity: WifiActivityModel) {
+    fun setWifiActivity(activity: DataActivityModel) {
         _wifiActivity.value = activity
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
index 800f3c0..5d0d87b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
@@ -31,10 +31,10 @@
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
-import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiActivityModel
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
@@ -724,7 +724,7 @@
     fun wifiActivity_nullWifiManager_receivesDefault() = runBlocking(IMMEDIATE) {
         underTest = createRepo(wifiManagerToUse = null)
 
-        var latest: WifiActivityModel? = null
+        var latest: DataActivityModel? = null
         val job = underTest
                 .wifiActivity
                 .onEach { latest = it }
@@ -737,7 +737,7 @@
 
     @Test
     fun wifiActivity_callbackGivesNone_activityFlowHasNone() = runBlocking(IMMEDIATE) {
-        var latest: WifiActivityModel? = null
+        var latest: DataActivityModel? = null
         val job = underTest
                 .wifiActivity
                 .onEach { latest = it }
@@ -746,7 +746,7 @@
         getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_NONE)
 
         assertThat(latest).isEqualTo(
-            WifiActivityModel(hasActivityIn = false, hasActivityOut = false)
+            DataActivityModel(hasActivityIn = false, hasActivityOut = false)
         )
 
         job.cancel()
@@ -754,7 +754,7 @@
 
     @Test
     fun wifiActivity_callbackGivesIn_activityFlowHasIn() = runBlocking(IMMEDIATE) {
-        var latest: WifiActivityModel? = null
+        var latest: DataActivityModel? = null
         val job = underTest
                 .wifiActivity
                 .onEach { latest = it }
@@ -763,7 +763,7 @@
         getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_IN)
 
         assertThat(latest).isEqualTo(
-            WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
+            DataActivityModel(hasActivityIn = true, hasActivityOut = false)
         )
 
         job.cancel()
@@ -771,7 +771,7 @@
 
     @Test
     fun wifiActivity_callbackGivesOut_activityFlowHasOut() = runBlocking(IMMEDIATE) {
-        var latest: WifiActivityModel? = null
+        var latest: DataActivityModel? = null
         val job = underTest
                 .wifiActivity
                 .onEach { latest = it }
@@ -780,7 +780,7 @@
         getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_OUT)
 
         assertThat(latest).isEqualTo(
-            WifiActivityModel(hasActivityIn = false, hasActivityOut = true)
+            DataActivityModel(hasActivityIn = false, hasActivityOut = true)
         )
 
         job.cancel()
@@ -788,7 +788,7 @@
 
     @Test
     fun wifiActivity_callbackGivesInout_activityFlowHasInAndOut() = runBlocking(IMMEDIATE) {
-        var latest: WifiActivityModel? = null
+        var latest: DataActivityModel? = null
         val job = underTest
                 .wifiActivity
                 .onEach { latest = it }
@@ -796,7 +796,7 @@
 
         getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT)
 
-        assertThat(latest).isEqualTo(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
+        assertThat(latest).isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = true))
 
         job.cancel()
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
index b38497a..2ecb17b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
@@ -20,10 +20,10 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
-import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiActivityModel
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -225,23 +225,23 @@
 
     @Test
     fun activity_matchesRepoWifiActivity() = runBlocking(IMMEDIATE) {
-        var latest: WifiActivityModel? = null
+        var latest: DataActivityModel? = null
         val job = underTest
             .activity
             .onEach { latest = it }
             .launchIn(this)
 
-        val activity1 = WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
+        val activity1 = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
         wifiRepository.setWifiActivity(activity1)
         yield()
         assertThat(latest).isEqualTo(activity1)
 
-        val activity2 = WifiActivityModel(hasActivityIn = false, hasActivityOut = false)
+        val activity2 = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
         wifiRepository.setWifiActivity(activity2)
         yield()
         assertThat(latest).isEqualTo(activity2)
 
-        val activity3 = WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
+        val activity3 = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
         wifiRepository.setWifiActivity(activity3)
         yield()
         assertThat(latest).isEqualTo(activity3)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 7502020..b47f177 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -27,13 +27,13 @@
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
 import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
-import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiActivityModel
 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
@@ -209,7 +209,7 @@
             .launchIn(this)
 
         // WHEN we update the repo to have activity
-        val activity = WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
+        val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
         wifiRepository.setWifiActivity(activity)
         yield()
 
@@ -252,7 +252,7 @@
             .launchIn(this)
 
         // WHEN we update the repo to have activity
-        val activity = WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
+        val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
         wifiRepository.setWifiActivity(activity)
         yield()
 
@@ -293,7 +293,7 @@
             .onEach { latestQs = it }
             .launchIn(this)
 
-        val activity = WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
+        val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
         wifiRepository.setWifiActivity(activity)
         yield()
 
@@ -319,7 +319,7 @@
             .onEach { latest = it }
             .launchIn(this)
 
-        val activity = WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
+        val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
         wifiRepository.setWifiActivity(activity)
         yield()
 
@@ -341,7 +341,7 @@
             .onEach { latest = it }
             .launchIn(this)
 
-        val activity = WifiActivityModel(hasActivityIn = false, hasActivityOut = true)
+        val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
         wifiRepository.setWifiActivity(activity)
         yield()
 
@@ -363,7 +363,7 @@
             .onEach { latest = it }
             .launchIn(this)
 
-        val activity = WifiActivityModel(hasActivityIn = false, hasActivityOut = true)
+        val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
         wifiRepository.setWifiActivity(activity)
         yield()
 
@@ -385,7 +385,7 @@
             .onEach { latest = it }
             .launchIn(this)
 
-        val activity = WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
+        val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
         wifiRepository.setWifiActivity(activity)
         yield()
 
@@ -407,7 +407,7 @@
             .onEach { latest = it }
             .launchIn(this)
 
-        val activity = WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
+        val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = false)
         wifiRepository.setWifiActivity(activity)
         yield()
 
@@ -429,7 +429,7 @@
             .onEach { latest = it }
             .launchIn(this)
 
-        val activity = WifiActivityModel(hasActivityIn = false, hasActivityOut = true)
+        val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = true)
         wifiRepository.setWifiActivity(activity)
         yield()
 
@@ -451,7 +451,7 @@
             .onEach { latest = it }
             .launchIn(this)
 
-        val activity = WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
+        val activity = DataActivityModel(hasActivityIn = true, hasActivityOut = true)
         wifiRepository.setWifiActivity(activity)
         yield()
 
@@ -473,7 +473,7 @@
             .onEach { latest = it }
             .launchIn(this)
 
-        val activity = WifiActivityModel(hasActivityIn = false, hasActivityOut = false)
+        val activity = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
         wifiRepository.setWifiActivity(activity)
         yield()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusFirstUsageListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusFirstUsageListenerTest.kt
new file mode 100644
index 0000000..8dd088f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusFirstUsageListenerTest.kt
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.stylus
+
+import android.content.Context
+import android.hardware.BatteryState
+import android.hardware.input.InputManager
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.view.InputDevice
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@Ignore("TODO(b/20579491): unignore on main")
+class StylusFirstUsageListenerTest : SysuiTestCase() {
+    @Mock lateinit var context: Context
+    @Mock lateinit var inputManager: InputManager
+    @Mock lateinit var stylusManager: StylusManager
+    @Mock lateinit var featureFlags: FeatureFlags
+    @Mock lateinit var internalStylusDevice: InputDevice
+    @Mock lateinit var otherDevice: InputDevice
+    @Mock lateinit var externalStylusDevice: InputDevice
+    @Mock lateinit var batteryState: BatteryState
+    @Mock lateinit var handler: Handler
+
+    private lateinit var stylusListener: StylusFirstUsageListener
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        whenever(featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)).thenReturn(true)
+        whenever(inputManager.isStylusEverUsed(context)).thenReturn(false)
+
+        stylusListener =
+            StylusFirstUsageListener(
+                context,
+                inputManager,
+                stylusManager,
+                featureFlags,
+                EXECUTOR,
+                handler
+            )
+        stylusListener.hasStarted = false
+
+        whenever(handler.post(any())).thenAnswer {
+            (it.arguments[0] as Runnable).run()
+            true
+        }
+
+        whenever(otherDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(false)
+        whenever(internalStylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true)
+        whenever(internalStylusDevice.isExternal).thenReturn(false)
+        whenever(externalStylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true)
+        whenever(externalStylusDevice.isExternal).thenReturn(true)
+
+        whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf())
+        whenever(inputManager.getInputDevice(OTHER_DEVICE_ID)).thenReturn(otherDevice)
+        whenever(inputManager.getInputDevice(INTERNAL_STYLUS_DEVICE_ID))
+            .thenReturn(internalStylusDevice)
+        whenever(inputManager.getInputDevice(EXTERNAL_STYLUS_DEVICE_ID))
+            .thenReturn(externalStylusDevice)
+    }
+
+    @Test
+    fun start_flagDisabled_doesNotRegister() {
+        whenever(featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)).thenReturn(false)
+
+        stylusListener.start()
+
+        verify(stylusManager, never()).registerCallback(any())
+        verify(inputManager, never()).setStylusEverUsed(context, true)
+    }
+
+    @Test
+    fun start_toggleHasStarted() {
+        stylusListener.start()
+
+        assert(stylusListener.hasStarted)
+    }
+
+    @Test
+    fun start_hasStarted_doesNotRegister() {
+        stylusListener.hasStarted = true
+
+        stylusListener.start()
+
+        verify(stylusManager, never()).registerCallback(any())
+    }
+
+    @Test
+    fun start_hostDeviceDoesNotSupportStylus_doesNotRegister() {
+        whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(OTHER_DEVICE_ID))
+
+        stylusListener.start()
+
+        verify(stylusManager, never()).registerCallback(any())
+        verify(inputManager, never()).setStylusEverUsed(context, true)
+    }
+
+    @Test
+    fun start_stylusEverUsed_doesNotRegister() {
+        whenever(inputManager.inputDeviceIds)
+            .thenReturn(intArrayOf(OTHER_DEVICE_ID, INTERNAL_STYLUS_DEVICE_ID))
+        whenever(inputManager.isStylusEverUsed(context)).thenReturn(true)
+
+        stylusListener.start()
+
+        verify(stylusManager, never()).registerCallback(any())
+        verify(inputManager, never()).setStylusEverUsed(context, true)
+    }
+
+    @Test
+    fun start_hostDeviceSupportsStylus_registersListener() {
+        whenever(inputManager.inputDeviceIds)
+            .thenReturn(intArrayOf(OTHER_DEVICE_ID, INTERNAL_STYLUS_DEVICE_ID))
+
+        stylusListener.start()
+
+        verify(stylusManager).registerCallback(any())
+        verify(inputManager, never()).setStylusEverUsed(context, true)
+    }
+
+    @Test
+    fun onStylusAdded_hasNotStarted_doesNotRegisterListener() {
+        stylusListener.hasStarted = false
+
+        stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
+
+        verifyZeroInteractions(inputManager)
+    }
+
+    @Test
+    fun onStylusAdded_internalStylus_registersListener() {
+        stylusListener.hasStarted = true
+
+        stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
+
+        verify(inputManager, times(1))
+            .addInputDeviceBatteryListener(INTERNAL_STYLUS_DEVICE_ID, EXECUTOR, stylusListener)
+    }
+
+    @Test
+    fun onStylusAdded_externalStylus_doesNotRegisterListener() {
+        stylusListener.hasStarted = true
+
+        stylusListener.onStylusAdded(EXTERNAL_STYLUS_DEVICE_ID)
+
+        verify(inputManager, never()).addInputDeviceBatteryListener(any(), any(), any())
+    }
+
+    @Test
+    fun onStylusAdded_otherDevice_doesNotRegisterListener() {
+        stylusListener.onStylusAdded(OTHER_DEVICE_ID)
+
+        verify(inputManager, never()).addInputDeviceBatteryListener(any(), any(), any())
+    }
+
+    @Test
+    fun onStylusRemoved_registeredDevice_unregistersListener() {
+        stylusListener.hasStarted = true
+        stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
+
+        stylusListener.onStylusRemoved(INTERNAL_STYLUS_DEVICE_ID)
+
+        verify(inputManager, times(1))
+            .removeInputDeviceBatteryListener(INTERNAL_STYLUS_DEVICE_ID, stylusListener)
+    }
+
+    @Test
+    fun onStylusRemoved_hasNotStarted_doesNotUnregisterListener() {
+        stylusListener.hasStarted = false
+        stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
+
+        stylusListener.onStylusRemoved(INTERNAL_STYLUS_DEVICE_ID)
+
+        verifyZeroInteractions(inputManager)
+    }
+
+    @Test
+    fun onStylusRemoved_unregisteredDevice_doesNotUnregisterListener() {
+        stylusListener.hasStarted = true
+
+        stylusListener.onStylusRemoved(INTERNAL_STYLUS_DEVICE_ID)
+
+        verifyNoMoreInteractions(inputManager)
+    }
+
+    @Test
+    fun onStylusBluetoothConnected_updateStylusFlagAndUnregisters() {
+        stylusListener.hasStarted = true
+        stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
+
+        stylusListener.onStylusBluetoothConnected(EXTERNAL_STYLUS_DEVICE_ID, "ANY")
+
+        verify(inputManager).setStylusEverUsed(context, true)
+        verify(inputManager, times(1))
+            .removeInputDeviceBatteryListener(INTERNAL_STYLUS_DEVICE_ID, stylusListener)
+        verify(stylusManager).unregisterCallback(stylusListener)
+    }
+
+    @Test
+    fun onStylusBluetoothConnected_hasNotStarted_doesNoting() {
+        stylusListener.hasStarted = false
+        stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
+
+        stylusListener.onStylusBluetoothConnected(EXTERNAL_STYLUS_DEVICE_ID, "ANY")
+
+        verifyZeroInteractions(inputManager)
+        verifyZeroInteractions(stylusManager)
+    }
+
+    @Test
+    fun onBatteryStateChanged_batteryPresent_updateStylusFlagAndUnregisters() {
+        stylusListener.hasStarted = true
+        stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
+        whenever(batteryState.isPresent).thenReturn(true)
+
+        stylusListener.onBatteryStateChanged(0, 1, batteryState)
+
+        verify(inputManager).setStylusEverUsed(context, true)
+        verify(inputManager, times(1))
+            .removeInputDeviceBatteryListener(INTERNAL_STYLUS_DEVICE_ID, stylusListener)
+        verify(stylusManager).unregisterCallback(stylusListener)
+    }
+
+    @Test
+    fun onBatteryStateChanged_batteryNotPresent_doesNotUpdateFlagOrUnregister() {
+        stylusListener.hasStarted = true
+        stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
+        whenever(batteryState.isPresent).thenReturn(false)
+
+        stylusListener.onBatteryStateChanged(0, 1, batteryState)
+
+        verifyZeroInteractions(stylusManager)
+        verify(inputManager, never())
+            .removeInputDeviceBatteryListener(INTERNAL_STYLUS_DEVICE_ID, stylusListener)
+    }
+
+    @Test
+    fun onBatteryStateChanged_hasNotStarted_doesNothing() {
+        stylusListener.hasStarted = false
+        stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
+        whenever(batteryState.isPresent).thenReturn(false)
+
+        stylusListener.onBatteryStateChanged(0, 1, batteryState)
+
+        verifyZeroInteractions(inputManager)
+        verifyZeroInteractions(stylusManager)
+    }
+
+    companion object {
+        private const val OTHER_DEVICE_ID = 0
+        private const val INTERNAL_STYLUS_DEVICE_ID = 1
+        private const val EXTERNAL_STYLUS_DEVICE_ID = 2
+        private val EXECUTOR = FakeExecutor(FakeSystemClock())
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index 82153d5..99e2012 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
 import com.android.systemui.util.concurrency.DelayableExecutor
@@ -66,6 +67,8 @@
     @Mock
     private lateinit var configurationController: ConfigurationController
     @Mock
+    private lateinit var dumpManager: DumpManager
+    @Mock
     private lateinit var windowManager: WindowManager
     @Mock
     private lateinit var powerManager: PowerManager
@@ -91,6 +94,7 @@
             fakeExecutor,
             accessibilityManager,
             configurationController,
+            dumpManager,
             powerManager,
             fakeWakeLockBuilder,
             fakeClock,
@@ -989,6 +993,7 @@
         @Main mainExecutor: DelayableExecutor,
         accessibilityManager: AccessibilityManager,
         configurationController: ConfigurationController,
+        dumpManager: DumpManager,
         powerManager: PowerManager,
         wakeLockBuilder: WakeLock.Builder,
         systemClock: SystemClock,
@@ -999,6 +1004,7 @@
         mainExecutor,
         accessibilityManager,
         configurationController,
+        dumpManager,
         powerManager,
         R.layout.chipbar,
         wakeLockBuilder,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 2e4d8e7..d3411c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.common.shared.model.TintedIcon
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -66,6 +67,7 @@
     @Mock private lateinit var logger: ChipbarLogger
     @Mock private lateinit var accessibilityManager: AccessibilityManager
     @Mock private lateinit var configurationController: ConfigurationController
+    @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var powerManager: PowerManager
     @Mock private lateinit var windowManager: WindowManager
     @Mock private lateinit var falsingManager: FalsingManager
@@ -100,6 +102,7 @@
                 fakeExecutor,
                 accessibilityManager,
                 configurationController,
+                dumpManager,
                 powerManager,
                 falsingManager,
                 falsingCollector,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
index d5167b3..b9a5bd7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/FakeChipbarCoordinator.kt
@@ -22,6 +22,7 @@
 import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
 import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -38,6 +39,7 @@
     mainExecutor: DelayableExecutor,
     accessibilityManager: AccessibilityManager,
     configurationController: ConfigurationController,
+    dumpManager: DumpManager,
     powerManager: PowerManager,
     falsingManager: FalsingManager,
     falsingCollector: FalsingCollector,
@@ -53,6 +55,7 @@
         mainExecutor,
         accessibilityManager,
         configurationController,
+        dumpManager,
         powerManager,
         falsingManager,
         falsingCollector,
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index dd9f1d8..f4a62f9 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -545,6 +545,10 @@
                 sz.height = size.getHeight();
                 sizeList.sizes.add(sz);
             }
+
+            if (!sizeList.sizes.isEmpty()) {
+                ret.add(sizeList);
+            }
         }
 
         return ret;
@@ -740,6 +744,19 @@
         }
 
         @Override
+        public List<SizeList> getSupportedPostviewResolutions(
+                android.hardware.camera2.extension.Size captureSize) {
+            Size sz = new Size(captureSize.width, captureSize.height);
+            Map<Integer, List<Size>> supportedSizesMap =
+                    mAdvancedExtender.getSupportedPostviewResolutions(sz);
+            if (supportedSizesMap != null) {
+                return initializeParcelable(supportedSizesMap);
+            }
+
+            return null;
+        }
+
+        @Override
         public List<SizeList> getSupportedPreviewOutputResolutions(String cameraId) {
             Map<Integer, List<Size>> supportedSizesMap =
                     mAdvancedExtender.getSupportedPreviewOutputResolutions(cameraId);
@@ -840,6 +857,15 @@
 
             return false;
         }
+
+        @Override
+        public boolean isPostviewAvailable() {
+            if (LATENCY_IMPROVEMENTS_SUPPORTED) {
+                return mAdvancedExtender.isPostviewAvailable();
+            }
+
+            return false;
+        }
     }
 
     private class CaptureCallbackStub implements SessionProcessorImpl.CaptureCallback {
@@ -1167,11 +1193,13 @@
 
         @Override
         public CameraSessionConfig initSession(String cameraId, OutputSurface previewSurface,
-                OutputSurface imageCaptureSurface) {
+                OutputSurface imageCaptureSurface, OutputSurface postviewSurface) {
             OutputSurfaceImplStub outputPreviewSurfaceImpl =
                     new OutputSurfaceImplStub(previewSurface);
             OutputSurfaceImplStub outputImageCaptureSurfaceImpl =
                     new OutputSurfaceImplStub(imageCaptureSurface);
+            OutputSurfaceImplStub outputPostviewSurfaceImpl =
+                    new OutputSurfaceImplStub(postviewSurface);
 
             Camera2SessionConfigImpl sessionConfig;
 
@@ -1179,7 +1207,8 @@
                 OutputSurfaceConfigurationImplStub outputSurfaceConfigs =
                         new OutputSurfaceConfigurationImplStub(outputPreviewSurfaceImpl,
                         // Image Analysis Output is currently only supported in CameraX
-                        outputImageCaptureSurfaceImpl, null /*imageAnalysisSurfaceConfig*/);
+                        outputImageCaptureSurfaceImpl, null /*imageAnalysisSurfaceConfig*/,
+                        outputPostviewSurfaceImpl);
 
                 sessionConfig = mSessionProcessor.initSession(cameraId,
                         mCharacteristicsHashMap, getApplicationContext(), outputSurfaceConfigs);
@@ -1264,7 +1293,14 @@
         }
 
         @Override
-        public int startCapture(ICaptureCallback callback) {
+        public int startCapture(ICaptureCallback callback, boolean isPostviewRequested) {
+            if (LATENCY_IMPROVEMENTS_SUPPORTED) {
+                return isPostviewRequested ? mSessionProcessor.startCaptureWithPostview(
+                        new CaptureCallbackStub(callback, mCameraId)) :
+                        mSessionProcessor.startCapture(new CaptureCallbackStub(callback,
+                        mCameraId));
+            }
+
             return mSessionProcessor.startCapture(new CaptureCallbackStub(callback, mCameraId));
         }
 
@@ -1288,12 +1324,15 @@
         private OutputSurfaceImpl mOutputPreviewSurfaceImpl;
         private OutputSurfaceImpl mOutputImageCaptureSurfaceImpl;
         private OutputSurfaceImpl mOutputImageAnalysisSurfaceImpl;
+        private OutputSurfaceImpl mOutputPostviewSurfaceImpl;
 
         public OutputSurfaceConfigurationImplStub(OutputSurfaceImpl previewOutput,
-                OutputSurfaceImpl imageCaptureOutput, OutputSurfaceImpl imageAnalysisOutput) {
+                OutputSurfaceImpl imageCaptureOutput, OutputSurfaceImpl imageAnalysisOutput,
+                OutputSurfaceImpl postviewOutput) {
             mOutputPreviewSurfaceImpl = previewOutput;
             mOutputImageCaptureSurfaceImpl = imageCaptureOutput;
             mOutputImageAnalysisSurfaceImpl = imageAnalysisOutput;
+            mOutputPostviewSurfaceImpl = postviewOutput;
         }
 
         @Override
@@ -1310,6 +1349,11 @@
         public OutputSurfaceImpl getImageAnalysisOutputSurface() {
             return mOutputImageAnalysisSurfaceImpl;
         }
+
+        @Override
+        public OutputSurfaceImpl getPostviewOutputSurface() {
+            return mOutputPostviewSurfaceImpl;
+        }
     }
 
     private class OutputSurfaceImplStub implements OutputSurfaceImpl {
@@ -1498,6 +1542,15 @@
         }
 
         @Override
+        public boolean isPostviewAvailable() {
+            if (LATENCY_IMPROVEMENTS_SUPPORTED) {
+                return mImageExtender.isPostviewAvailable();
+            }
+
+            return false;
+        }
+
+        @Override
         public CaptureStageImpl onEnableSession() {
             return initializeParcelable(mImageExtender.onEnableSession(), mCameraId);
         }
@@ -1577,6 +1630,21 @@
         }
 
         @Override
+        public List<SizeList> getSupportedPostviewResolutions(
+                android.hardware.camera2.extension.Size captureSize) {
+            if (LATENCY_IMPROVEMENTS_SUPPORTED) {
+                Size sz = new Size(captureSize.width, captureSize.height);
+                List<Pair<Integer, android.util.Size[]>> sizes =
+                        mImageExtender.getSupportedPostviewResolutions(sz);
+                if ((sizes != null) && !sizes.isEmpty()) {
+                    return initializeParcelable(sizes);
+                }
+            }
+
+            return null;
+        }
+
+        @Override
         public LatencyRange getEstimatedCaptureLatencyRange(
                 android.hardware.camera2.extension.Size outputSize) {
             if (LATENCY_API_SUPPORTED) {
@@ -1715,8 +1783,21 @@
         }
 
         @Override
-        public void onResolutionUpdate(android.hardware.camera2.extension.Size size) {
-            mCaptureProcessor.onResolutionUpdate(new android.util.Size(size.width, size.height));
+        public void onPostviewOutputSurface(Surface surface) {
+            mCaptureProcessor.onPostviewOutputSurface(surface);
+        }
+
+        @Override
+        public void onResolutionUpdate(android.hardware.camera2.extension.Size size,
+                android.hardware.camera2.extension.Size postviewSize) {
+            if (postviewSize != null) {
+                mCaptureProcessor.onResolutionUpdate(
+                        new android.util.Size(size.width, size.height),
+                        new android.util.Size(postviewSize.width, postviewSize.height));
+            } else {
+                mCaptureProcessor.onResolutionUpdate(
+                        new android.util.Size(size.width, size.height));
+            }
         }
 
         @Override
@@ -1725,7 +1806,8 @@
         }
 
         @Override
-        public void process(List<CaptureBundle> captureList, IProcessResultImpl resultCallback) {
+        public void process(List<CaptureBundle> captureList, IProcessResultImpl resultCallback,
+                boolean isPostviewRequested) {
             HashMap<Integer, Pair<Image, TotalCaptureResult>> captureMap = new HashMap<>();
             for (CaptureBundle captureBundle : captureList) {
                 captureMap.put(captureBundle.stage, new Pair<> (
@@ -1734,7 +1816,12 @@
                                 captureBundle.sequenceId)));
             }
             if (!captureMap.isEmpty()) {
-                if ((resultCallback != null) && (RESULT_API_SUPPORTED)) {
+                if ((LATENCY_IMPROVEMENTS_SUPPORTED) && (isPostviewRequested)) {
+                    ProcessResultCallback processResultCallback = (resultCallback != null)
+                            ? new ProcessResultCallback(resultCallback, mCameraId) : null;
+                    mCaptureProcessor.processWithPostview(captureMap, processResultCallback,
+                            null /*executor*/);
+                } else if ((resultCallback != null) && (RESULT_API_SUPPORTED)) {
                     mCaptureProcessor.process(captureMap, new ProcessResultCallback(resultCallback,
                                     mCameraId), null /*executor*/);
                 } else if (resultCallback == null) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index f0b5959..05e305c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -42,6 +42,7 @@
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.accessibilityservice.MagnificationConfig;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
@@ -107,6 +108,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -129,6 +132,11 @@
     private static final String TRACE_WM = "WindowManagerInternal";
     private static final int WAIT_WINDOWS_TIMEOUT_MILLIS = 5000;
 
+    /** Display type for displays associated with the default user of th device. */
+    public static final int DISPLAY_TYPE_DEFAULT = 1 << 0;
+    /** Display type for displays associated with an AccessibilityDisplayProxy user. */
+    public static final int DISPLAY_TYPE_PROXY = 1 << 1;
+
     protected static final String TAKE_SCREENSHOT = "takeScreenshot";
     protected final Context mContext;
     protected final SystemSupport mSystemSupport;
@@ -157,6 +165,8 @@
     // The attribution tag set by the service that is bound to this instance
     protected String mAttributionTag;
 
+    protected int mDisplayTypes = DISPLAY_TYPE_DEFAULT;
+
     // The service that's bound to this instance. Whenever this value is non-null, this
     // object is registered as a death recipient
     IBinder mService;
@@ -224,6 +234,14 @@
      */
     private SparseArray<Long> mRequestTakeScreenshotOfWindowTimestampMs = new SparseArray<>();
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "DISPLAY_TYPE_" }, value = {
+            DISPLAY_TYPE_DEFAULT,
+            DISPLAY_TYPE_PROXY
+    })
+    public @interface DisplayTypes {}
+
     public interface SystemSupport {
         /**
          * @return The current dispatcher for key events
@@ -520,7 +538,8 @@
             }
             final AccessibilityWindowInfo.WindowListSparseArray allWindows =
                     new AccessibilityWindowInfo.WindowListSparseArray();
-            final ArrayList<Integer> displayList = mA11yWindowManager.getDisplayListLocked();
+            final ArrayList<Integer> displayList = mA11yWindowManager.getDisplayListLocked(
+                    mDisplayTypes);
             final int displayListCounts = displayList.size();
             if (displayListCounts > 0) {
                 for (int i = 0; i < displayListCounts; i++) {
@@ -538,6 +557,10 @@
         }
     }
 
+    protected void setDisplayTypes(@DisplayTypes int displayTypes) {
+        mDisplayTypes = displayTypes;
+    }
+
     @Override
     public AccessibilityWindowInfo getWindow(int windowId) {
         if (svcConnTracingEnabled()) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 59c1c54..b6fecf5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -457,7 +457,7 @@
                 new MagnificationScaleProvider(mContext));
         mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
         mCaptioningManagerImpl = new CaptioningManagerImpl(mContext);
-        mProxyManager = new ProxyManager(mLock);
+        mProxyManager = new ProxyManager(mLock, mA11yWindowManager);
         init();
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 7c68c8a..8af5e11 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -21,6 +21,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static com.android.server.accessibility.AbstractAccessibilityServiceConnection.DISPLAY_TYPE_DEFAULT;
+import static com.android.server.accessibility.AbstractAccessibilityServiceConnection.DISPLAY_TYPE_PROXY;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -169,6 +171,7 @@
         private List<AccessibilityWindowInfo> mWindows;
         private boolean mTrackingWindows = false;
         private boolean mHasWatchOutsideTouchWindow;
+        private boolean mIsProxy;
 
         /**
          * Constructor for DisplayWindowsObserver.
@@ -1016,6 +1019,33 @@
     }
 
     /**
+     * Starts tracking a display as belonging to a proxy. Creates the window observer if necessary.
+     * @param displayId
+     */
+    public void startTrackingDisplayProxy(int displayId) {
+        startTrackingWindows(displayId);
+        synchronized (mLock) {
+            DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId);
+            if (observer != null) {
+                observer.mIsProxy = true;
+            }
+        }
+    }
+
+    /**
+     * Stops tracking a display as belonging to a proxy.
+     * @param displayId
+     */
+    public void stopTrackingDisplayProxy(int displayId) {
+        synchronized (mLock) {
+            DisplayWindowsObserver observer = mDisplayWindowsObservers.get(displayId);
+            if (observer != null) {
+                observer.mIsProxy = false;
+            }
+        }
+    }
+
+    /**
      * Checks if we are tracking windows on any display.
      *
      * @return {@code true} if the observer is tracking windows on any display,
@@ -1728,15 +1758,21 @@
     /**
      * Returns the display list including all displays which are tracking windows.
      *
+     * @param displayTypes the types of displays to retrieve
      * @return The display list.
      */
-    public ArrayList<Integer> getDisplayListLocked() {
+    public ArrayList<Integer> getDisplayListLocked(
+            @AbstractAccessibilityServiceConnection.DisplayTypes int displayTypes) {
         final ArrayList<Integer> displayList = new ArrayList<>();
         final int count = mDisplayWindowsObservers.size();
         for (int i = 0; i < count; i++) {
             final DisplayWindowsObserver observer = mDisplayWindowsObservers.valueAt(i);
             if (observer != null) {
-                displayList.add(observer.mDisplayId);
+                if (!observer.mIsProxy && (displayTypes & DISPLAY_TYPE_DEFAULT) != 0) {
+                    displayList.add(observer.mDisplayId);
+                } else if (observer.mIsProxy && (displayTypes & DISPLAY_TYPE_PROXY) != 0) {
+                    displayList.add(observer.mDisplayId);
+                }
             }
         }
         return displayList;
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
index d7f9c12..d53a080 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
@@ -39,12 +39,14 @@
 import android.view.KeyEvent;
 import android.view.accessibility.AccessibilityDisplayProxy;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
 
 import androidx.annotation.Nullable;
 
 import com.android.server.wm.WindowManagerInternal;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -74,6 +76,7 @@
                 mainHandler, lock, securityPolicy, systemSupport, trace, windowManagerInternal,
                 /* systemActionPerformer= */ null, awm, /* activityTaskManagerService= */ null);
         mDisplayId = displayId;
+        setDisplayTypes(DISPLAY_TYPE_PROXY);
     }
 
     /**
@@ -189,6 +192,17 @@
     }
 
     @Override
+    public AccessibilityWindowInfo.WindowListSparseArray getWindows() {
+        final AccessibilityWindowInfo.WindowListSparseArray allWindows = super.getWindows();
+        AccessibilityWindowInfo.WindowListSparseArray displayWindows = new
+                AccessibilityWindowInfo.WindowListSparseArray();
+        // Filter here so A11yInteractionClient will not cache all the windows belonging to other
+        // proxy connections.
+        displayWindows.put(mDisplayId, allWindows.get(mDisplayId, Collections.emptyList()));
+        return displayWindows;
+    }
+
+    @Override
     public void binderDied() {
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
index f28191f..8527358 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
@@ -55,8 +55,11 @@
     private SparseArray<ProxyAccessibilityServiceConnection> mProxyA11yServiceConnections =
             new SparseArray<>();
 
-    ProxyManager(Object lock) {
+    private AccessibilityWindowManager mA11yWindowManager;
+
+    ProxyManager(Object lock, AccessibilityWindowManager awm) {
         mLock = lock;
+        mA11yWindowManager = awm;
     }
 
     /**
@@ -99,6 +102,8 @@
                     }
                 };
         client.asBinder().linkToDeath(deathRecipient, 0);
+
+        mA11yWindowManager.startTrackingDisplayProxy(displayId);
         // Notify apps that the service state has changed.
         // A11yManager#A11yServicesStateChangeListener
         synchronized (mLock) {
@@ -122,6 +127,7 @@
                 return true;
             }
         }
+        mA11yWindowManager.stopTrackingDisplayProxy(displayId);
         return false;
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 6cc2214..2188b99 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -253,6 +253,7 @@
                     securityPolicy, systemSupport, trace, windowManagerInternal,
                     systemActionPerformer, awm);
             mMainHandler = mainHandler;
+            setDisplayTypes(DISPLAY_TYPE_DEFAULT | DISPLAY_TYPE_PROXY);
         }
 
         void connectServiceUnknownThread() {
diff --git a/services/api/current.txt b/services/api/current.txt
index da5b1fc..b5798d5 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -169,3 +169,52 @@
 
 }
 
+package com.android.server.wm {
+
+  public interface ActivityInterceptorCallback {
+    method public default void onActivityLaunched(@NonNull android.app.TaskInfo, @NonNull android.content.pm.ActivityInfo, @NonNull com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo);
+    method @Nullable public com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult onInterceptActivityLaunch(@NonNull com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo);
+    field public static final int MAINLINE_SDK_SANDBOX_ORDER_ID = 1001; // 0x3e9
+  }
+
+  public static final class ActivityInterceptorCallback.ActivityInterceptResult {
+    ctor public ActivityInterceptorCallback.ActivityInterceptResult(@NonNull android.content.Intent, @NonNull android.app.ActivityOptions, boolean);
+    method @NonNull public android.app.ActivityOptions getActivityOptions();
+    method @NonNull public android.content.Intent getIntent();
+    method public boolean isActivityResolved();
+  }
+
+  public static final class ActivityInterceptorCallback.ActivityInterceptorInfo {
+    method @NonNull public android.content.pm.ActivityInfo getActivityInfo();
+    method @Nullable public String getCallingFeatureId();
+    method @Nullable public String getCallingPackage();
+    method public int getCallingPid();
+    method public int getCallingUid();
+    method @Nullable public android.app.ActivityOptions getCheckedOptions();
+    method @Nullable public Runnable getClearOptionsAnimationRunnable();
+    method @NonNull public android.content.Intent getIntent();
+    method public int getRealCallingPid();
+    method public int getRealCallingUid();
+    method @NonNull public android.content.pm.ResolveInfo getResolveInfo();
+    method @Nullable public String getResolvedType();
+    method public int getUserId();
+  }
+
+  public static final class ActivityInterceptorCallback.ActivityInterceptorInfo.Builder {
+    ctor public ActivityInterceptorCallback.ActivityInterceptorInfo.Builder(int, int, int, int, int, @NonNull android.content.Intent, @NonNull android.content.pm.ResolveInfo, @NonNull android.content.pm.ActivityInfo);
+    method @NonNull public com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo build();
+    method @NonNull public com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo.Builder setCallingFeatureId(@NonNull String);
+    method @NonNull public com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo.Builder setCallingPackage(@NonNull String);
+    method @NonNull public com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo.Builder setCheckedOptions(@NonNull android.app.ActivityOptions);
+    method @NonNull public com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo.Builder setClearOptionsAnimationRunnable(@NonNull Runnable);
+    method @NonNull public com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptorInfo.Builder setResolvedType(@NonNull String);
+  }
+
+  public class ActivityInterceptorCallbackRegistry {
+    method @NonNull public static com.android.server.wm.ActivityInterceptorCallbackRegistry getInstance();
+    method public void registerActivityInterceptorCallback(int, @NonNull com.android.server.wm.ActivityInterceptorCallback);
+    method public void unregisterActivityInterceptorCallback(int);
+  }
+
+}
+
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 195fee1..4d173d6 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -60,6 +60,7 @@
 import android.hardware.input.VirtualTouchscreenConfig;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.LocaleList;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -123,6 +124,9 @@
     // The default setting for showing the pointer on new displays.
     @GuardedBy("mVirtualDeviceLock")
     private boolean mDefaultShowPointerIcon = true;
+    @GuardedBy("mVirtualDeviceLock")
+    @Nullable
+    private LocaleList mLocaleList = null;
 
     private ActivityListener createListenerAdapter() {
         return new ActivityListener() {
@@ -247,6 +251,13 @@
         return mParams.getName();
     }
 
+    /** Returns the locale of the device. */
+    LocaleList getDeviceLocaleList() {
+        synchronized (mVirtualDeviceLock) {
+            return mLocaleList;
+        }
+    }
+
     /** Returns the policy specified for this policy type */
     public @VirtualDeviceParams.DevicePolicy int getDevicePolicy(
             @VirtualDeviceParams.PolicyType int policyType) {
@@ -344,6 +355,7 @@
                 mVirtualAudioController.stopListening();
                 mVirtualAudioController = null;
             }
+            mLocaleList = null;
         }
         mOnDeviceCloseListener.onClose(mDeviceId);
         mAppToken.unlinkToDeath(this, 0);
@@ -445,6 +457,7 @@
                         "Cannot create a virtual keyboard for a display not associated with "
                                 + "this virtual device");
             }
+            mLocaleList = LocaleList.forLanguageTags(config.getLanguageTag());
         }
         final long ident = Binder.clearCallingIdentity();
         try {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index d446417..d317298 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -42,6 +42,7 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.LocaleList;
 import android.os.Looper;
 import android.os.Parcel;
 import android.os.Process;
@@ -137,21 +138,22 @@
 
         @Nullable
         @Override
-        public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
-            if (info.callingPackage == null) {
+        public ActivityInterceptResult onInterceptActivityLaunch(@NonNull
+                ActivityInterceptorInfo info) {
+            if (info.getCallingPackage() == null) {
                 return null;
             }
-            PendingTrampoline pt = mPendingTrampolines.remove(info.callingPackage);
+            PendingTrampoline pt = mPendingTrampolines.remove(info.getCallingPackage());
             if (pt == null) {
                 return null;
             }
             pt.mResultReceiver.send(VirtualDeviceManager.LAUNCH_SUCCESS, null);
-            ActivityOptions options = info.checkedOptions;
+            ActivityOptions options = info.getCheckedOptions();
             if (options == null) {
                 options = ActivityOptions.makeBasic();
             }
             return new ActivityInterceptResult(
-                    info.intent, options.setLaunchDisplayId(pt.mDisplayId));
+                    info.getIntent(), options.setLaunchDisplayId(pt.mDisplayId));
         }
     };
 
@@ -591,6 +593,21 @@
         }
 
         @Override
+        @Nullable
+        public LocaleList getPreferredLocaleListForUid(int uid) {
+            // TODO: b/263188984 support the case where an app is running on multiple VDs
+            synchronized (mVirtualDeviceManagerLock) {
+                for (int i = 0; i < mAppsOnVirtualDevices.size(); i++) {
+                    if (mAppsOnVirtualDevices.valueAt(i).contains(uid)) {
+                        int deviceId = mAppsOnVirtualDevices.keyAt(i);
+                        return mVirtualDevices.get(deviceId).getDeviceLocaleList();
+                    }
+                }
+            }
+            return null;
+        }
+
+        @Override
         public boolean isAppRunningOnAnyVirtualDevice(int uid) {
             synchronized (mVirtualDeviceManagerLock) {
                 int size = mVirtualDevices.size();
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 9f3f761..5f5327d 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -582,7 +582,7 @@
     public abstract ResolveInfo resolveIntentExported(Intent intent, String resolvedType,
             @PackageManager.ResolveInfoFlagsBits long flags,
             @PrivateResolveFlags long privateResolveFlags, int userId, boolean resolveForStart,
-            int filterCallingUid);
+            int filterCallingUid, int callingPid);
 
     /**
     * Resolves a service intent, allowing instant apps to be resolved.
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index b4ab254..d1e0f16b 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -28,12 +28,10 @@
 import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.os.PowerManager;
 import android.os.RecoverySystem;
-import android.os.RemoteCallback;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -63,6 +61,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -188,9 +187,10 @@
     public static void onSettingsProviderPublished(Context context) {
         handleNativeRescuePartyResets();
         ContentResolver contentResolver = context.getContentResolver();
-        Settings.Config.registerMonitorCallback(contentResolver, new RemoteCallback(result -> {
-            handleMonitorCallback(context, result);
-        }));
+        DeviceConfig.setMonitorCallback(
+                contentResolver,
+                Executors.newSingleThreadExecutor(),
+                new RescuePartyMonitorCallback(context));
     }
 
 
@@ -278,27 +278,22 @@
         return SystemClock.elapsedRealtime();
     }
 
-    private static void handleMonitorCallback(Context context, Bundle result) {
-        String callbackType = result.getString(Settings.EXTRA_MONITOR_CALLBACK_TYPE, "");
-        switch (callbackType) {
-            case Settings.EXTRA_NAMESPACE_UPDATED_CALLBACK:
-                String updatedNamespace = result.getString(Settings.EXTRA_NAMESPACE);
-                if (updatedNamespace != null) {
-                    startObservingPackages(context, updatedNamespace);
-                }
-                break;
-            case Settings.EXTRA_ACCESS_CALLBACK:
-                String callingPackage = result.getString(Settings.EXTRA_CALLING_PACKAGE, null);
-                String namespace = result.getString(Settings.EXTRA_NAMESPACE, null);
-                if (namespace != null && callingPackage != null) {
-                    RescuePartyObserver.getInstance(context).recordDeviceConfigAccess(
+    private static class RescuePartyMonitorCallback implements DeviceConfig.MonitorCallback {
+        Context mContext;
+
+        RescuePartyMonitorCallback(Context context) {
+            this.mContext = context;
+        }
+
+        public void onNamespaceUpdate(@NonNull String updatedNamespace) {
+            startObservingPackages(mContext, updatedNamespace);
+        }
+
+        public void onDeviceConfigAccess(@NonNull String callingPackage,
+                @NonNull String namespace) {
+            RescuePartyObserver.getInstance(mContext).recordDeviceConfigAccess(
                             callingPackage,
                             namespace);
-                }
-                break;
-            default:
-                Slog.w(TAG, "Unrecognized DeviceConfig callback");
-                break;
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 87daa6d..907205c 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -704,6 +704,26 @@
         }
     }
 
+    static String getProcessNameForService(ServiceInfo sInfo, ComponentName name,
+            String callingPackage, String instanceName, boolean isSdkSandbox,
+            boolean inSharedIsolatedProcess) {
+        if (isSdkSandbox) {
+            // For SDK sandbox, the process name is passed in as the instanceName
+            return instanceName;
+        }
+        if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
+            // For regular processes, just the name in sInfo
+            return sInfo.processName;
+        }
+        // Isolated processes remain.
+        if (inSharedIsolatedProcess) {
+            // Shared isolated processes are scoped to the calling package
+            return callingPackage + ":ishared:" + instanceName;
+        } else {
+            return sInfo.processName + ":" + name.getClassName();
+        }
+    }
+
     ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
             int callingPid, int callingUid, boolean fgRequired, String callingPackage,
             @Nullable String callingFeatureId, final int userId)
@@ -736,7 +756,7 @@
 
         ServiceLookupResult res =
             retrieveServiceLocked(service, null, resolvedType, callingPackage,
-                    callingPid, callingUid, userId, true, callerFg, false, false);
+                    callingPid, callingUid, userId, true, callerFg, false, false, false);
         if (res == null) {
             return null;
         }
@@ -1338,7 +1358,8 @@
 
         // If this service is active, make sure it is stopped.
         ServiceLookupResult r = retrieveServiceLocked(service, null, resolvedType, null,
-                Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false, false, false);
+                Binder.getCallingPid(), Binder.getCallingUid(), userId, false, false, false, false,
+                false);
         if (r != null) {
             if (r.record != null) {
                 final long origId = Binder.clearCallingIdentity();
@@ -1430,7 +1451,7 @@
     IBinder peekServiceLocked(Intent service, String resolvedType, String callingPackage) {
         ServiceLookupResult r = retrieveServiceLocked(service, null, resolvedType, callingPackage,
                 Binder.getCallingPid(), Binder.getCallingUid(),
-                UserHandle.getCallingUserId(), false, false, false, false);
+                UserHandle.getCallingUserId(), false, false, false, false, false);
 
         IBinder ret = null;
         if (r != null) {
@@ -3237,11 +3258,13 @@
                 != ProcessList.SCHED_GROUP_BACKGROUND;
         final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
         final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0;
+        final boolean inSharedIsolatedProcess = (flags & Context.BIND_SHARED_ISOLATED_PROCESS) != 0;
 
         ServiceLookupResult res = retrieveServiceLocked(service, instanceName,
                 isSdkSandboxService, sdkSandboxClientAppUid, sdkSandboxClientAppPackage,
                 resolvedType, callingPackage, callingPid, callingUid, userId, true, callerFg,
-                isBindExternal, allowInstant, null /* fgsDelegateOptions */);
+                isBindExternal, allowInstant, null /* fgsDelegateOptions */,
+                inSharedIsolatedProcess);
         if (res == null) {
             return 0;
         }
@@ -3697,10 +3720,11 @@
             String instanceName, String resolvedType, String callingPackage,
             int callingPid, int callingUid, int userId,
             boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
-            boolean allowInstant) {
-        return retrieveServiceLocked(service, instanceName, false, 0, null, resolvedType,
+            boolean allowInstant, boolean inSharedIsolatedProcess) {
+        return retrieveServiceLocked(service, instanceName, false, INVALID_UID, null, resolvedType,
                 callingPackage, callingPid, callingUid, userId, createIfNeeded, callingFromFg,
-                isBindExternal, allowInstant, null /* fgsDelegateOptions */);
+                isBindExternal, allowInstant, null /* fgsDelegateOptions */,
+                inSharedIsolatedProcess);
     }
 
     private ServiceLookupResult retrieveServiceLocked(Intent service,
@@ -3708,7 +3732,8 @@
             String sdkSandboxClientAppPackage, String resolvedType,
             String callingPackage, int callingPid, int callingUid, int userId,
             boolean createIfNeeded, boolean callingFromFg, boolean isBindExternal,
-            boolean allowInstant, ForegroundServiceDelegationOptions fgsDelegateOptions) {
+            boolean allowInstant, ForegroundServiceDelegationOptions fgsDelegateOptions,
+            boolean inSharedIsolatedProcess) {
         if (isSdkSandboxService && instanceName == null) {
             throw new IllegalArgumentException("No instanceName provided for sdk sandbox process");
         }
@@ -3803,11 +3828,15 @@
                 final Intent.FilterComparison filter =
                         new Intent.FilterComparison(service.cloneFilter());
                 final ServiceRestarter res = new ServiceRestarter();
+                final String processName = getProcessNameForService(sInfo, cn, callingPackage,
+                        null /* instanceName */, false /* isSdkSandbox */,
+                        false /* inSharedIsolatedProcess */);
                 r = new ServiceRecord(mAm, cn /* name */, cn /* instanceName */,
                         sInfo.applicationInfo.packageName, sInfo.applicationInfo.uid, filter, sInfo,
-                        callingFromFg, res, null /* sdkSandboxProcessName */,
+                        callingFromFg, res, processName,
                         INVALID_UID /* sdkSandboxClientAppUid */,
-                        null /* sdkSandboxClientAppPackage */);
+                        null /* sdkSandboxClientAppPackage */,
+                        false /* inSharedIsolatedProcess */);
                 res.setService(r);
                 smap.mServicesByInstanceName.put(cn, r);
                 smap.mServicesByIntent.put(filter, r);
@@ -3901,6 +3930,21 @@
                     throw new SecurityException("BIND_EXTERNAL_SERVICE failed, " + name +
                             " is not an externalService");
                 }
+                if (inSharedIsolatedProcess) {
+                    if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) == 0) {
+                        throw new SecurityException("BIND_SHARED_ISOLATED_PROCESS failed, "
+                                + className + " is not an isolatedProcess");
+                    }
+                    if ((sInfo.flags & ServiceInfo.FLAG_ALLOW_SHARED_ISOLATED_PROCESS) == 0) {
+                        throw new SecurityException("BIND_SHARED_ISOLATED_PROCESS failed, "
+                                + className + " has not set the allowSharedIsolatedProcess "
+                                + " attribute.");
+                    }
+                    if (instanceName == null) {
+                        throw new IllegalArgumentException("instanceName must be provided for "
+                                + "binding a service into a shared isolated process.");
+                    }
+                }
                 if (userId > 0) {
                     if (mAm.isSingleton(sInfo.processName, sInfo.applicationInfo,
                             sInfo.name, sInfo.flags)
@@ -3934,12 +3978,12 @@
                     final Intent.FilterComparison filter
                             = new Intent.FilterComparison(service.cloneFilter());
                     final ServiceRestarter res = new ServiceRestarter();
-                    String sdkSandboxProcessName = isSdkSandboxService ? instanceName
-                                                                                  : null;
+                    String processName = getProcessNameForService(sInfo, name, callingPackage,
+                            instanceName, false, inSharedIsolatedProcess);
                     r = new ServiceRecord(mAm, className, name, definingPackageName,
                             definingUid, filter, sInfo, callingFromFg, res,
-                            sdkSandboxProcessName, sdkSandboxClientAppUid,
-                            sdkSandboxClientAppPackage);
+                            processName, sdkSandboxClientAppUid,
+                            sdkSandboxClientAppPackage, inSharedIsolatedProcess);
                     res.setService(r);
                     smap.mServicesByInstanceName.put(name, r);
                     smap.mServicesByIntent.put(filter, r);
@@ -4728,21 +4772,52 @@
                 }
             }
         } else {
-            // If this service runs in an isolated process, then each time
-            // we call startProcessLocked() we will get a new isolated
-            // process, starting another process if we are currently waiting
-            // for a previous process to come up.  To deal with this, we store
-            // in the service any current isolated process it is running in or
-            // waiting to have come up.
-            app = r.isolationHostProc;
-            if (WebViewZygote.isMultiprocessEnabled()
-                    && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
-                hostingRecord = HostingRecord.byWebviewZygote(r.instanceName, r.definingPackageName,
-                        r.definingUid, r.serviceInfo.processName);
-            }
-            if ((r.serviceInfo.flags & ServiceInfo.FLAG_USE_APP_ZYGOTE) != 0) {
-                hostingRecord = HostingRecord.byAppZygote(r.instanceName, r.definingPackageName,
-                        r.definingUid, r.serviceInfo.processName);
+            if (r.inSharedIsolatedProcess) {
+                app = mAm.mProcessList.getSharedIsolatedProcess(procName, r.appInfo.uid,
+                        r.appInfo.packageName);
+                if (app != null) {
+                    final IApplicationThread thread = app.getThread();
+                    final int pid = app.getPid();
+                    final UidRecord uidRecord = app.getUidRecord();
+                    if (thread != null) {
+                        try {
+                            if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+                                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                                        "realStartServiceLocked: " + r.shortInstanceName);
+                            }
+                            realStartServiceLocked(r, app, thread, pid, uidRecord, execInFg,
+                                    enqueueOomAdj);
+                            return null;
+                        } catch (TransactionTooLargeException e) {
+                            throw e;
+                        } catch (RemoteException e) {
+                            Slog.w(TAG, "Exception when starting service " + r.shortInstanceName,
+                                    e);
+                        } finally {
+                            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                        }
+                        // If a dead object exception was thrown -- fall through to
+                        // restart the application.
+                    }
+                }
+            } else {
+                // If this service runs in an isolated process, then each time
+                // we call startProcessLocked() we will get a new isolated
+                // process, starting another process if we are currently waiting
+                // for a previous process to come up.  To deal with this, we store
+                // in the service any current isolated process it is running in or
+                // waiting to have come up.
+                app = r.isolationHostProc;
+                if (WebViewZygote.isMultiprocessEnabled()
+                        && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
+                    hostingRecord = HostingRecord.byWebviewZygote(r.instanceName,
+                            r.definingPackageName,
+                            r.definingUid, r.serviceInfo.processName);
+                }
+                if ((r.serviceInfo.flags & ServiceInfo.FLAG_USE_APP_ZYGOTE) != 0) {
+                    hostingRecord = HostingRecord.byAppZygote(r.instanceName, r.definingPackageName,
+                            r.definingUid, r.serviceInfo.processName);
+                }
             }
         }
 
@@ -7649,7 +7724,7 @@
                 null /* sdkSandboxClientAppPackage */, null /* resolvedType */, callingPackage,
                 callingPid, callingUid, userId, true /* createIfNeeded */,
                 false /* callingFromFg */, false /* isBindExternal */, false /* allowInstant */ ,
-                options);
+                options, false /* inSharedIsolatedProcess */);
         if (res == null || res.record == null) {
             Slog.d(TAG,
                     "startForegroundServiceDelegateLocked retrieveServiceLocked returns null");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8a6b600..d7c4286 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -197,6 +197,7 @@
 import android.app.ITaskStackListener;
 import android.app.IUiAutomationConnection;
 import android.app.IUidObserver;
+import android.app.IUnsafeIntentStrictModeCallback;
 import android.app.IUserSwitchObserver;
 import android.app.Instrumentation;
 import android.app.Notification;
@@ -695,6 +696,10 @@
     static final int BROADCAST_QUEUE_BG_OFFLOAD = 2;
     static final int BROADCAST_QUEUE_FG_OFFLOAD = 3;
 
+    @GuardedBy("this")
+    private final SparseArray<IUnsafeIntentStrictModeCallback>
+            mStrictModeCallbacks = new SparseArray<>();
+
     // Convenient for easy iteration over the queues. Foreground is first
     // so that dispatch of foreground broadcasts gets precedence.
     final BroadcastQueue[] mBroadcastQueues;
@@ -7412,10 +7417,11 @@
         if (shareDescription != null) {
             triggerShellBugreport.putExtra(EXTRA_DESCRIPTION, shareDescription);
         }
+        UserHandle callingUser = Binder.getCallingUserHandle();
         final long identity = Binder.clearCallingIdentity();
         try {
             // Send broadcast to shell to trigger bugreport using Bugreport API
-            mContext.sendBroadcastAsUser(triggerShellBugreport, UserHandle.SYSTEM);
+            mContext.sendBroadcastAsUser(triggerShellBugreport, callingUser);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -8795,6 +8801,27 @@
         }
     }
 
+    /**
+     * Register a callback to raise strict mode violations.
+     * @param callback The binder used to communicate the violations.
+     */
+    @Override
+    public void registerStrictModeCallback(IBinder callback) {
+        int callingPid = Binder.getCallingPid();
+        mStrictModeCallbacks.put(callingPid,
+                IUnsafeIntentStrictModeCallback.Stub.asInterface(callback));
+        try {
+            callback.linkToDeath(new DeathRecipient() {
+                @Override
+                public void binderDied() {
+                    mStrictModeCallbacks.remove(callingPid);
+                }
+            }, 0);
+        } catch (RemoteException e) {
+            mStrictModeCallbacks.remove(callingPid);
+        }
+    }
+
     // Depending on the policy in effect, there could be a bunch of
     // these in quick succession so we try to batch these together to
     // minimize disk writes, number of dropbox entries, and maximize
@@ -12656,7 +12683,7 @@
      * @param query the list of broadcast filters
      * @param platformCompat the instance of platform compat
      */
-    private static void filterNonExportedComponents(Intent intent, int callingUid,
+    private void filterNonExportedComponents(Intent intent, int callingUid, int callingPid,
             List query, PlatformCompat platformCompat, String callerPackage, String resolvedType) {
         if (query == null
                 || intent.getPackage() != null
@@ -12664,6 +12691,7 @@
                 || ActivityManager.canAccessUnexportedComponents(callingUid)) {
             return;
         }
+        IUnsafeIntentStrictModeCallback callback = mStrictModeCallbacks.get(callingPid);
         for (int i = query.size() - 1; i >= 0; i--) {
             String componentInfo;
             ResolveInfo resolveInfo;
@@ -12684,6 +12712,15 @@
             } else {
                 continue;
             }
+            if (callback != null) {
+                mHandler.post(() -> {
+                    try {
+                        callback.onImplicitIntentMatchedInternalComponent(intent.cloneFilter());
+                    } catch (RemoteException e) {
+                        mStrictModeCallbacks.remove(callingPid);
+                    }
+                });
+            }
             boolean hasToBeExportedToMatch = platformCompat.isChangeEnabledByUid(
                     ActivityManagerService.IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS,
                     callingUid);
@@ -13053,7 +13090,7 @@
             String resolvedType, IServiceConnection connection, int flags, String instanceName,
             String callingPackage, int userId) throws TransactionTooLargeException {
         return bindServiceInstance(caller, token, service, resolvedType, connection, flags,
-                instanceName, false, 0, null, callingPackage, userId);
+                instanceName, false, INVALID_UID, null, callingPackage, userId);
     }
 
     private int bindServiceInstance(IApplicationThread caller, IBinder token, Intent service,
@@ -14615,7 +14652,7 @@
             }
         }
 
-        filterNonExportedComponents(intent, callingUid, registeredReceivers,
+        filterNonExportedComponents(intent, callingUid, callingPid, registeredReceivers,
                 mPlatformCompat, callerPackage, resolvedType);
         int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
         if (!ordered && NR > 0 && !mEnableModernQueue) {
@@ -14721,7 +14758,7 @@
         if ((receivers != null && receivers.size() > 0)
                 || resultTo != null) {
             BroadcastQueue queue = broadcastQueueForIntent(intent);
-            filterNonExportedComponents(intent, callingUid, receivers,
+            filterNonExportedComponents(intent, callingUid, callingPid, receivers,
                     mPlatformCompat, callerPackage, resolvedType);
             BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
                     callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
@@ -18194,6 +18231,16 @@
                 return mServices.getClientPackagesLocked(servicePackageName);
             }
         }
+
+        @Override
+        public IUnsafeIntentStrictModeCallback getRegisteredStrictModeCallback(int callingPid) {
+            return mStrictModeCallbacks.get(callingPid);
+        }
+
+        @Override
+        public void unregisterStrictModeCallback(int callingPid) {
+            mStrictModeCallbacks.remove(callingPid);
+        }
     }
 
     long inputDispatchingTimedOut(int pid, final boolean aboveSystem, TimeoutRecord timeoutRecord) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index d1bcf87..23ed0c4 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -136,7 +136,7 @@
 public final class BatteryStatsService extends IBatteryStats.Stub
         implements PowerManagerInternal.LowPowerModeListener,
         BatteryStatsImpl.PlatformIdleStateCallback,
-        BatteryStatsImpl.MeasuredEnergyRetriever,
+        BatteryStatsImpl.EnergyStatsRetriever,
         Watchdog.Monitor {
     static final String TAG = "BatteryStatsService";
     static final boolean DBG = false;
@@ -2531,7 +2531,7 @@
         awaitCompletion();
         syncStats("dump", BatteryExternalStatsWorker.UPDATE_ALL);
         synchronized (mStats) {
-            mStats.dumpMeasuredEnergyStatsLocked(pw);
+            mStats.dumpEnergyConsumerStatsLocked(pw);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 81e249c..80f1321 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3004,6 +3004,16 @@
         }
     }
 
+    ProcessRecord getSharedIsolatedProcess(String processName, int uid, String packageName) {
+        for (int i = 0, size = mIsolatedProcesses.size(); i < size; i++) {
+            final ProcessRecord app = mIsolatedProcesses.valueAt(i);
+            if (app.info.uid == uid && app.info.packageName.equals(packageName)
+                    && app.processName.equals(processName)) {
+                return app;
+            }
+        }
+        return null;
+    }
     @Nullable
     @GuardedBy("mService")
     List<Integer> getIsolatedProcessesLocked(int uid) {
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 05726f4..8c242743 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -19,6 +19,7 @@
 import static android.app.PendingIntent.FLAG_IMMUTABLE;
 import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
 import static android.os.PowerExemptionManager.REASON_DENIED;
+import static android.os.Process.INVALID_UID;
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -118,6 +119,7 @@
     boolean fgWaiting;      // is a timeout for going foreground already scheduled?
     boolean isNotAppComponentUsage; // is service binding not considered component/package usage?
     boolean isForeground;   // is service currently in foreground mode?
+    boolean inSharedIsolatedProcess; // is the service in a shared isolated process
     int foregroundId;       // Notification ID of last foreground req.
     Notification foregroundNoti; // Notification record of foreground state.
     long fgDisplayTime;     // time at which the FGS notification should become visible
@@ -723,6 +725,7 @@
         isSdkSandbox = false;
         sdkSandboxClientAppUid = 0;
         sdkSandboxClientAppPackage = null;
+        inSharedIsolatedProcess = false;
     }
 
     public static ServiceRecord newEmptyInstanceForTest(ActivityManagerService ams) {
@@ -734,14 +737,14 @@
             Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
             Runnable restarter) {
         this(ams, name, instanceName, definingPackageName, definingUid, intent, sInfo, callerIsFg,
-                restarter, null, 0, null);
+                restarter, sInfo.processName, INVALID_UID, null, false);
     }
 
     ServiceRecord(ActivityManagerService ams, ComponentName name,
             ComponentName instanceName, String definingPackageName, int definingUid,
             Intent.FilterComparison intent, ServiceInfo sInfo, boolean callerIsFg,
-            Runnable restarter, String sdkSandboxProcessName, int sdkSandboxClientAppUid,
-            String sdkSandboxClientAppPackage) {
+            Runnable restarter, String processName, int sdkSandboxClientAppUid,
+            String sdkSandboxClientAppPackage, boolean inSharedIsolatedProcess) {
         this.ams = ams;
         this.name = name;
         this.instanceName = instanceName;
@@ -752,16 +755,11 @@
         serviceInfo = sInfo;
         appInfo = sInfo.applicationInfo;
         packageName = sInfo.applicationInfo.packageName;
-        this.isSdkSandbox = sdkSandboxProcessName != null;
+        this.isSdkSandbox = sdkSandboxClientAppUid != INVALID_UID;
         this.sdkSandboxClientAppUid = sdkSandboxClientAppUid;
         this.sdkSandboxClientAppPackage = sdkSandboxClientAppPackage;
-        if ((sInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0) {
-            processName = sInfo.processName + ":" + instanceName.getClassName();
-        } else if (sdkSandboxProcessName != null) {
-            processName = sdkSandboxProcessName;
-        } else {
-            processName = sInfo.processName;
-        }
+        this.inSharedIsolatedProcess = inSharedIsolatedProcess;
+        this.processName = processName;
         permission = sInfo.permission;
         exported = sInfo.exported;
         this.restarter = restarter;
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 9cf4082..18839a8 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -161,6 +161,7 @@
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.component.ParsedAttribution;
 import com.android.server.policy.AppOpsPolicy;
 
@@ -3590,11 +3591,11 @@
      *
      * @return The restriction matching the package
      */
-    private RestrictionBypass getBypassforPackage(@NonNull AndroidPackage pkg) {
-        return new RestrictionBypass(pkg.getUid() == Process.SYSTEM_UID, pkg.isPrivileged(),
-                mContext.checkPermission(android.Manifest.permission
-                        .EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS, -1, pkg.getUid())
-                == PackageManager.PERMISSION_GRANTED);
+    private RestrictionBypass getBypassforPackage(@NonNull PackageState packageState) {
+        return new RestrictionBypass(packageState.getAppId() == Process.SYSTEM_UID,
+                packageState.isPrivileged(), mContext.checkPermission(
+                android.Manifest.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS, -1,
+                packageState.getAppId()) == PackageManager.PERMISSION_GRANTED);
     }
 
     /**
@@ -3690,11 +3691,12 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
-            AndroidPackage pkg = pmInt.getPackage(packageName);
+            var pkgState = pmInt.getPackageStateInternal(packageName);
+            var pkg = pkgState == null ? null : pkgState.getAndroidPackage();
             if (pkg != null) {
                 isAttributionTagValid = isAttributionInPackage(pkg, attributionTag);
-                pkgUid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid()));
-                bypass = getBypassforPackage(pkg);
+                pkgUid = UserHandle.getUid(userId, pkgState.getAppId());
+                bypass = getBypassforPackage(pkgState);
             }
             if (!isAttributionTagValid) {
                 AndroidPackage proxyPkg = proxyPackageName != null
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9b433cf..b5053960 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -21,6 +21,7 @@
 import static android.media.AudioManager.RINGER_MODE_NORMAL;
 import static android.media.AudioManager.RINGER_MODE_SILENT;
 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
+import static android.media.AudioManager.STREAM_MUSIC;
 import static android.media.AudioManager.STREAM_SYSTEM;
 import static android.os.Process.FIRST_APPLICATION_UID;
 import static android.os.Process.INVALID_UID;
@@ -130,6 +131,8 @@
 import android.media.audiopolicy.AudioProductStrategy;
 import android.media.audiopolicy.AudioVolumeGroup;
 import android.media.audiopolicy.IAudioPolicyCallback;
+import android.media.permission.ClearCallingIdentityContext;
+import android.media.permission.SafeCloseable;
 import android.media.projection.IMediaProjection;
 import android.media.projection.IMediaProjectionCallback;
 import android.media.projection.IMediaProjectionManager;
@@ -382,6 +385,7 @@
     private static final int MSG_RESET_SPATIALIZER = 50;
     private static final int MSG_NO_LOG_FOR_PLAYER_I = 51;
     private static final int MSG_DISPATCH_PREFERRED_MIXER_ATTRIBUTES = 52;
+    private static final int MSG_LOWER_VOLUME_TO_RS1 = 53;
 
     /** Messages handled by the {@link SoundDoseHelper}. */
     /*package*/ static final int SAFE_MEDIA_VOLUME_MSG_START = 1000;
@@ -8580,6 +8584,10 @@
                     onDispatchPreferredMixerAttributesChanged(msg.getData(), msg.arg1);
                     break;
 
+                case MSG_LOWER_VOLUME_TO_RS1:
+                    onLowerVolumeToRs1();
+                    break;
+
                 default:
                     if (msg.what >= SAFE_MEDIA_VOLUME_MSG_START) {
                         // msg could be for the SoundDoseHelper
@@ -9787,6 +9795,38 @@
         mSoundDoseHelper.disableSafeMediaVolume(callingPackage);
     }
 
+    @Override
+    public void lowerVolumeToRs1(String callingPackage) {
+        enforceVolumeController("lowerVolumeToRs1");
+        postLowerVolumeToRs1();
+    }
+
+    /*package*/ void postLowerVolumeToRs1() {
+        sendMsg(mAudioHandler, MSG_LOWER_VOLUME_TO_RS1, SENDMSG_QUEUE,
+                // no params, no delay
+                0, 0, null, 0);
+    }
+
+    /**
+     * Called when handling MSG_LOWER_VOLUME_TO_RS1
+     */
+    private void onLowerVolumeToRs1() {
+        final ArrayList<AudioDeviceAttributes> devices = getDevicesForAttributesInt(
+                new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build(), true);
+        final int nativeDeviceType;
+        final AudioDeviceAttributes ada;
+        if (devices.isEmpty()) {
+            ada = devices.get(0);
+            nativeDeviceType = ada.getInternalType();
+        } else {
+            nativeDeviceType = AudioSystem.DEVICE_OUT_USB_HEADSET;
+            ada = new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_USB_HEADSET, "");
+        }
+        final int index = mSoundDoseHelper.safeMediaVolumeIndex(nativeDeviceType);
+        setStreamVolumeWithAttributionInt(STREAM_MUSIC, index, /*flags*/ 0, ada,
+                "com.android.server.audio", "AudioService");
+    }
+
     //==========================================================================================
     // Hdmi CEC:
     // - System audio mode:
@@ -10298,6 +10338,9 @@
     public interface ISafeHearingVolumeController {
         /** Displays an instructional safeguard as required by the safe hearing standard. */
         void postDisplaySafeVolumeWarning(int flags);
+
+        /** Displays a warning about transient exposure to high level playback */
+        void postDisplayCsdWarning(@AudioManager.CsdWarning int csdEvent, int displayDurationMs);
     }
 
     /** Wrapper which encapsulates the {@link IVolumeController} functionality. */
@@ -10400,6 +10443,20 @@
             }
         }
 
+        @Override
+        public void postDisplayCsdWarning(
+                @AudioManager.CsdWarning int csdWarning, int displayDurationMs) {
+            if (mController == null) {
+                Log.e(TAG, "Unable to display CSD warning, no controller");
+                return;
+            }
+            try {
+                mController.displayCsdWarning(csdWarning, displayDurationMs);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Error calling displayCsdWarning for warning " + csdWarning, e);
+            }
+        }
+
         public void postVolumeChanged(int streamType, int flags) {
             if (mController == null)
                 return;
@@ -11164,6 +11221,34 @@
         mPrefMixerAttrDispatcher.finishBroadcast();
     }
 
+
+    /** @see AudioManager#supportsBluetoothVariableLatency() */
+    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public boolean supportsBluetoothVariableLatency() {
+        super.supportsBluetoothVariableLatency_enforcePermission();
+        try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+            return AudioSystem.supportsBluetoothVariableLatency();
+        }
+    }
+
+    /** @see AudioManager#setBluetoothVariableLatencyEnabled(boolean) */
+    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public void setBluetoothVariableLatencyEnabled(boolean enabled) {
+        super.setBluetoothVariableLatencyEnabled_enforcePermission();
+        try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+            AudioSystem.setBluetoothVariableLatencyEnabled(enabled);
+        }
+    }
+
+    /** @see AudioManager#isBluetoothVariableLatencyEnabled(boolean) */
+    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    public boolean isBluetoothVariableLatencyEnabled() {
+        super.isBluetoothVariableLatencyEnabled_enforcePermission();
+        try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+            return AudioSystem.isBluetoothVariableLatencyEnabled();
+        }
+    }
+
     private final Object mExtVolumeControllerLock = new Object();
     private IAudioPolicyCallback mExtVolumeController;
     private void setExtVolumeController(IAudioPolicyCallback apc) {
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 6279a4d..7edd911 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -26,6 +26,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.media.ISoundDose;
 import android.media.ISoundDoseCallback;
@@ -40,6 +41,7 @@
 import android.util.Log;
 import android.util.MathUtils;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.audio.AudioService.AudioHandler;
 import com.android.server.audio.AudioService.ISafeHearingVolumeController;
@@ -96,11 +98,19 @@
 
     private static final float CUSTOM_RS2_VALUE = 90;
 
+    // timeouts for the CSD warnings, -1 means no timeout (dialog must be ack'd by user)
+    private static final int CSD_WARNING_TIMEOUT_MS_DOSE_1X = 7000;
+    private static final int CSD_WARNING_TIMEOUT_MS_DOSE_5X = 5000;
+    private static final int CSD_WARNING_TIMEOUT_MS_ACCUMULATION_START = -1;
+    private static final int CSD_WARNING_TIMEOUT_MS_MOMENTARY_EXPOSURE = 5000;
+
     private final EventLogger mLogger = new EventLogger(AudioService.LOG_NB_EVENTS_SOUND_DOSE,
             "CSD updates");
 
     private int mMcc = 0;
 
+    private final boolean mEnableCsd;
+
     final Object mSafeMediaVolumeStateLock = new Object();
     private int mSafeMediaVolumeState;
 
@@ -144,6 +154,8 @@
 
     private ISoundDose mSoundDose;
     private float mCurrentCsd = 0.f;
+    // dose at which the next dose reached warning occurs
+    private float mNextCsdWarning = 1.0f;
     private final List<SoundDoseRecord> mDoseRecords = new ArrayList<>();
 
     private final Context mContext;
@@ -153,10 +165,42 @@
             Log.w(TAG, "DeviceId " + deviceId + " triggered momentary exposure with value: "
                     + currentMel);
             mLogger.enqueue(SoundDoseEvent.getMomentaryExposureEvent(currentMel));
+            if (mEnableCsd) {
+                mVolumeController.postDisplayCsdWarning(
+                        AudioManager.CSD_WARNING_MOMENTARY_EXPOSURE,
+                        getTimeoutMsForWarning(AudioManager.CSD_WARNING_MOMENTARY_EXPOSURE));
+            }
         }
 
         public void onNewCsdValue(float currentCsd, SoundDoseRecord[] records) {
             Log.i(TAG, "onNewCsdValue: " + currentCsd);
+            if (mCurrentCsd < currentCsd) {
+                // dose increase: going over next threshold?
+                if ((mCurrentCsd < mNextCsdWarning) && (currentCsd >= mNextCsdWarning)) {
+                    if (mEnableCsd) {
+                        if (mNextCsdWarning == 5.0f) {
+                            // 500% dose repeat
+                            mVolumeController.postDisplayCsdWarning(
+                                    AudioManager.CSD_WARNING_DOSE_REPEATED_5X,
+                                    getTimeoutMsForWarning(
+                                            AudioManager.CSD_WARNING_DOSE_REPEATED_5X));
+                            // on the 5x dose warning, the volume reduction happens right away
+                            mAudioService.postLowerVolumeToRs1();
+                        } else {
+                            mVolumeController.postDisplayCsdWarning(
+                                    AudioManager.CSD_WARNING_DOSE_REACHED_1X,
+                                    getTimeoutMsForWarning(
+                                            AudioManager.CSD_WARNING_DOSE_REACHED_1X));
+                        }
+                    }
+                    mNextCsdWarning += 1.0f;
+                }
+            } else {
+                // dose decrease: dropping below previous threshold of warning?
+                if ((currentCsd < mNextCsdWarning - 1.0f) && (mNextCsdWarning >= 2.0f)) {
+                    mNextCsdWarning -= 1.0f;
+                }
+            }
             mCurrentCsd = currentCsd;
             mDoseRecords.addAll(Arrays.asList(records));
             long totalDuration = 0;
@@ -184,10 +228,12 @@
         mSafeMediaVolumeState = mSettings.getGlobalInt(audioService.getContentResolver(),
                 Settings.Global.AUDIO_SAFE_VOLUME_STATE, 0);
 
+        mEnableCsd = mContext.getResources().getBoolean(R.bool.config_audio_csd_enabled_default);
+
         // The default safe volume index read here will be replaced by the actual value when
         // the mcc is read by onConfigureSafeVolume()
         mSafeMediaVolumeIndex = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_safe_media_volume_index) * 10;
+                R.integer.config_safe_media_volume_index) * 10;
 
         mAlarmManager = (AlarmManager) mContext.getSystemService(
                 Context.ALARM_SERVICE);
@@ -401,6 +447,7 @@
     }
 
     /*package*/ void dump(PrintWriter pw) {
+        pw.print("  mEnableCsd="); pw.println(mEnableCsd);
         pw.print("  mSafeMediaVolumeState=");
         pw.println(safeMediaVolumeStateToString(mSafeMediaVolumeState));
         pw.print("  mSafeMediaVolumeIndex="); pw.println(mSafeMediaVolumeIndex);
@@ -491,6 +538,21 @@
         }
     }
 
+    private int getTimeoutMsForWarning(@AudioManager.CsdWarning int csdWarning) {
+        switch (csdWarning) {
+            case AudioManager.CSD_WARNING_DOSE_REACHED_1X:
+                return CSD_WARNING_TIMEOUT_MS_DOSE_1X;
+            case AudioManager.CSD_WARNING_DOSE_REPEATED_5X:
+                return CSD_WARNING_TIMEOUT_MS_DOSE_5X;
+            case AudioManager.CSD_WARNING_MOMENTARY_EXPOSURE:
+                return CSD_WARNING_TIMEOUT_MS_MOMENTARY_EXPOSURE;
+            case AudioManager.CSD_WARNING_ACCUMULATION_START:
+                return CSD_WARNING_TIMEOUT_MS_ACCUMULATION_START;
+        }
+        Log.e(TAG, "Invalid CSD warning " + csdWarning, new Exception());
+        return -1;
+    }
+
     @GuardedBy("mSafeMediaVolumeStateLock")
     private void setSafeMediaVolumeEnabled(boolean on, String caller) {
         if ((mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_NOT_CONFIGURED) && (mSafeMediaVolumeState
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index 09bec5e..e3ea1a6 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -17,7 +17,9 @@
 package com.android.server.companion.virtual;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.companion.virtual.IVirtualDevice;
+import android.os.LocaleList;
 
 import java.util.Set;
 
@@ -111,6 +113,20 @@
     public abstract int getBaseVirtualDisplayFlags(IVirtualDevice virtualDevice);
 
     /**
+     * Returns the preferred locale hints of the Virtual Device on which the given app is running,
+     * or {@code null} if the hosting virtual device doesn't have a virtual keyboard or the app is
+     * not on any virtual device.
+     *
+     * If an app is on multiple virtual devices, the locale of the virtual device created the
+     * earliest will be returned.
+     *
+     * See {@link android.hardware.input.VirtualKeyboardConfig#setLanguageTag() for how the locale
+     * is specified for virtual keyboard.
+     */
+    @Nullable
+    public abstract LocaleList getPreferredLocaleListForUid(int uid);
+
+    /**
      * Returns true if the given {@code uid} is currently running on any virtual devices. This is
      * determined by whether the app has any activities in the task stack on a virtual-device-owned
      * display.
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 19dbee7..c15e419 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -140,6 +140,7 @@
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnProfile;
 import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.BinderUtils;
 import com.android.net.module.util.NetdUtils;
 import com.android.net.module.util.NetworkStackConstants;
 import com.android.server.DeviceIdleInternal;
@@ -2783,6 +2784,16 @@
         return hasIPV6 && !hasIPV4;
     }
 
+    private void setVpnNetworkPreference(String session, Set<Range<Integer>> ranges) {
+        BinderUtils.withCleanCallingIdentity(
+                () -> mConnectivityManager.setVpnDefaultForUids(session, ranges));
+    }
+
+    private void clearVpnNetworkPreference(String session) {
+        BinderUtils.withCleanCallingIdentity(
+                () -> mConnectivityManager.setVpnDefaultForUids(session, Collections.EMPTY_LIST));
+    }
+
     /**
      * Internal class managing IKEv2/IPsec VPN connectivity
      *
@@ -2894,6 +2905,9 @@
                     (r, exe) -> {
                         Log.d(TAG, "Runnable " + r + " rejected by the mExecutor");
                     });
+            setVpnNetworkPreference(mSessionKey,
+                    createUserAndRestrictedProfilesRanges(mUserId,
+                            mConfig.allowedApplications, mConfig.disallowedApplications));
         }
 
         @Override
@@ -3047,7 +3061,6 @@
                     mConfig.dnsServers.addAll(dnsAddrStrings);
 
                     mConfig.underlyingNetworks = new Network[] {network};
-                    mConfig.disallowedApplications = getAppExclusionList(mPackage);
 
                     networkAgent = mNetworkAgent;
 
@@ -3750,6 +3763,7 @@
             mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
             mConnectivityDiagnosticsManager.unregisterConnectivityDiagnosticsCallback(
                     mDiagnosticsCallback);
+            clearVpnNetworkPreference(mSessionKey);
 
             mExecutor.shutdown();
         }
@@ -4310,6 +4324,7 @@
             mConfig.requiresInternetValidation = profile.requiresInternetValidation;
             mConfig.excludeLocalRoutes = profile.excludeLocalRoutes;
             mConfig.allowBypass = profile.isBypassable;
+            mConfig.disallowedApplications = getAppExclusionList(mPackage);
 
             switch (profile.type) {
                 case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
@@ -4462,6 +4477,9 @@
                         .setUids(createUserAndRestrictedProfilesRanges(
                                 mUserId, null /* allowedApplications */, excludedApps))
                         .build();
+                setVpnNetworkPreference(getSessionKeyLocked(),
+                        createUserAndRestrictedProfilesRanges(mUserId,
+                                mConfig.allowedApplications, mConfig.disallowedApplications));
                 doSendNetworkCapabilities(mNetworkAgent, mNetworkCapabilities);
             }
         }
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index d510523..c47d749 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -149,7 +149,8 @@
             new ActivityInterceptorCallback() {
                 @Nullable
                 @Override
-                public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
+                public ActivityInterceptResult onInterceptActivityLaunch(@NonNull
+                        ActivityInterceptorInfo info) {
                     return null;
                 }
 
diff --git a/services/core/java/com/android/server/incident/IncidentCompanionService.java b/services/core/java/com/android/server/incident/IncidentCompanionService.java
index 87fe785..b8e7d49 100644
--- a/services/core/java/com/android/server/incident/IncidentCompanionService.java
+++ b/services/core/java/com/android/server/incident/IncidentCompanionService.java
@@ -34,7 +34,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.util.Log;
 
 import com.android.internal.util.DumpUtils;
@@ -128,21 +127,21 @@
             try {
                 final Context context = getContext();
 
-                final int primaryUser = getAndValidateUser(context);
-                if (primaryUser == UserHandle.USER_NULL) {
+                // Get the current admin user. Only they can do incident reports.
+                final int currentAdminUser = getCurrentUserIfAdmin();
+                if (currentAdminUser == UserHandle.USER_NULL) {
                     return;
                 }
 
                 final Intent intent = new Intent(Intent.ACTION_INCIDENT_REPORT_READY);
                 intent.setComponent(new ComponentName(pkg, cls));
 
-                Log.d(TAG, "sendReportReadyBroadcast sending primaryUser=" + primaryUser
-                        + " userHandle=" + UserHandle.getUserHandleForUid(primaryUser)
+                Log.d(TAG, "sendReportReadyBroadcast sending currentUser=" + currentAdminUser
+                        + " userHandle=" + UserHandle.of(currentAdminUser)
                         + " intent=" + intent);
 
-                // Send it to the primary user.  Only they can do incident reports.
                 context.sendBroadcastAsUserMultiplePermissions(intent,
-                        UserHandle.getUserHandleForUid(primaryUser),
+                        UserHandle.of(currentAdminUser),
                         DUMP_AND_USAGE_STATS_PERMISSIONS);
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -414,10 +413,10 @@
     }
 
     /**
-     * Check whether the current user is the primary user, and return the user id if they are.
+     * Check whether the current user is an admin user, and return the user id if they are.
      * Returns UserHandle.USER_NULL if not valid.
      */
-    public static int getAndValidateUser(Context context) {
+    public static int getCurrentUserIfAdmin() {
         // Current user
         UserInfo currentUser;
         try {
@@ -427,28 +426,21 @@
             throw new RuntimeException(ex);
         }
 
-        // Primary user
-        final UserManager um = UserManager.get(context);
-        final UserInfo primaryUser = um.getPrimaryUser();
-
         // Check that we're using the right user.
         if (currentUser == null) {
             Log.w(TAG, "No current user.  Nobody to approve the report."
                     + " The report will be denied.");
             return UserHandle.USER_NULL;
         }
-        if (primaryUser == null) {
-            Log.w(TAG, "No primary user.  Nobody to approve the report."
-                    + " The report will be denied.");
-            return UserHandle.USER_NULL;
-        }
-        if (primaryUser.id != currentUser.id) {
-            Log.w(TAG, "Only the primary user can approve bugreports, but they are not"
-                    + " the current user. The report will be denied.");
+
+        if (!currentUser.isAdmin()) {
+            Log.w(TAG, "Only an admin user running in foreground can approve "
+                    + "bugreports, but the current foreground user is not an admin user. "
+                    + "The report will be denied.");
             return UserHandle.USER_NULL;
         }
 
-        return primaryUser.id;
+        return currentUser.id;
     }
 }
 
diff --git a/services/core/java/com/android/server/incident/PendingReports.java b/services/core/java/com/android/server/incident/PendingReports.java
index f39bebf..684b5f1 100644
--- a/services/core/java/com/android/server/incident/PendingReports.java
+++ b/services/core/java/com/android/server/incident/PendingReports.java
@@ -16,6 +16,7 @@
 
 package com.android.server.incident;
 
+import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
 import android.content.ComponentName;
@@ -30,6 +31,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Log;
 
 import java.io.FileDescriptor;
@@ -272,15 +274,22 @@
             return;
         }
 
-        // Find the primary user of this device.
-        final int primaryUser = getAndValidateUser();
-        if (primaryUser == UserHandle.USER_NULL) {
+        // Find the current user of the device and check if they are an admin.
+        final int currentAdminUser = getCurrentUserIfAdmin();
+        final int callingUser = UserHandle.getUserId(callingUid);
+
+        // Deny the report if the current admin user is null
+        // or the calling user is not from the same profile group of current user.
+        if (currentAdminUser == UserHandle.USER_NULL
+                || !isSameProfileGroupUser(callingUser, currentAdminUser)) {
+            Log.w(TAG, "Calling user " + callingUser + " doesn't belong to the same profile "
+                    + "group of the current admin user " + currentAdminUser);
             denyReportBeforeAddingRec(listener, callingPackage);
             return;
         }
 
         // Find the approver app (hint: it's PermissionController).
-        final ComponentName receiver = getApproverComponent(primaryUser);
+        final ComponentName receiver = getApproverComponent(currentAdminUser);
         if (receiver == null) {
             // We couldn't find an approver... so deny the request here and now, before we
             // do anything else.
@@ -298,26 +307,26 @@
         try {
             listener.asBinder().linkToDeath(() -> {
                 Log.i(TAG, "Got death notification listener=" + listener);
-                cancelReportImpl(listener, receiver, primaryUser);
+                cancelReportImpl(listener, receiver, currentAdminUser);
             }, 0);
         } catch (RemoteException ex) {
             Log.e(TAG, "Remote died while trying to register death listener: " + rec.getUri());
             // First, remove from our list.
-            cancelReportImpl(listener, receiver, primaryUser);
+            cancelReportImpl(listener, receiver, currentAdminUser);
         }
 
         // Go tell Permission controller to start asking the user.
-        sendBroadcast(receiver, primaryUser);
+        sendBroadcast(receiver, currentAdminUser);
     }
 
     /**
      * Cancel a pending report request (because of an explicit call to cancel)
      */
     private void cancelReportImpl(IIncidentAuthListener listener) {
-        final int primaryUser = getAndValidateUser();
-        final ComponentName receiver = getApproverComponent(primaryUser);
-        if (primaryUser != UserHandle.USER_NULL && receiver != null) {
-            cancelReportImpl(listener, receiver, primaryUser);
+        final int currentAdminUser = getCurrentUserIfAdmin();
+        final ComponentName receiver = getApproverComponent(currentAdminUser);
+        if (currentAdminUser != UserHandle.USER_NULL && receiver != null) {
+            cancelReportImpl(listener, receiver, currentAdminUser);
         }
     }
 
@@ -326,13 +335,13 @@
      * by the calling app, or because of a binder death).
      */
     private void cancelReportImpl(IIncidentAuthListener listener, ComponentName receiver,
-            int primaryUser) {
+            @UserIdInt int user) {
         // First, remove from our list.
         synchronized (mLock) {
             removePendingReportRecLocked(listener);
         }
         // Second, call back to PermissionController to say it's canceled.
-        sendBroadcast(receiver, primaryUser);
+        sendBroadcast(receiver, user);
     }
 
     /**
@@ -342,21 +351,21 @@
      * cleanup cases to keep the apps' list in sync with ours.
      */
     private void sendBroadcast() {
-        final int primaryUser = getAndValidateUser();
-        if (primaryUser == UserHandle.USER_NULL) {
+        final int currentAdminUser = getCurrentUserIfAdmin();
+        if (currentAdminUser == UserHandle.USER_NULL) {
             return;
         }
-        final ComponentName receiver = getApproverComponent(primaryUser);
+        final ComponentName receiver = getApproverComponent(currentAdminUser);
         if (receiver == null) {
             return;
         }
-        sendBroadcast(receiver, primaryUser);
+        sendBroadcast(receiver, currentAdminUser);
     }
 
     /**
      * Send the confirmation broadcast.
      */
-    private void sendBroadcast(ComponentName receiver, int primaryUser) {
+    private void sendBroadcast(ComponentName receiver, int currentUser) {
         final Intent intent = new Intent(Intent.ACTION_PENDING_INCIDENT_REPORTS_CHANGED);
         intent.setComponent(receiver);
         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -364,8 +373,8 @@
         final BroadcastOptions options = BroadcastOptions.makeBasic();
         options.setBackgroundActivityStartsAllowed(true);
 
-        // Send it to the primary user.
-        mContext.sendBroadcastAsUser(intent, UserHandle.getUserHandleForUid(primaryUser),
+        // Send it to the current user.
+        mContext.sendBroadcastAsUser(intent, UserHandle.of(currentUser),
                 android.Manifest.permission.APPROVE_INCIDENT_REPORTS, options.toBundle());
     }
 
@@ -420,11 +429,11 @@
     }
 
     /**
-     * Check whether the current user is the primary user, and return the user id if they are.
+     * Check whether the current user is an admin user, and return the user id if they are.
      * Returns UserHandle.USER_NULL if not valid.
      */
-    private int getAndValidateUser() {
-        return IncidentCompanionService.getAndValidateUser(mContext);
+    private int getCurrentUserIfAdmin() {
+        return IncidentCompanionService.getCurrentUserIfAdmin();
     }
 
     /**
@@ -461,5 +470,15 @@
             return false;
         }
     }
+
+    /**
+     * Checks if the 2 provided user ids belong to the same profile group
+     * using {@link UserManager#isSameProfileGroup(int, int)}
+     */
+    private boolean isSameProfileGroupUser(@UserIdInt int currentAdminUser,
+            @UserIdInt int callingUser) {
+        return UserManager.get(mContext)
+                .isSameProfileGroup(currentAdminUser, callingUser);
+    }
 }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 97c8305..5840acf 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -183,6 +183,7 @@
 import com.android.server.ServiceThread;
 import com.android.server.SystemServerInitThreadPool;
 import com.android.server.SystemService;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 import com.android.server.input.InputManagerInternal;
 import com.android.server.inputmethod.InputMethodManagerInternal.InputMethodListListener;
 import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
@@ -304,6 +305,8 @@
      */
     @Nullable
     private AudioManagerInternal mAudioManagerInternal = null;
+    @Nullable
+    private VirtualDeviceManagerInternal mVdmInternal = null;
 
     // All known input methods.
     final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
@@ -2533,6 +2536,16 @@
         mCurVirtualDisplayToScreenMatrix =
                 getVirtualDisplayToScreenMatrixLocked(cs.mSelfReportedDisplayId,
                         mDisplayIdToShowIme);
+        // Override the locale hints if the app is running on a virtual device.
+        if (mVdmInternal == null) {
+            mVdmInternal = LocalServices.getService(VirtualDeviceManagerInternal.class);
+        }
+        if (mVdmInternal != null && editorInfo.hintLocales == null) {
+            LocaleList hintsFromVirtualDevice = mVdmInternal.getPreferredLocaleListForUid(cs.mUid);
+            if (hintsFromVirtualDevice != null) {
+                editorInfo.hintLocales = hintsFromVirtualDevice;
+            }
+        }
         mCurEditorInfo = editorInfo;
 
         // If configured, we want to avoid starting up the IME if it is not supposed to be showing
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 90f2a6a..2253a9a 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -375,7 +375,7 @@
      * @param profileUserId  profile user Id
      * @param profileUserPassword  profile original password (when it has separated lock).
      */
-    public void tieProfileLockIfNecessary(int profileUserId,
+    private void tieProfileLockIfNecessary(int profileUserId,
             LockscreenCredential profileUserPassword) {
         if (DEBUG) Slog.v(TAG, "Check child profile lock for user: " + profileUserId);
         // Only for profiles that shares credential with parent
@@ -713,7 +713,8 @@
             userHandle);
     }
 
-    public void onCleanupUser(int userId) {
+    @VisibleForTesting
+    void onCleanupUser(int userId) {
         hideEncryptionNotification(new UserHandle(userId));
         // User is stopped with its CE key evicted. Restore strong auth requirement to the default
         // flags after boot since stopping and restarting a user later is equivalent to rebooting
@@ -725,7 +726,7 @@
         }
     }
 
-    public void onStartUser(final int userId) {
+    private void onStartUser(final int userId) {
         maybeShowEncryptionNotificationForUser(userId, "user started");
     }
 
@@ -779,7 +780,7 @@
         }
     }
 
-    public void onUnlockUser(final int userId) {
+    private void onUnlockUser(final int userId) {
         // Perform tasks which require locks in LSS on a handler, as we are callbacks from
         // ActivityManager.unlockUser()
         mHandler.post(new Runnable() {
@@ -1222,7 +1223,7 @@
      * {@link #CREDENTIAL_TYPE_PATTERN}, {@link #CREDENTIAL_TYPE_PIN} and
      * {@link #CREDENTIAL_TYPE_PASSWORD}
      */
-    public int getCredentialTypeInternal(int userId) {
+    private int getCredentialTypeInternal(int userId) {
         if (userId == USER_FRP) {
             return getFrpCredentialType();
         }
@@ -1687,10 +1688,6 @@
     }
 
     private void onPostPasswordChanged(LockscreenCredential newCredential, int userHandle) {
-        if (userHandle == UserHandle.USER_SYSTEM && isDeviceEncryptionEnabled() &&
-            shouldEncryptWithCredentials() && newCredential.isNone()) {
-            setCredentialRequiredToDecrypt(false);
-        }
         if (newCredential.isPattern()) {
             setBoolean(LockPatternUtils.PATTERN_EVER_CHOSEN_KEY, true, userHandle);
         }
@@ -1768,24 +1765,6 @@
         return mInjector.getDevicePolicyManager().getPasswordHistoryLength(null, userId);
     }
 
-    private static boolean isDeviceEncryptionEnabled() {
-        return StorageManager.isEncrypted();
-    }
-
-    private boolean shouldEncryptWithCredentials() {
-        return isCredentialRequiredToDecrypt() && !isDoNotAskCredentialsOnBootSet();
-    }
-
-    private boolean isDoNotAskCredentialsOnBootSet() {
-        return mInjector.getDevicePolicyManager().getDoNotAskCredentialsOnBoot();
-    }
-
-    private boolean isCredentialRequiredToDecrypt() {
-        final int value = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, -1);
-        return value != 0;
-    }
-
     private UserManager getUserManagerFromCache(int userId) {
         UserHandle userHandle = UserHandle.of(userId);
         if (mUserManagerCache.containsKey(userHandle)) {
@@ -1802,17 +1781,11 @@
         }
     }
 
+    @VisibleForTesting /** Note: this method is overridden in unit tests */
     protected boolean isCredentialSharableWithParent(int userId) {
         return getUserManagerFromCache(userId).isCredentialSharableWithParent();
     }
 
-    private void setCredentialRequiredToDecrypt(boolean required) {
-        if (isDeviceEncryptionEnabled()) {
-            Settings.Global.putInt(mContext.getContentResolver(),
-                    Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, required ? 1 : 0);
-        }
-    }
-
     /** Register the given WeakEscrowTokenRemovedListener. */
     @Override
     public boolean registerWeakEscrowTokenRemovedListener(
@@ -2560,7 +2533,7 @@
         }
     }
 
-    protected synchronized IGateKeeperService getGateKeeperService() {
+    private synchronized IGateKeeperService getGateKeeperService() {
         if (mGateKeeperService != null) {
             return mGateKeeperService;
         }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
index 1203769..b77670d 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
@@ -44,6 +44,7 @@
 /**
  * Keeps track of requests for strong authentication.
  */
+@VisibleForTesting // public visibility is needed for Mockito
 public class LockSettingsStrongAuth {
 
     private static final String TAG = "LockSettings";
diff --git a/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java b/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java
index e43d4e8..ddc0e54 100644
--- a/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java
+++ b/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java
@@ -26,6 +26,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.widget.LockscreenCredential;
 
@@ -60,6 +61,7 @@
  * <p> The encrypted credential is stored in-memory only so the cache does not persist across
  * reboots.
  */
+@VisibleForTesting // public visibility is needed for Mockito
 public class ManagedProfilePasswordCache {
 
     private static final String TAG = "ManagedProfilePasswordCache";
diff --git a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
index 21fb403..b039d5e 100644
--- a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
+++ b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
@@ -43,7 +43,7 @@
  * If a /metadata partition does not exist, GSIs are not supported, and PasswordSlotManager will
  * simply not persist the slot mapping.
  */
-public class PasswordSlotManager {
+class PasswordSlotManager {
     private static final String TAG = "PasswordSlotManager";
 
     private static final String GSI_RUNNING_PROP = "ro.gsid.image_running";
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
index cb6e43c..b283726 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
@@ -49,7 +49,7 @@
 import javax.crypto.spec.GCMParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
-public class SyntheticPasswordCrypto {
+class SyntheticPasswordCrypto {
     private static final String TAG = "SyntheticPasswordCrypto";
     private static final int AES_GCM_KEY_SIZE = 32; // AES-256-GCM
     private static final int AES_GCM_IV_SIZE = 12;
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 66ce429..67d1c71 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -112,7 +112,7 @@
  *       WEAVER_SLOT: Contains the Weaver slot number used by this protector.  Only exists if the
  *                    protector uses Weaver.
  */
-public class SyntheticPasswordManager {
+class SyntheticPasswordManager {
     private static final String SP_BLOB_NAME = "spblob";
     private static final String SP_E0_NAME = "e0";
     private static final String SP_P1_NAME = "p1";
@@ -394,7 +394,7 @@
         }
     }
 
-    static class SyntheticPasswordBlob {
+    private static class SyntheticPasswordBlob {
         byte mVersion;
         byte mProtectorType;
         byte[] mContent;
@@ -431,7 +431,7 @@
     static final int TOKEN_TYPE_STRONG = 0;
     static final int TOKEN_TYPE_WEAK = 1;
 
-    static class TokenData {
+    private static class TokenData {
         byte[] secdiscardableOnDisk;
         byte[] weaverSecret;
         byte[] aggregatedSecret;
@@ -1623,7 +1623,7 @@
         SyntheticPasswordCrypto.destroyProtectorKey(keyAlias);
     }
 
-    public static long generateProtectorId() {
+    private static long generateProtectorId() {
         SecureRandom rng = new SecureRandom();
         long result;
         do {
@@ -1685,15 +1685,17 @@
         return Arrays.copyOf(key, mWeaverConfig.keySize);
     }
 
+    @VisibleForTesting
     protected long sidFromPasswordHandle(byte[] handle) {
         return nativeSidFromPasswordHandle(handle);
     }
 
+    @VisibleForTesting
     protected byte[] scrypt(byte[] password, byte[] salt, int n, int r, int p, int outLen) {
         return new Scrypt().scrypt(password, salt, n, r, p, outLen);
     }
 
-    native long nativeSidFromPasswordHandle(byte[] handle);
+    private native long nativeSidFromPasswordHandle(byte[] handle);
 
     @VisibleForTesting
     static byte[] bytesToHex(byte[] bytes) {
diff --git a/services/core/java/com/android/server/locksettings/VersionedPasswordMetrics.java b/services/core/java/com/android/server/locksettings/VersionedPasswordMetrics.java
index b06e381..b10dfb2 100644
--- a/services/core/java/com/android/server/locksettings/VersionedPasswordMetrics.java
+++ b/services/core/java/com/android/server/locksettings/VersionedPasswordMetrics.java
@@ -25,7 +25,7 @@
  * A versioned and serializable wrapper around {@link PasswordMetrics},
  * for long-term persistence on disk.
  */
-public class VersionedPasswordMetrics {
+class VersionedPasswordMetrics {
     private static final int VERSION_1 = 1;
 
     private final PasswordMetrics mMetrics;
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index b611b5d..25a39cc 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -21,6 +21,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.content.om.OverlayInfo;
 import android.content.om.OverlayableInfo;
 import android.os.Build.VERSION_CODES;
@@ -32,6 +33,7 @@
 import android.util.Slog;
 
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 
 import java.io.IOException;
 import java.lang.annotation.Retention;
@@ -99,16 +101,17 @@
      * </ul>.
      */
     @IdmapStatus int createIdmap(@NonNull final AndroidPackage targetPackage,
-            @NonNull final AndroidPackage overlayPackage, String overlayBasePath,
-            String overlayName, int userId) {
+            @NonNull PackageState overlayPackageState, @NonNull final AndroidPackage overlayPackage,
+            String overlayBasePath, String overlayName, @UserIdInt int userId) {
         if (DEBUG) {
             Slog.d(TAG, "create idmap for " + targetPackage.getPackageName() + " and "
                     + overlayPackage.getPackageName());
         }
-        final String targetPath = targetPackage.getBaseApkPath();
+        final String targetPath = targetPackage.getSplits().get(0).getPath();
         try {
-            int policies = calculateFulfilledPolicies(targetPackage, overlayPackage, userId);
-            boolean enforce = enforceOverlayable(overlayPackage);
+            int policies = calculateFulfilledPolicies(targetPackage, overlayPackageState,
+                    overlayPackage, userId);
+            boolean enforce = enforceOverlayable(overlayPackageState, overlayPackage);
             if (mIdmapDaemon.verifyIdmap(targetPath, overlayBasePath, overlayName, policies,
                     enforce, userId)) {
                 return IDMAP_IS_VERIFIED;
@@ -174,13 +177,14 @@
      * Checks if overlayable and policies should be enforced on the specified overlay for backwards
      * compatibility with pre-Q overlays.
      */
-    private boolean enforceOverlayable(@NonNull final AndroidPackage overlayPackage) {
+    private boolean enforceOverlayable(@NonNull PackageState overlayPackageState,
+            @NonNull final AndroidPackage overlayPackage) {
         if (overlayPackage.getTargetSdkVersion() >= VERSION_CODES.Q) {
             // Always enforce policies for overlays targeting Q+.
             return true;
         }
 
-        if (overlayPackage.isVendor()) {
+        if (overlayPackageState.isVendor()) {
             // If the overlay is on a pre-Q vendor partition, do not enforce overlayable
             // restrictions on this overlay because the pre-Q platform has no understanding of
             // overlayable.
@@ -189,14 +193,15 @@
 
         // Do not enforce overlayable restrictions on pre-Q overlays that are signed with the
         // platform signature or that are preinstalled.
-        return !(overlayPackage.isSystem() || overlayPackage.isSignedWithPlatformKey());
+        return !(overlayPackageState.isSystem() || overlayPackage.isSignedWithPlatformKey());
     }
 
     /**
      * Retrieves a bitmask for idmap2 that represents the policies the overlay fulfills.
      */
     private int calculateFulfilledPolicies(@NonNull final AndroidPackage targetPackage,
-            @NonNull final AndroidPackage overlayPackage, int userId)  {
+            @NonNull PackageState overlayPackageState, @NonNull final AndroidPackage overlayPackage,
+            @UserIdInt int userId)  {
         int fulfilledPolicies = OverlayablePolicy.PUBLIC;
 
         // Overlay matches target signature
@@ -221,28 +226,28 @@
         }
 
         // Vendor partition (/vendor)
-        if (overlayPackage.isVendor()) {
+        if (overlayPackageState.isVendor()) {
             return fulfilledPolicies | OverlayablePolicy.VENDOR_PARTITION;
         }
 
         // Product partition (/product)
-        if (overlayPackage.isProduct()) {
+        if (overlayPackageState.isProduct()) {
             return fulfilledPolicies | OverlayablePolicy.PRODUCT_PARTITION;
         }
 
         // Odm partition (/odm)
-        if (overlayPackage.isOdm()) {
+        if (overlayPackageState.isOdm()) {
             return fulfilledPolicies | OverlayablePolicy.ODM_PARTITION;
         }
 
         // Oem partition (/oem)
-        if (overlayPackage.isOem()) {
+        if (overlayPackageState.isOem()) {
             return fulfilledPolicies | OverlayablePolicy.OEM_PARTITION;
         }
 
         // System_ext partition (/system_ext) is considered as system
         // Check this last since every partition except for data is scanned as system in the PMS.
-        if (overlayPackage.isSystem() || overlayPackage.isSystemExt()) {
+        if (overlayPackageState.isSystem() || overlayPackageState.isSystemExt()) {
             return fulfilledPolicies | OverlayablePolicy.SYSTEM_PARTITION;
         }
 
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index c35d8631..015b7fd 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -28,7 +28,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
-import com.android.server.pm.pkg.AndroidPackage;
 
 import java.io.IOException;
 import java.util.List;
@@ -112,13 +111,13 @@
         }
 
         final String targetPackageName = overlayInfo.targetPackageName;
-        final AndroidPackage targetPkgInfo = mPackageManager.getPackageForUser(targetPackageName,
-                userId);
-        if (targetPkgInfo == null) {
+        var targetPkgState = mPackageManager.getPackageStateForUser(targetPackageName, userId);
+        var targetPkg = targetPkgState == null ? null : targetPkgState.getAndroidPackage();
+        if (targetPkg == null) {
             return ActorState.TARGET_NOT_FOUND;
         }
 
-        if (targetPkgInfo.isDebuggable()) {
+        if (targetPkg.isDebuggable()) {
             return ActorState.ALLOWED;
         }
 
@@ -189,13 +188,13 @@
         }
 
         String actorPackageName = actorUriPair.first;
-        AndroidPackage actorPackage = mPackageManager.getPackageForUser(actorPackageName, userId);
-        if (actorPackage == null) {
+        var actorPackageState = mPackageManager.getPackageStateForUser(actorPackageName, userId);
+        if (actorPackageState == null || actorPackageState.getAndroidPackage() == null) {
             return ActorState.ACTOR_NOT_FOUND;
         }
 
         // Currently only pre-installed apps can be actors
-        if (!actorPackage.isSystem()) {
+        if (!actorPackageState.isSystem()) {
             return ActorState.ACTOR_NOT_PREINSTALLED;
         }
 
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 9022a885..062f0fc 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -91,7 +91,7 @@
 import com.android.server.SystemService;
 import com.android.server.pm.KnownPackages;
 import com.android.server.pm.UserManagerService;
-import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 
 import libcore.util.EmptyArray;
 
@@ -426,9 +426,9 @@
                 traceBegin(TRACE_TAG_RRO, "OMS#onPackageAdded " + packageName);
                 for (final int userId : userIds) {
                     synchronized (mLock) {
-                        final AndroidPackage pkg = mPackageManager.onPackageAdded(
-                                packageName, userId);
-                        if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) {
+                        var packageState = mPackageManager.onPackageAdded(packageName, userId);
+                        if (packageState != null && !mPackageManager.isInstantApp(packageName,
+                                userId)) {
                             try {
                                 updateTargetPackagesLocked(
                                         mImpl.onPackageAdded(packageName, userId));
@@ -449,9 +449,9 @@
                 traceBegin(TRACE_TAG_RRO, "OMS#onPackageChanged " + packageName);
                 for (int userId : userIds) {
                     synchronized (mLock) {
-                        final AndroidPackage pkg = mPackageManager.onPackageUpdated(
-                                packageName, userId);
-                        if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) {
+                        var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+                        if (packageState != null && !mPackageManager.isInstantApp(packageName,
+                                userId)) {
                             try {
                                 updateTargetPackagesLocked(
                                         mImpl.onPackageChanged(packageName, userId));
@@ -472,9 +472,9 @@
                 traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplacing " + packageName);
                 for (int userId : userIds) {
                     synchronized (mLock) {
-                        final AndroidPackage pkg = mPackageManager.onPackageUpdated(
-                                packageName, userId);
-                        if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) {
+                        var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+                        if (packageState != null && !mPackageManager.isInstantApp(packageName,
+                                userId)) {
                             try {
                                 updateTargetPackagesLocked(mImpl.onPackageReplacing(packageName,
                                         systemUpdateUninstall, userId));
@@ -495,9 +495,9 @@
                 traceBegin(TRACE_TAG_RRO, "OMS#onPackageReplaced " + packageName);
                 for (int userId : userIds) {
                     synchronized (mLock) {
-                        final AndroidPackage pkg = mPackageManager.onPackageUpdated(
-                                packageName, userId);
-                        if (pkg != null && !mPackageManager.isInstantApp(packageName, userId)) {
+                        var packageState = mPackageManager.onPackageUpdated(packageName, userId);
+                        if (packageState != null && !mPackageManager.isInstantApp(packageName,
+                                userId)) {
                             try {
                                 updateTargetPackagesLocked(
                                         mImpl.onPackageReplaced(packageName, userId));
@@ -1123,11 +1123,11 @@
     };
 
     private static final class PackageManagerHelperImpl implements PackageManagerHelper {
-        private static class AndroidPackageUsers {
-            private AndroidPackage mPackage;
+        private static class PackageStateUsers {
+            private PackageState mPackageState;
             private final Set<Integer> mInstalledUsers = new ArraySet<>();
-            private AndroidPackageUsers(@NonNull AndroidPackage pkg) {
-                this.mPackage = pkg;
+            private PackageStateUsers(@NonNull PackageState packageState) {
+                this.mPackageState = packageState;
             }
         }
         private final Context mContext;
@@ -1139,7 +1139,7 @@
         // intent, querying the PackageManagerService for the actual current
         // state may lead to contradictions within OMS. Better then to lag
         // behind until all pending intents have been processed.
-        private final ArrayMap<String, AndroidPackageUsers> mCache = new ArrayMap<>();
+        private final ArrayMap<String, PackageStateUsers> mCache = new ArrayMap<>();
         private final Set<Integer> mInitializedUsers = new ArraySet<>();
 
         PackageManagerHelperImpl(Context context) {
@@ -1155,18 +1155,22 @@
          * @return a map of package name to all packages installed in the user
          */
         @NonNull
-        public ArrayMap<String, AndroidPackage> initializeForUser(final int userId) {
+        public ArrayMap<String, PackageState> initializeForUser(final int userId) {
             if (!mInitializedUsers.contains(userId)) {
                 mInitializedUsers.add(userId);
-                mPackageManagerInternal.forEachInstalledPackage(
-                        (pkg) -> addPackageUser(pkg, userId), userId);
+                mPackageManagerInternal.forEachPackageState((packageState -> {
+                    if (packageState.getPkg() != null
+                            && packageState.getUserStateOrDefault(userId).isInstalled()) {
+                        addPackageUser(packageState, userId);
+                    }
+                }));
             }
 
-            final ArrayMap<String, AndroidPackage> userPackages = new ArrayMap<>();
+            final ArrayMap<String, PackageState> userPackages = new ArrayMap<>();
             for (int i = 0, n = mCache.size(); i < n; i++) {
-                final AndroidPackageUsers pkg = mCache.valueAt(i);
+                final PackageStateUsers pkg = mCache.valueAt(i);
                 if (pkg.mInstalledUsers.contains(userId)) {
-                    userPackages.put(mCache.keyAt(i), pkg.mPackage);
+                    userPackages.put(mCache.keyAt(i), pkg.mPackageState);
                 }
             }
             return userPackages;
@@ -1174,11 +1178,11 @@
 
         @Override
         @Nullable
-        public AndroidPackage getPackageForUser(@NonNull final String packageName,
+        public PackageState getPackageStateForUser(@NonNull final String packageName,
                 final int userId) {
-            final AndroidPackageUsers pkg = mCache.get(packageName);
+            final PackageStateUsers pkg = mCache.get(packageName);
             if (pkg != null && pkg.mInstalledUsers.contains(userId)) {
-                return pkg.mPackage;
+                return pkg.mPackageState;
             }
             try {
                 if (!mPackageManager.isPackageAvailable(packageName, userId)) {
@@ -1193,9 +1197,9 @@
         }
 
         @NonNull
-        private AndroidPackage addPackageUser(@NonNull final String packageName,
+        private PackageState addPackageUser(@NonNull final String packageName,
                 final int user) {
-            final AndroidPackage pkg = mPackageManagerInternal.getPackage(packageName);
+            final PackageState pkg = mPackageManagerInternal.getPackageStateInternal(packageName);
             if (pkg == null) {
                 Slog.w(TAG, "Android package for '" + packageName + "' could not be found;"
                         + " continuing as if package was never added", new Throwable());
@@ -1205,23 +1209,23 @@
         }
 
         @NonNull
-        private AndroidPackage addPackageUser(@NonNull final AndroidPackage pkg,
+        private PackageState addPackageUser(@NonNull final PackageState pkg,
                 final int user) {
-            AndroidPackageUsers pkgUsers = mCache.get(pkg.getPackageName());
+            PackageStateUsers pkgUsers = mCache.get(pkg.getPackageName());
             if (pkgUsers == null) {
-                pkgUsers = new AndroidPackageUsers(pkg);
+                pkgUsers = new PackageStateUsers(pkg);
                 mCache.put(pkg.getPackageName(), pkgUsers);
             } else {
-                pkgUsers.mPackage = pkg;
+                pkgUsers.mPackageState = pkg;
             }
             pkgUsers.mInstalledUsers.add(user);
-            return pkgUsers.mPackage;
+            return pkgUsers.mPackageState;
         }
 
 
         @NonNull
         private void removePackageUser(@NonNull final String packageName, final int user) {
-            final AndroidPackageUsers pkgUsers = mCache.get(packageName);
+            final PackageStateUsers pkgUsers = mCache.get(packageName);
             if (pkgUsers == null) {
                 return;
             }
@@ -1229,20 +1233,20 @@
         }
 
         @NonNull
-        private void removePackageUser(@NonNull final AndroidPackageUsers pkg, final int user) {
+        private void removePackageUser(@NonNull final PackageStateUsers pkg, final int user) {
             pkg.mInstalledUsers.remove(user);
             if (pkg.mInstalledUsers.isEmpty()) {
-                mCache.remove(pkg.mPackage.getPackageName());
+                mCache.remove(pkg.mPackageState.getPackageName());
             }
         }
 
         @Nullable
-        public AndroidPackage onPackageAdded(@NonNull final String packageName, final int userId) {
+        public PackageState onPackageAdded(@NonNull final String packageName, final int userId) {
             return addPackageUser(packageName, userId);
         }
 
         @Nullable
-        public AndroidPackage onPackageUpdated(@NonNull final String packageName,
+        public PackageState onPackageUpdated(@NonNull final String packageName,
                 final int userId) {
             return addPackageUser(packageName, userId);
         }
@@ -1289,14 +1293,15 @@
         public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
                 @NonNull String targetOverlayableName, int userId)
                 throws IOException {
-            final AndroidPackage packageInfo = getPackageForUser(packageName, userId);
-            if (packageInfo == null) {
+            var packageState = getPackageStateForUser(packageName, userId);
+            var pkg = packageState == null ? null : packageState.getAndroidPackage();
+            if (pkg == null) {
                 throw new IOException("Unable to get target package");
             }
 
             ApkAssets apkAssets = null;
             try {
-                apkAssets = ApkAssets.loadFromPath(packageInfo.getBaseApkPath());
+                apkAssets = ApkAssets.loadFromPath(pkg.getSplits().get(0).getPath());
                 return apkAssets.getOverlayableInfo(targetOverlayableName);
             } finally {
                 if (apkAssets != null) {
@@ -1311,14 +1316,15 @@
         @Override
         public boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
                 throws IOException {
-            AndroidPackage packageInfo = getPackageForUser(targetPackageName, userId);
-            if (packageInfo == null) {
+            var packageState = getPackageStateForUser(targetPackageName, userId);
+            var pkg = packageState == null ? null : packageState.getAndroidPackage();
+            if (pkg == null) {
                 throw new IOException("Unable to get target package");
             }
 
             ApkAssets apkAssets = null;
             try {
-                apkAssets = ApkAssets.loadFromPath(packageInfo.getBaseApkPath());
+                apkAssets = ApkAssets.loadFromPath(pkg.getSplits().get(0).getPath());
                 return apkAssets.definesOverlayable();
             } finally {
                 if (apkAssets != null) {
@@ -1370,8 +1376,8 @@
 
             for (int i = 0, n = mCache.size(); i < n; i++) {
                 final String packageName = mCache.keyAt(i);
-                final AndroidPackageUsers pkg = mCache.valueAt(i);
-                pw.print(TAB1 + packageName + ": " + pkg.mPackage + " users=");
+                final PackageStateUsers pkg = mCache.valueAt(i);
+                pw.print(TAB1 + packageName + ": " + pkg.mPackageState + " users=");
                 pw.println(TextUtils.join(", ", pkg.mInstalledUsers));
             }
         }
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 9d5830c..1beba9f 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -50,6 +50,7 @@
 import com.android.internal.content.om.OverlayConfig;
 import com.android.internal.util.CollectionUtils;
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -72,7 +73,7 @@
  */
 final class OverlayManagerServiceImpl {
     /**
-     * @deprecated Not used. See {@link android.content.om.OverlayInfo#STATE_TARGET_UPGRADING}.
+     * @deprecated Not used. See {@link OverlayInfo#STATE_TARGET_IS_BEING_REPLACED}.
      */
     @Deprecated
     private static final int FLAG_TARGET_IS_BEING_REPLACED = 1 << 0;
@@ -164,14 +165,15 @@
 
         // Remove the settings of all overlays that are no longer installed for this user.
         final ArraySet<UserPackage> updatedTargets = new ArraySet<>();
-        final ArrayMap<String, AndroidPackage> userPackages = mPackageManager.initializeForUser(
+        final ArrayMap<String, PackageState> userPackages = mPackageManager.initializeForUser(
                 newUserId);
         CollectionUtils.addAll(updatedTargets, removeOverlaysForUser(
                 (info) -> !userPackages.containsKey(info.packageName), newUserId));
 
         final ArraySet<String> overlaidByOthers = new ArraySet<>();
-        for (AndroidPackage androidPackage : userPackages.values()) {
-            final String overlayTarget = androidPackage.getOverlayTarget();
+        for (PackageState packageState : userPackages.values()) {
+            var pkg = packageState.getAndroidPackage();
+            final String overlayTarget = pkg == null ? null : pkg.getOverlayTarget();
             if (!TextUtils.isEmpty(overlayTarget)) {
                 overlaidByOthers.add(overlayTarget);
             }
@@ -180,18 +182,24 @@
         // Update the state of all installed packages containing overlays, and initialize new
         // overlays that are not currently in the settings.
         for (int i = 0, n = userPackages.size(); i < n; i++) {
-            final AndroidPackage pkg = userPackages.valueAt(i);
+            final PackageState packageState = userPackages.valueAt(i);
+            var pkg = packageState.getAndroidPackage();
+            if (pkg == null) {
+                continue;
+            }
+
+            var packageName = packageState.getPackageName();
             try {
                 CollectionUtils.addAll(updatedTargets,
                         updatePackageOverlays(pkg, newUserId, 0 /* flags */));
 
                 // When a new user is switched to for the first time, package manager must be
                 // informed of the overlay paths for all overlaid packages installed in the user.
-                if (overlaidByOthers.contains(pkg.getPackageName())) {
-                    updatedTargets.add(UserPackage.of(newUserId, pkg.getPackageName()));
+                if (overlaidByOthers.contains(packageName)) {
+                    updatedTargets.add(UserPackage.of(newUserId, packageName));
                 }
             } catch (OperationFailedException e) {
-                Slog.e(TAG, "failed to initialize overlays of '" + pkg.getPackageName()
+                Slog.e(TAG, "failed to initialize overlays of '" + packageName
                         + "' for user " + newUserId + "", e);
             }
         }
@@ -364,7 +372,7 @@
                 }
 
                 currentInfo = mSettings.init(overlay, userId, pkg.getOverlayTarget(),
-                        pkg.getOverlayTargetOverlayableName(), pkg.getBaseApkPath(),
+                        pkg.getOverlayTargetOverlayableName(), pkg.getSplits().get(0).getPath(),
                         isPackageConfiguredMutable(pkg),
                         isPackageConfiguredEnabled(pkg),
                         getPackageConfiguredPriority(pkg), pkg.getOverlayCategory(),
@@ -401,7 +409,8 @@
                 updateOverlaysForTarget(pkgName, userId, flags));
 
         // Realign the overlay settings with PackageManager's view of the package.
-        final AndroidPackage pkg = mPackageManager.getPackageForUser(pkgName, userId);
+        final PackageState packageState = mPackageManager.getPackageStateForUser(pkgName, userId);
+        var pkg = packageState == null ? null : packageState.getAndroidPackage();
         if (pkg == null) {
             return onPackageRemoved(pkgName, userId);
         }
@@ -790,10 +799,15 @@
     private boolean updateState(@NonNull final CriticalOverlayInfo info,
             final int userId, final int flags) throws OverlayManagerSettings.BadKeyException {
         final OverlayIdentifier overlay = info.getOverlayIdentifier();
-        final AndroidPackage targetPackage = mPackageManager.getPackageForUser(
-                info.getTargetPackageName(), userId);
-        final AndroidPackage overlayPackage = mPackageManager.getPackageForUser(
-                info.getPackageName(), userId);
+        var targetPackageState =
+                mPackageManager.getPackageStateForUser(info.getTargetPackageName(), userId);
+        var targetPackage =
+                targetPackageState == null ? null : targetPackageState.getAndroidPackage();
+
+        var overlayPackageState =
+                mPackageManager.getPackageStateForUser(info.getPackageName(), userId);
+        var overlayPackage =
+                overlayPackageState == null ? null : overlayPackageState.getAndroidPackage();
 
         boolean modified = false;
         if (overlayPackage == null) {
@@ -803,7 +817,8 @@
 
         modified |= mSettings.setCategory(overlay, userId, overlayPackage.getOverlayCategory());
         if (!info.isFabricated()) {
-            modified |= mSettings.setBaseCodePath(overlay, userId, overlayPackage.getBaseApkPath());
+            modified |= mSettings.setBaseCodePath(overlay, userId,
+                    overlayPackage.getSplits().get(0).getPath());
         }
 
         // Immutable RROs targeting to "android", ie framework-res.apk, are handled by native
@@ -812,7 +827,7 @@
         @IdmapManager.IdmapStatus int idmapStatus = IDMAP_NOT_EXIST;
         if (targetPackage != null && !("android".equals(info.getTargetPackageName())
                 && !isPackageConfiguredMutable(overlayPackage))) {
-            idmapStatus = mIdmapManager.createIdmap(targetPackage,
+            idmapStatus = mIdmapManager.createIdmap(targetPackage, overlayPackageState,
                     overlayPackage, updatedOverlayInfo.baseCodePath, overlay.getOverlayName(),
                     userId);
             modified |= (idmapStatus & IDMAP_IS_MODIFIED) != 0;
diff --git a/services/core/java/com/android/server/om/PackageManagerHelper.java b/services/core/java/com/android/server/om/PackageManagerHelper.java
index cbdabdd..da2fc04 100644
--- a/services/core/java/com/android/server/om/PackageManagerHelper.java
+++ b/services/core/java/com/android/server/om/PackageManagerHelper.java
@@ -24,7 +24,7 @@
 import android.util.ArrayMap;
 
 import com.android.server.pm.PackageManagerServiceUtils;
-import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 
 import java.io.IOException;
 import java.util.Map;
@@ -44,13 +44,13 @@
      * @return a map of package name to all packages installed in the user
      */
     @NonNull
-    ArrayMap<String, AndroidPackage> initializeForUser(final int userId);
+    ArrayMap<String, PackageState> initializeForUser(final int userId);
 
     /**
      * Retrieves the package information if it is installed for the user.
      */
     @Nullable
-    AndroidPackage getPackageForUser(@NonNull final String packageName, final int userId);
+    PackageState getPackageStateForUser(@NonNull final String packageName, final int userId);
 
     /**
      * Returns whether the package is an instant app.
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 58428ca..2fdc4cd 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -33,8 +33,8 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Slog;
 
@@ -189,10 +189,10 @@
     }
 
     /**
-     * Validates that the current user is the primary user or when bugreport is requested remotely
-     * and current user is affiliated user.
+     * Validates that the current user is an admin user or, when bugreport is requested remotely
+     * that the current user is an affiliated user.
      *
-     * @throws IllegalArgumentException if the current user is not the primary user
+     * @throws IllegalArgumentException if the current user is not an admin user
      */
     private void ensureUserCanTakeBugReport(int bugreportMode) {
         UserInfo currentUser = null;
@@ -202,20 +202,17 @@
             // Impossible to get RemoteException for an in-process call.
         }
 
-        UserInfo primaryUser = UserManager.get(mContext).getPrimaryUser();
         if (currentUser == null) {
-            logAndThrow("No current user. Only primary user is allowed to take bugreports.");
+            logAndThrow("There is no current user, so no bugreport can be requested.");
         }
-        if (primaryUser == null) {
-            logAndThrow("No primary user. Only primary user is allowed to take bugreports.");
-        }
-        if (primaryUser.id != currentUser.id) {
+
+        if (!currentUser.isAdmin()) {
             if (bugreportMode == BugreportParams.BUGREPORT_MODE_REMOTE
                     && isCurrentUserAffiliated(currentUser.id)) {
                 return;
             }
-            logAndThrow("Current user not primary user. Only primary user"
-                    + " is allowed to take bugreports.");
+            logAndThrow(TextUtils.formatSimple("Current user %s is not an admin user."
+                    + " Only admin users are allowed to take bugreport.", currentUser.id));
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index b8bdabe..ed31187 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -23,6 +23,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.os.CreateAppDataArgs;
@@ -47,6 +48,7 @@
 import com.android.server.pm.dex.ArtManagerService;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.SELinuxUtil;
 
@@ -179,13 +181,13 @@
     }
 
     private void prepareAppDataAndMigrate(@NonNull Installer.Batch batch,
-            @NonNull AndroidPackage pkg, int userId, @StorageManager.StorageFlags int flags,
-            boolean maybeMigrateAppData) {
+            @NonNull PackageState packageState, @NonNull AndroidPackage pkg, @UserIdInt int userId,
+            @StorageManager.StorageFlags int flags, boolean maybeMigrateAppData) {
         prepareAppData(batch, pkg, Process.INVALID_UID, userId, flags).thenRun(() -> {
             // Note: this code block is executed with the Installer lock
             // already held, since it's invoked as a side-effect of
             // executeBatchLI()
-            if (maybeMigrateAppData && maybeMigrateAppDataLIF(pkg, userId)) {
+            if (maybeMigrateAppData && maybeMigrateAppDataLIF(packageState, pkg, userId)) {
                 // We may have just shuffled around app data directories, so
                 // prepare them one more time
                 final Installer.Batch batchInner = new Installer.Batch();
@@ -320,8 +322,9 @@
      * CE/DE data to match the {@code defaultToDeviceProtectedStorage} flag
      * requested by the app.
      */
-    private boolean maybeMigrateAppDataLIF(AndroidPackage pkg, int userId) {
-        if (pkg.isSystem() && !StorageManager.isFileEncrypted()
+    private boolean maybeMigrateAppDataLIF(@NonNull PackageState packageState,
+            @NonNull AndroidPackage pkg, @UserIdInt int userId) {
+        if (packageState.isSystem() && !StorageManager.isFileEncrypted()
                 && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
             final int storageTarget = pkg.isDefaultToDeviceProtectedStorage()
                     ? StorageManager.FLAG_STORAGE_DE : StorageManager.FLAG_STORAGE_CE;
@@ -454,7 +457,7 @@
             }
 
             if (ps.getUserStateOrDefault(userId).isInstalled()) {
-                prepareAppDataAndMigrate(batch, ps.getPkg(), userId, flags, migrateAppData);
+                prepareAppDataAndMigrate(batch, ps, ps.getPkg(), userId, flags, migrateAppData);
                 preparedCount++;
             }
         }
@@ -529,8 +532,8 @@
                         && packageStateInternal.getUserStateOrDefault(
                                 UserHandle.USER_SYSTEM).isInstalled()) {
                     AndroidPackage pkg = packageStateInternal.getPkg();
-                    prepareAppDataAndMigrate(batch, pkg, UserHandle.USER_SYSTEM, storageFlags,
-                            true /* maybeMigrateAppData */);
+                    prepareAppDataAndMigrate(batch, packageStateInternal, pkg,
+                            UserHandle.USER_SYSTEM, storageFlags, true /* maybeMigrateAppData */);
                     count++;
                 }
             }
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index ea84283..c232b36 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -593,7 +593,7 @@
     CharSequence getHarmfulAppWarning(@NonNull String packageName, @UserIdInt int userId);
 
     /**
-     * Only keep package names that refer to {@link AndroidPackage#isSystem system} packages.
+     * Only keep package names that refer to {@link PackageState#isSystem system} packages.
      *
      * @param pkgNames The packages to filter
      *
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 06aadd92..d873736 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -130,6 +130,7 @@
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.PackageStateUtils;
 import com.android.server.pm.pkg.PackageUserStateInternal;
@@ -223,7 +224,7 @@
             }
 
             return PackageUserStateUtils.isMatch(pkgState.getUserStateOrDefault(userId),
-                    pkg.isSystem(), pkg.isEnabled(), component, flags);
+                    pkgState.isSystem(), pkg.isEnabled(), component, flags);
         }
 
         @Nullable
@@ -1571,7 +1572,8 @@
         }
 
         AndroidPackage p = mPackages.get(packageName);
-        if (matchFactoryOnly && p != null && !p.isSystem()) {
+        var packageState = mSettings.getPackage(packageName);
+        if (matchFactoryOnly && p != null && !packageState.isSystem()) {
             return null;
         }
         if (DEBUG_PACKAGE_INFO) {
@@ -1689,7 +1691,7 @@
             for (AndroidPackage p : mPackages.values()) {
                 PackageStateInternal ps = getPackageStateInternal(p.getPackageName());
                 if (listFactory) {
-                    if (!p.isSystem()) {
+                    if (!ps.isSystem()) {
                         continue;
                     }
                     PackageStateInternal psDisabled =
@@ -2566,8 +2568,9 @@
     public int getPackageUidInternal(String packageName,
             @PackageManager.PackageInfoFlagsBits long flags, int userId, int callingUid) {
         // reader
+        var packageState = mSettings.getPackage(packageName);
         final AndroidPackage p = mPackages.get(packageName);
-        if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, flags)) {
+        if (p != null && AndroidPackageUtils.isMatchForSystemOnly(packageState, flags)) {
             final PackageStateInternal ps = getPackageStateInternal(p.getPackageName(), callingUid);
             if (ps != null && ps.getUserStateOrDefault(userId).isInstalled()
                     && !shouldFilterApplication(ps, callingUid, userId)) {
@@ -3616,7 +3619,7 @@
             return null;
         }
         if (ps.getPkg() != null
-                && AndroidPackageUtils.isMatchForSystemOnly(ps.getPkg(), flags)) {
+                && AndroidPackageUtils.isMatchForSystemOnly(ps, flags)) {
             if (ps.getUserStateOrDefault(userId).isInstalled()
                     && !shouldFilterApplication(ps, callingUid, userId)) {
                 return mPermissionManager.getGidsForUid(UserHandle.getUid(userId,
@@ -5416,6 +5419,7 @@
         final int userId = UserHandle.getCallingUserId();
         for (int index = 0; index < numPackages; index++) {
             final AndroidPackage p = mPackages.valueAt(index);
+            var packageState = mSettings.getPackage(p.getPackageName());
 
             final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0)
                     && !p.isDirectBootAware();
@@ -5423,7 +5427,7 @@
                     && p.isDirectBootAware();
 
             if (p.isPersistent()
-                    && (!safeMode || p.isSystem())
+                    && (!safeMode || packageState.isSystem())
                     && (matchesUnaware || matchesAware)) {
                 PackageStateInternal ps = mSettings.getPackage(p.getPackageName());
                 if (ps != null) {
@@ -5522,7 +5526,7 @@
     }
 
     /**
-     * Only keep package names that refer to {@link AndroidPackage#isSystem system} packages.
+     * Only keep package names that refer to {@link PackageState#isSystem system} packages.
      *
      * @param pkgNames The packages to filter
      *
@@ -5542,13 +5546,13 @@
                 continue;
             }
 
-            AndroidPackage pkg = getPackage(pkgName);
-            if (pkg == null) {
+            var packageState = getPackageStateInternal(pkgName);
+            if (packageState == null || packageState.getAndroidPackage() == null) {
                 Log.w(TAG, "Could not find package " + pkgName);
                 continue;
             }
 
-            if (!pkg.isSystem()) {
+            if (!packageState.isSystem()) {
                 Log.w(TAG, pkgName + " is not system");
                 continue;
             }
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index a6def7d..9ea1807 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -536,7 +536,7 @@
                     nextUserId);
             mPm.mDomainVerificationManager.clearPackageForUser(ps.getPackageName(), nextUserId);
         }
-        mPermissionManager.onPackageUninstalled(ps.getPackageName(), ps.getAppId(), pkg,
+        mPermissionManager.onPackageUninstalled(ps.getPackageName(), ps.getAppId(), ps, pkg,
                 sharedUserPkgs, userId);
 
         if (outInfo != null) {
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 5661399..69aaa0d 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -124,21 +124,22 @@
      * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped}
      * and {@code numberOfPackagesFailed}.
      */
-    public int[] performDexOptUpgrade(List<AndroidPackage> pkgs, boolean showDialog,
+    public int[] performDexOptUpgrade(List<PackageStateInternal> packageStates, boolean showDialog,
             final int compilationReason, boolean bootComplete) {
 
         int numberOfPackagesVisited = 0;
         int numberOfPackagesOptimized = 0;
         int numberOfPackagesSkipped = 0;
         int numberOfPackagesFailed = 0;
-        final int numberOfPackagesToDexopt = pkgs.size();
+        final int numberOfPackagesToDexopt = packageStates.size();
 
-        for (AndroidPackage pkg : pkgs) {
+        for (var packageState : packageStates) {
+            var pkg = packageState.getAndroidPackage();
             numberOfPackagesVisited++;
 
             boolean useProfileForDexopt = false;
 
-            if ((mPm.isFirstBoot() || mPm.isDeviceUpgrading()) && pkg.isSystem()) {
+            if ((mPm.isFirstBoot() || mPm.isDeviceUpgrading()) && packageState.isSystem()) {
                 // Copy over initial preopt profiles since we won't get any JIT samples for methods
                 // that are already compiled.
                 File profileFile = new File(getPrebuildProfilePath(pkg));
@@ -234,7 +235,7 @@
             }
 
             if (SystemProperties.getBoolean(mPm.PRECOMPILE_LAYOUTS, false)) {
-                mPm.mArtManagerService.compileLayouts(pkg);
+                mPm.mArtManagerService.compileLayouts(packageState, pkg);
             }
 
             int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
@@ -369,13 +370,8 @@
         List<PackageStateInternal> pkgSettings =
                 getPackagesForDexopt(snapshot.getPackageStates().values(), mPm);
 
-        List<AndroidPackage> pkgs = new ArrayList<>(pkgSettings.size());
-        for (int index = 0; index < pkgSettings.size(); index++) {
-            pkgs.add(pkgSettings.get(index).getPkg());
-        }
-
         final long startTime = System.nanoTime();
-        final int[] stats = performDexOptUpgrade(pkgs, mPm.isPreNUpgrade() /* showDialog */,
+        final int[] stats = performDexOptUpgrade(pkgSettings, mPm.isPreNUpgrade() /* showDialog */,
                 causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT_AFTER_OTA,
                 false /* bootComplete */);
 
diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
index 823bc71..6825dd7 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -194,9 +194,14 @@
             }
         }
         final OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
-                consumer -> mPm.forEachPackageInternal(mPm.snapshotComputer(),
-                        pkg -> consumer.accept(pkg, pkg.isSystem(),
-                                apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
+                consumer -> mPm.forEachPackageState(mPm.snapshotComputer(),
+                        packageState -> {
+                            var pkg = packageState.getPkg();
+                            if (pkg != null) {
+                                consumer.accept(pkg, packageState.isSystem(),
+                                        apkInApexPreInstalledPaths.get(pkg.getPackageName()));
+                            }
+                        }));
 
         // do this first before mucking with mPackages for the "expecting better" case
         updateStubSystemAppsList(mStubSystemApps);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 34bb6ec..7049f9a 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -168,6 +168,7 @@
 import com.android.server.pm.permission.Permission;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.SharedLibraryWrapper;
 import com.android.server.pm.pkg.component.ComponentMutateUtils;
@@ -285,7 +286,8 @@
             pkgSetting = request.getScannedPackageSetting();
             if (originalPkgSetting != null) {
                 mPm.mSettings.addRenamedPackageLPw(
-                        AndroidPackageUtils.getRealPackageOrNull(parsedPackage),
+                        AndroidPackageUtils.getRealPackageOrNull(parsedPackage,
+                                pkgSetting.isSystem()),
                         originalPkgSetting.getPackageName());
                 mPm.mTransferredPackages.add(originalPkgSetting.getPackageName());
             } else {
@@ -502,7 +504,7 @@
                 }
             }
 
-            mPm.mPermissionManager.onPackageAdded(pkg,
+            mPm.mPermissionManager.onPackageAdded(pkgSetting,
                     (scanFlags & SCAN_AS_INSTANT_APP) != 0, oldPkg);
         }
 
@@ -1224,9 +1226,7 @@
             if (ps != null) {
                 if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
 
-                if (ps.getPkg() != null) {
-                    systemApp = ps.getPkg().isSystem();
-                }
+                systemApp = ps.isSystem();
                 request.setOriginUsers(
                         ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true));
             }
@@ -1401,17 +1401,17 @@
 
             try {
                 PackageSetting pkgSetting;
-                AndroidPackage oldPackage;
                 synchronized (mPm.mLock) {
                     pkgSetting = mPm.mSettings.getPackageLPr(pkgName);
-                    oldPackage = mPm.mPackages.get(pkgName);
                 }
                 boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null
-                        && pkgSetting.getPkgState().isUpdatedSystemApp();
+                        && pkgSetting.isUpdatedSystemApp();
                 final String abiOverride = deriveAbiOverride(request.getAbiOverride());
-                boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();
+
+                // TODO: Are these system flags actually set properly at this stage?
+                boolean isUpdatedSystemAppInferred = pkgSetting != null && pkgSetting.isSystem();
                 final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
-                        derivedAbi = mPackageAbiHelper.derivePackageAbi(parsedPackage,
+                        derivedAbi = mPackageAbiHelper.derivePackageAbi(parsedPackage, systemApp,
                         isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
                         abiOverride, ScanPackageUtils.getAppLib32InstallDir());
                 derivedAbi.first.applyTo(parsedPackage);
@@ -1443,6 +1443,7 @@
                 freezePackageForInstall(pkgName, installFlags, "installPackageLI");
         boolean shouldCloseFreezerBeforeReturn = true;
         try {
+            final PackageState oldPackageState;
             final AndroidPackage oldPackage;
             String renamedPackage;
             boolean sysPkg = false;
@@ -1454,8 +1455,9 @@
             if (replace) {
                 final String pkgName11 = parsedPackage.getPackageName();
                 synchronized (mPm.mLock) {
-                    oldPackage = mPm.mPackages.get(pkgName11);
+                    oldPackageState = mPm.mSettings.getPackageLPr(pkgName11);
                 }
+                oldPackage = oldPackageState.getAndroidPackage();
                 if (parsedPackage.isStaticSharedLibrary()) {
                     // Static libs have a synthetic package name containing the version
                     // and cannot be updated as an update would get a new package name,
@@ -1514,7 +1516,7 @@
                     }
 
                     // don't allow a system upgrade unless the upgrade hash matches
-                    if (oldPackage.getRestrictUpdateHash() != null && oldPackage.isSystem()) {
+                    if (oldPackage.getRestrictUpdateHash() != null && oldPackageState.isSystem()) {
                         final byte[] digestBytes;
                         try {
                             final MessageDigest digest = MessageDigest.getInstance("SHA-512");
@@ -1611,15 +1613,15 @@
                 removedInfo.mRemovedPackageVersionCode = oldPackage.getLongVersionCode();
                 request.setRemovedInfo(removedInfo);
 
-                sysPkg = oldPackage.isSystem();
+                sysPkg = oldPackageState.isSystem();
                 if (sysPkg) {
                     // Set the system/privileged/oem/vendor/product flags as needed
-                    final boolean privileged = oldPackage.isPrivileged();
-                    final boolean oem = oldPackage.isOem();
-                    final boolean vendor = oldPackage.isVendor();
-                    final boolean product = oldPackage.isProduct();
-                    final boolean odm = oldPackage.isOdm();
-                    final boolean systemExt = oldPackage.isSystemExt();
+                    final boolean privileged = oldPackageState.isPrivileged();
+                    final boolean oem = oldPackageState.isOem();
+                    final boolean vendor = oldPackageState.isVendor();
+                    final boolean product = oldPackageState.isProduct();
+                    final boolean odm = oldPackageState.isOdm();
+                    final boolean systemExt = oldPackageState.isSystemExt();
                     final @ParsingPackageUtils.ParseFlags int systemParseFlags = parseFlags;
                     final @PackageManagerService.ScanFlags int systemScanFlags = scanFlags
                             | SCAN_AS_SYSTEM
@@ -2040,7 +2042,7 @@
             final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
             final int userId = installRequest.getUserId();
             if (ps != null) {
-                if (pkg.isSystem()) {
+                if (ps.isSystem()) {
                     if (DEBUG_INSTALL) {
                         Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
                     }
@@ -2359,8 +2361,7 @@
 
                 // Unfortunately, the updated system app flag is only tracked on this PackageSetting
                 boolean isUpdatedSystemApp =
-                        installRequest.getScannedPackageSetting().getPkgState()
-                        .isUpdatedSystemApp();
+                        installRequest.getScannedPackageSetting().isUpdatedSystemApp();
 
                 realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
 
@@ -2703,7 +2704,7 @@
                 // Send PACKAGE_ADDED broadcast for users that see the package for the first time
                 // sendPackageAddedForNewUsers also deals with system apps
                 int appId = UserHandle.getAppId(request.getUid());
-                boolean isSystem = request.getPkg().isSystem();
+                boolean isSystem = request.isInstallSystem();
                 mPm.sendPackageAddedForNewUsers(mPm.snapshotComputer(), packageName,
                         isSystem || virtualPreload, virtualPreload /*startReceiver*/, appId,
                         firstUserIds, firstInstantUserIds, dataLoaderType);
@@ -2780,7 +2781,7 @@
                             null /*broadcastAllowList*/,
                             mBroadcastHelper.getTemporaryAppAllowlistBroadcastOptions(
                                     REASON_PACKAGE_REPLACED).toBundle());
-                } else if (launchedForRestore && !request.getPkg().isSystem()) {
+                } else if (launchedForRestore && !request.isInstallSystem()) {
                     // First-install and we did a restore, so we're responsible for the
                     // first-launch broadcast.
                     if (DEBUG_BACKUP) {
@@ -3779,9 +3780,11 @@
 
         synchronized (mPm.mLock) {
             platformPackage = mPm.getPlatformPackage();
+            var isSystemApp = AndroidPackageUtils.isSystem(parsedPackage);
             final String renamedPkgName = mPm.mSettings.getRenamedPackageLPr(
-                    AndroidPackageUtils.getRealPackageOrNull(parsedPackage));
-            realPkgName = ScanPackageUtils.getRealPackageName(parsedPackage, renamedPkgName);
+                    AndroidPackageUtils.getRealPackageOrNull(parsedPackage, isSystemApp));
+            realPkgName = ScanPackageUtils.getRealPackageName(parsedPackage, renamedPkgName,
+                    isSystemApp);
             if (realPkgName != null) {
                 ScanPackageUtils.ensurePackageRenamed(parsedPackage, renamedPkgName);
             }
@@ -3845,7 +3848,7 @@
 
         boolean isUpdatedSystemApp;
         if (installedPkgSetting != null) {
-            isUpdatedSystemApp = installedPkgSetting.getPkgState().isUpdatedSystemApp();
+            isUpdatedSystemApp = installedPkgSetting.isUpdatedSystemApp();
         } else {
             isUpdatedSystemApp = disabledPkgSetting != null;
         }
@@ -4433,7 +4436,7 @@
 
     private void assertPackageWithSharedUserIdIsPrivileged(AndroidPackage pkg)
             throws PackageManagerException {
-        if (!pkg.isPrivileged() && (pkg.getSharedUserId() != null)) {
+        if (!AndroidPackageUtils.isPrivileged(pkg) && (pkg.getSharedUserId() != null)) {
             SharedUserSetting sharedUserSetting = null;
             try {
                 synchronized (mPm.mLock) {
@@ -4461,16 +4464,17 @@
 
     private @PackageManagerService.ScanFlags int adjustScanFlags(
             @PackageManagerService.ScanFlags int scanFlags,
-            PackageSetting pkgSetting, PackageSetting disabledPkgSetting, UserHandle user,
-            AndroidPackage pkg) {
-        scanFlags = ScanPackageUtils.adjustScanFlagsWithPackageSetting(scanFlags, pkgSetting,
+            @Nullable PackageSetting existingPkgSetting,
+            @Nullable PackageSetting disabledPkgSetting, UserHandle user,
+            @NonNull AndroidPackage pkg) {
+        scanFlags = ScanPackageUtils.adjustScanFlagsWithPackageSetting(scanFlags, existingPkgSetting,
                 disabledPkgSetting, user);
 
         // Exception for privileged apps that share a user with a priv-app.
         final boolean skipVendorPrivilegeScan = ((scanFlags & SCAN_AS_VENDOR) != 0)
                 && ScanPackageUtils.getVendorPartitionVersion() < 28;
         if (((scanFlags & SCAN_AS_PRIVILEGED) == 0)
-                && !pkg.isPrivileged()
+                && !AndroidPackageUtils.isPrivileged(pkg)
                 && (pkg.getSharedUserId() != null)
                 && !skipVendorPrivilegeScan) {
             SharedUserSetting sharedUserSetting = null;
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index b600aa8..eb3b29c 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -53,7 +53,6 @@
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.util.Preconditions;
 import com.android.server.pm.parsing.PackageParser2;
-import com.android.server.pm.pkg.AndroidPackage;
 
 import libcore.io.IoUtils;
 
@@ -197,12 +196,13 @@
         }
         // Override with defaults if needed.
         Computer snapshot = mPm.snapshotComputer();
-        AndroidPackage installedPkg = snapshot.getPackage(packageName);
+        var installedPkgState = snapshot.getPackageStateInternal(packageName);
+        var installedPkg = installedPkgState == null ? null : installedPkgState.getAndroidPackage();
         if (installedPkg != null) {
             // Currently installed package which the new package is attempting to replace
             recommendedInstallLocation = InstallLocationUtils.installLocationPolicy(
                     installLocation, recommendedInstallLocation, mInstallFlags,
-                    installedPkg.isSystem(), installedPkg.isExternalStorage());
+                    installedPkgState.isSystem(), installedPkg.isExternalStorage());
         }
 
         final boolean onInt = (mInstallFlags & PackageManager.INSTALL_INTERNAL) != 0;
diff --git a/services/core/java/com/android/server/pm/MovePackageHelper.java b/services/core/java/com/android/server/pm/MovePackageHelper.java
index b66c6ac..52adc0d 100644
--- a/services/core/java/com/android/server/pm/MovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/MovePackageHelper.java
@@ -86,7 +86,7 @@
             throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package");
         }
         final AndroidPackage pkg = packageState.getPkg();
-        if (pkg.isSystem()) {
+        if (packageState.isSystem()) {
             throw new PackageManagerException(MOVE_FAILED_SYSTEM_PACKAGE,
                     "Cannot move system application");
         }
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java
index d839b14..6faf68d 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelper.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java
@@ -38,8 +38,8 @@
      * which varies depending on where and how the package was installed.
      */
     @NonNull
-    NativeLibraryPaths deriveNativeLibraryPaths(AndroidPackage pkg, boolean isUpdatedSystemApp,
-            File appLib32InstallDir);
+    NativeLibraryPaths deriveNativeLibraryPaths(AndroidPackage pkg, boolean isSystemApp,
+            boolean isUpdatedSystemApp, File appLib32InstallDir);
 
     /**
      * Calculate the abis for a bundled app. These can uniquely be determined from the contents of
@@ -54,8 +54,9 @@
      *
      * If {@code extractLibs} is true, native libraries are extracted from the app if required.
      */
-    Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, boolean isUpdatedSystemApp,
-            String cpuAbiOverride, File appLib32InstallDir) throws PackageManagerException;
+    Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, boolean isSystemApp,
+            boolean isUpdatedSystemApp, String cpuAbiOverride, File appLib32InstallDir)
+            throws PackageManagerException;
 
     /**
      * Calculates adjusted ABIs for a set of packages belonging to a shared user so that they all
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 249de3c..e254300 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -133,13 +133,13 @@
     }
 
     @Override
-    public NativeLibraryPaths deriveNativeLibraryPaths(AndroidPackage pkg,
+    public NativeLibraryPaths deriveNativeLibraryPaths(AndroidPackage pkg, boolean isSystemApp,
             boolean isUpdatedSystemApp, File appLib32InstallDir) {
         // Trying to derive the paths, thus need the raw ABI info from the parsed package, and the
         // current state in PackageSetting is irrelevant.
         return deriveNativeLibraryPaths(new Abis(AndroidPackageUtils.getRawPrimaryCpuAbi(pkg),
                 AndroidPackageUtils.getRawSecondaryCpuAbi(pkg)), appLib32InstallDir, pkg.getPath(),
-                pkg.getBaseApkPath(), pkg.isSystem(), isUpdatedSystemApp);
+                pkg.getBaseApkPath(), isSystemApp, isUpdatedSystemApp);
     }
 
     private static NativeLibraryPaths deriveNativeLibraryPaths(final Abis abis,
@@ -297,7 +297,7 @@
     }
 
     @Override
-    public Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg,
+    public Pair<Abis, NativeLibraryPaths> derivePackageAbi(AndroidPackage pkg, boolean isSystemApp,
             boolean isUpdatedSystemApp, String cpuAbiOverride, File appLib32InstallDir)
             throws PackageManagerException {
         // Give ourselves some initial paths; we'll come back for another
@@ -307,10 +307,10 @@
         final NativeLibraryPaths initialLibraryPaths = deriveNativeLibraryPaths(
                 new Abis(pkgRawPrimaryCpuAbi, pkgRawSecondaryCpuAbi),
                 appLib32InstallDir, pkg.getPath(),
-                pkg.getBaseApkPath(), pkg.isSystem(),
+                pkg.getBaseApkPath(), isSystemApp,
                 isUpdatedSystemApp);
 
-        final boolean extractLibs = shouldExtractLibs(pkg, isUpdatedSystemApp);
+        final boolean extractLibs = shouldExtractLibs(pkg, isSystemApp, isUpdatedSystemApp);
 
         final String nativeLibraryRootStr = initialLibraryPaths.nativeLibraryRootDir;
         final boolean useIsaSpecificSubdirs = initialLibraryPaths.nativeLibraryRootRequiresIsa;
@@ -467,15 +467,16 @@
         final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi);
         return new Pair<>(abis,
                 deriveNativeLibraryPaths(abis, appLib32InstallDir,
-                        pkg.getPath(), pkg.getBaseApkPath(), pkg.isSystem(),
+                        pkg.getPath(), pkg.getBaseApkPath(), isSystemApp,
                         isUpdatedSystemApp));
     }
 
-    private boolean shouldExtractLibs(AndroidPackage pkg, boolean isUpdatedSystemApp) {
+    private boolean shouldExtractLibs(AndroidPackage pkg, boolean isSystemApp,
+            boolean isUpdatedSystemApp) {
         // We shouldn't extract libs if the package is a library or if extractNativeLibs=false
         boolean extractLibs = !AndroidPackageUtils.isLibrary(pkg) && pkg.isExtractNativeLibs();
         // We shouldn't attempt to extract libs from system app when it was not updated.
-        if (pkg.isSystem() && !isUpdatedSystemApp) {
+        if (isSystemApp && !isUpdatedSystemApp) {
             extractLibs = false;
         }
         return extractLibs;
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 49f3a3c..aaf8755 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -79,6 +79,7 @@
 import com.android.server.pm.dex.PackageDexUsage;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
 
 import dalvik.system.DexFile;
@@ -472,8 +473,7 @@
             String classLoaderContext, int dexoptFlags, int uid,
             CompilerStats.PackageStats packageStats, boolean downgrade, String profileName,
             String dexMetadataPath, int compilationReason) {
-        String oatDir = getPackageOatDirIfSupported(pkg,
-                pkgSetting.getTransientState().isUpdatedSystemApp());
+        String oatDir = getPackageOatDirIfSupported(pkgSetting, pkg);
 
         int dexoptNeeded = getDexoptNeeded(pkg.getPackageName(), path, isa, compilerFilter,
                 classLoaderContext, profileAnalysisResult, downgrade, dexoptFlags, oatDir);
@@ -1026,8 +1026,9 @@
      * not needed or unsupported for the package.
      */
     @Nullable
-    private String getPackageOatDirIfSupported(AndroidPackage pkg, boolean isUpdatedSystemApp) {
-        if (!AndroidPackageUtils.canHaveOatDir(pkg, isUpdatedSystemApp)) {
+    private String getPackageOatDirIfSupported(@NonNull PackageState packageState,
+            @NonNull AndroidPackage pkg) {
+        if (!AndroidPackageUtils.canHaveOatDir(packageState, pkg)) {
             return null;
         }
         File codePath = new File(pkg.getPath());
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index f65a65d..ed4c849 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1910,8 +1910,8 @@
             return true;
         }
 
-        return !existingPkgSetting.getPkg().isSystem()
-                && !existingPkgSetting.getTransientState().isUpdatedSystemApp();
+        return !existingPkgSetting.isSystem()
+                && !existingPkgSetting.isUpdatedSystemApp();
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index 65e7ce1..cc9c1e0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -473,10 +473,10 @@
     public final ResolveInfo resolveIntentExported(Intent intent, String resolvedType,
             @PackageManager.ResolveInfoFlagsBits long flags,
             @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
-            boolean resolveForStart, int filterCallingUid) {
+            boolean resolveForStart, int filterCallingUid, int callingPid) {
         return getResolveIntentHelper().resolveIntentInternal(snapshot(),
                 intent, resolvedType, flags, privateResolveFlags, userId, resolveForStart,
-                filterCallingUid, true);
+                filterCallingUid, true, callingPid);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index bf6bb22..a52ed8b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1574,7 +1574,7 @@
                     Slog.e(TAG, "Failed to find package " + packageName);
                     return;
                 }
-                final String newSeInfo = SELinuxMMAC.getSeInfo(pkg, sharedUser,
+                final String newSeInfo = SELinuxMMAC.getSeInfo(packageState, pkg, sharedUser,
                         m.mInjector.getCompatibility());
 
                 if (!newSeInfo.equals(oldSeInfo)) {
@@ -1905,7 +1905,8 @@
         mPreferredActivityHelper = new PreferredActivityHelper(this);
         mResolveIntentHelper = new ResolveIntentHelper(mContext, mPreferredActivityHelper,
                 injector.getCompatibility(), mUserManager, mDomainVerificationManager,
-                mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity);
+                mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity,
+                injector.getBackgroundHandler());
         mDexOptHelper = new DexOptHelper(this);
         mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mBroadcastHelper,
                 mProtectedPackages);
@@ -2241,20 +2242,19 @@
             mRequiredSdkSandboxPackage = getRequiredSdkSandboxPackageName(computer);
 
             // Initialize InstantAppRegistry's Instant App list for all users.
-            for (AndroidPackage pkg : mPackages.values()) {
-                if (pkg.isSystem()) {
-                    continue;
+            forEachPackageState(computer, packageState -> {
+                var pkg = packageState.getAndroidPackage();
+                if (pkg == null || packageState.isSystem()) {
+                    return;
                 }
                 for (int userId : userIds) {
-                    final PackageStateInternal ps =
-                            computer.getPackageStateInternal(pkg.getPackageName());
-                    if (ps == null || !ps.getUserStateOrDefault(userId).isInstantApp()
-                            || !ps.getUserStateOrDefault(userId).isInstalled()) {
+                    if (!packageState.getUserStateOrDefault(userId).isInstantApp()
+                            || !packageState.getUserStateOrDefault(userId).isInstalled()) {
                         continue;
                     }
-                    mInstantAppRegistry.addInstantApp(userId, ps.getAppId());
+                    mInstantAppRegistry.addInstantApp(userId, packageState.getAppId());
                 }
-            }
+            });
 
             mInstallerService = mInjector.getPackageInstallerService();
             final ComponentName instantAppResolverComponent = getInstantAppResolver(computer);
@@ -3685,7 +3685,7 @@
         PackageStateInternal packageState = computer.getPackageStateInternal(componentPkgName);
         if (packageState == null || packageState.getPkg() == null
                 || (!packageState.isSystem()
-                && !packageState.getTransientState().isUpdatedSystemApp())) {
+                && !packageState.isUpdatedSystemApp())) {
             throw new SecurityException(
                     "Changing the label is not allowed for " + componentName);
         }
@@ -3887,7 +3887,7 @@
             final AndroidPackage deletedPkg = pkgSetting.getPkg();
             final boolean isSystemStub = (deletedPkg != null)
                     && deletedPkg.isStub()
-                    && deletedPkg.isSystem();
+                    && pkgSetting.isSystem();
             if (isSystemStub
                     && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                     || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
@@ -6408,7 +6408,7 @@
             }
 
             AndroidPackage pkg = packageState.getPkg();
-            return pkg != null && pkg.isSystem() && pkg.isPersistent();
+            return pkg != null && packageState.isSystem() && pkg.isPersistent();
         }
 
         @Override
@@ -6967,8 +6967,7 @@
         if (packageState == null || packageState.getPkg() == null) {
             return false;
         }
-        return AndroidPackageUtils.canHaveOatDir(packageState.getPkg(),
-                packageState.getTransientState().isUpdatedSystemApp());
+        return AndroidPackageUtils.canHaveOatDir(packageState, packageState.getPkg());
     }
 
     long deleteOatArtifactsOfPackage(@NonNull Computer snapshot, String packageName) {
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 41985e38..9b6bfd9 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -46,6 +46,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.pm.parsing.PackageCacher;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.pm.pkg.AndroidPackage;
@@ -163,13 +164,16 @@
         synchronized (mPm.mLock) {
             final AndroidPackage removedPackage = mPm.mPackages.remove(packageName);
             if (removedPackage != null) {
-                cleanPackageDataStructuresLILPw(removedPackage, chatty);
+                // TODO: Use PackageState for isSystem
+                cleanPackageDataStructuresLILPw(removedPackage,
+                        AndroidPackageUtils.isSystem(removedPackage), chatty);
             }
         }
     }
 
     @GuardedBy("mPm.mLock")
-    private void cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean chatty) {
+    private void cleanPackageDataStructuresLILPw(AndroidPackage pkg, boolean isSystemApp,
+            boolean chatty) {
         mPm.mComponentResolver.removeAllComponents(pkg, chatty);
         mPermissionManager.onPackageRemoved(pkg);
         mPm.getPackageProperty().removeAllProperties(pkg);
@@ -194,7 +198,7 @@
         }
 
         r = null;
-        if (pkg.isSystem()) {
+        if (isSystemApp) {
             // Only system apps can hold shared libraries.
             final int libraryNamesSize = pkg.getLibraryNames().size();
             for (i = 0; i < libraryNamesSize; i++) {
@@ -321,7 +325,7 @@
                     List<AndroidPackage> sharedUserPkgs =
                             sus != null ? sus.getPackages() : Collections.emptyList();
                     mPermissionManager.onPackageUninstalled(packageName, deletedPs.getAppId(),
-                            deletedPkg, sharedUserPkgs, UserHandle.USER_ALL);
+                            deletedPs, deletedPkg, sharedUserPkgs, UserHandle.USER_ALL);
                     // After permissions are handled, check if the shared user can be migrated
                     if (sus != null) {
                         mPm.mSettings.checkAndConvertSharedUserSettingsLPw(sus);
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index 622c6ee..970f8ce 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -25,6 +25,8 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.IUnsafeIntentStrictModeCallback;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -42,6 +44,7 @@
 import android.content.pm.ResolveInfo;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.Trace;
@@ -53,6 +56,7 @@
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.LocalServices;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.pkg.AndroidPackage;
@@ -84,6 +88,8 @@
     private final Supplier<ResolveInfo> mResolveInfoSupplier;
     @NonNull
     private final Supplier<ActivityInfo> mInstantAppInstallerActivitySupplier;
+    @NonNull
+    private final Handler mHandler;
 
     ResolveIntentHelper(@NonNull Context context,
             @NonNull PreferredActivityHelper preferredActivityHelper,
@@ -91,7 +97,8 @@
             @NonNull DomainVerificationManagerInternal domainVerificationManager,
             @NonNull UserNeedsBadgingCache userNeedsBadgingCache,
             @NonNull Supplier<ResolveInfo> resolveInfoSupplier,
-            @NonNull Supplier<ActivityInfo> instantAppInstallerActivitySupplier) {
+            @NonNull Supplier<ActivityInfo> instantAppInstallerActivitySupplier,
+            @NonNull Handler handler) {
         mContext = context;
         mPreferredActivityHelper = preferredActivityHelper;
         mPlatformCompat = platformCompat;
@@ -100,11 +107,12 @@
         mUserNeedsBadging = userNeedsBadgingCache;
         mResolveInfoSupplier = resolveInfoSupplier;
         mInstantAppInstallerActivitySupplier = instantAppInstallerActivitySupplier;
+        mHandler = handler;
     }
 
     private static void filterNonExportedComponents(Intent intent, int filterCallingUid,
-            List<ResolveInfo> query, PlatformCompat platformCompat, String resolvedType,
-            Computer computer) {
+            int callingPid, List<ResolveInfo> query, PlatformCompat platformCompat,
+            String resolvedType, Computer computer, Handler handler) {
         if (query == null
                 || intent.getPackage() != null
                 || intent.getComponent() != null
@@ -113,6 +121,10 @@
         }
         AndroidPackage caller = computer.getPackage(filterCallingUid);
         String callerPackage = caller == null ? "Not specified" : caller.getPackageName();
+        ActivityManagerInternal activityManagerInternal = LocalServices
+                .getService(ActivityManagerInternal.class);
+        final IUnsafeIntentStrictModeCallback callback = activityManagerInternal
+                .getRegisteredStrictModeCallback(callingPid);
         for (int i = query.size() - 1; i >= 0; i--) {
             if (!query.get(i).getComponentInfo().exported) {
                 boolean hasToBeExportedToMatch = platformCompat.isChangeEnabledByUid(
@@ -130,6 +142,15 @@
                         resolvedType,
                         intent.getScheme(),
                         hasToBeExportedToMatch);
+                if (callback != null) {
+                    handler.post(() -> {
+                        try {
+                            callback.onImplicitIntentMatchedInternalComponent(intent.cloneFilter());
+                        } catch (RemoteException e) {
+                            activityManagerInternal.unregisterStrictModeCallback(callingPid);
+                        }
+                    });
+                }
                 if (!hasToBeExportedToMatch) {
                     return;
                 }
@@ -148,7 +169,7 @@
             @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
             boolean resolveForStart, int filterCallingUid) {
         return resolveIntentInternal(computer, intent, resolvedType, flags,
-                privateResolveFlags, userId, resolveForStart, filterCallingUid, false);
+                privateResolveFlags, userId, resolveForStart, filterCallingUid, false, 0);
     }
 
     /**
@@ -160,7 +181,8 @@
     public ResolveInfo resolveIntentInternal(Computer computer, Intent intent, String resolvedType,
             @PackageManager.ResolveInfoFlagsBits long flags,
             @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
-            boolean resolveForStart, int filterCallingUid, boolean exportedComponentsOnly) {
+            boolean resolveForStart, int filterCallingUid, boolean exportedComponentsOnly,
+            int callingPid) {
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
 
@@ -177,8 +199,8 @@
                     resolvedType, flags, privateResolveFlags, filterCallingUid, userId,
                     resolveForStart, true /*allowDynamicSplits*/);
             if (exportedComponentsOnly) {
-                filterNonExportedComponents(intent, filterCallingUid, query,
-                        mPlatformCompat, resolvedType, computer);
+                filterNonExportedComponents(intent, filterCallingUid, callingPid, query,
+                        mPlatformCompat, resolvedType, computer, mHandler);
             }
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index f7e04d4..2596006 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -16,6 +16,8 @@
 
 package com.android.server.pm;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.Disabled;
 import android.compat.annotation.EnabledAfter;
@@ -29,6 +31,7 @@
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.SharedUserApi;
 
 import libcore.io.IoUtils;
@@ -381,14 +384,15 @@
      * @param compatibility     the PlatformCompat service to ask about state of compat changes.
      * @return String representing the resulting seinfo.
      */
-    public static String getSeInfo(AndroidPackage pkg, SharedUserApi sharedUser,
-            PlatformCompat compatibility) {
+    public static String getSeInfo(@NonNull PackageState packageState, @NonNull AndroidPackage pkg,
+            @Nullable SharedUserApi sharedUser, @NonNull PlatformCompat compatibility) {
         final int targetSdkVersion = getTargetSdkVersionForSeInfo(pkg, sharedUser,
                 compatibility);
         // TODO(b/71593002): isPrivileged for sharedUser and appInfo should never be out of sync.
         // They currently can be if the sharedUser apps are signed with the platform key.
-        final boolean isPrivileged = (sharedUser != null)
-                ? sharedUser.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();
+        final boolean isPrivileged =
+                (sharedUser != null) ? sharedUser.isPrivileged() | packageState.isPrivileged()
+                        : packageState.isPrivileged();
         return getSeInfo(pkg, isPrivileged, targetSdkVersion);
     }
 
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 6572d7b..386e403 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -265,23 +265,24 @@
             pkgSetting.getPkgState().setUpdatedSystemApp(true);
         }
 
-        pkgSetting.getTransientState().setSeInfo(SELinuxMMAC.getSeInfo(parsedPackage,
+        pkgSetting.getTransientState().setSeInfo(SELinuxMMAC.getSeInfo(pkgSetting, parsedPackage,
                 sharedUserSetting, injector.getCompatibility()));
 
-        if (parsedPackage.isSystem()) {
+        if (pkgSetting.isSystem()) {
             configurePackageComponents(parsedPackage);
         }
 
         final String cpuAbiOverride = deriveAbiOverride(request.mCpuAbiOverride);
-        final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
+        final boolean isSystemApp = pkgSetting.isSystem();
+        final boolean isUpdatedSystemApp = pkgSetting.isUpdatedSystemApp();
 
         final File appLib32InstallDir = getAppLib32InstallDir();
         if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
             if (needToDeriveAbi) {
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
                 final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =
-                        packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp,
-                                cpuAbiOverride, appLib32InstallDir);
+                        packageAbiHelper.derivePackageAbi(parsedPackage, isSystemApp,
+                                isUpdatedSystemApp, cpuAbiOverride, appLib32InstallDir);
                 derivedAbi.first.applyTo(parsedPackage);
                 derivedAbi.second.applyTo(parsedPackage);
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -291,14 +292,13 @@
                 // structure. Try to detect abi based on directory structure.
 
                 String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
-                if (parsedPackage.isSystem() && !isUpdatedSystemApp
-                        && pkgRawPrimaryCpuAbi == null) {
+                if (isSystemApp && !isUpdatedSystemApp && pkgRawPrimaryCpuAbi == null) {
                     final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(
                             parsedPackage);
                     abis.applyTo(parsedPackage);
                     abis.applyTo(pkgSetting);
                     final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
-                            packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
+                            packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isSystemApp,
                                     isUpdatedSystemApp, appLib32InstallDir);
                     nativeLibraryPaths.applyTo(parsedPackage);
                 }
@@ -310,7 +310,7 @@
                         .setSecondaryCpuAbi(secondaryCpuAbiFromSettings);
 
                 final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
-                        packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,
+                        packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isSystemApp,
                                 isUpdatedSystemApp, appLib32InstallDir);
                 nativeLibraryPaths.applyTo(parsedPackage);
 
@@ -336,8 +336,8 @@
             // ABIs we determined during compilation, but the path will depend on the final
             // package path (after the rename away from the stage path).
             final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =
-                    packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp,
-                            appLib32InstallDir);
+                    packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isSystemApp,
+                            isUpdatedSystemApp, appLib32InstallDir);
             nativeLibraryPaths.applyTo(parsedPackage);
         }
 
@@ -398,7 +398,7 @@
         parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions()
                 .contains(android.Manifest.permission.FACTORY_TEST));
 
-        if (parsedPackage.isSystem()) {
+        if (isSystemApp) {
             pkgSetting.setIsOrphaned(true);
         }
 
@@ -428,8 +428,8 @@
         pkgSetting.setLastModifiedTime(scanFileTime);
         // TODO(b/135203078): Remove, move to constructor
         pkgSetting.setPkg(parsedPackage)
-                .setPkgFlags(PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
-                        PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting));
+                .setFlags(PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting))
+                .setPrivateFlags(PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting));
         if (parsedPackage.getLongVersionCode() != pkgSetting.getVersionCode()) {
             pkgSetting.setLongVersionCode(parsedPackage.getLongVersionCode());
         }
@@ -707,9 +707,9 @@
      * been installed under one of this package's original names.
      */
     public static @Nullable String getRealPackageName(@NonNull AndroidPackage pkg,
-            @Nullable String renamedPkgName) {
+            @Nullable String renamedPkgName, boolean isSystemApp) {
         if (isPackageRenamed(pkg, renamedPkgName)) {
-            return AndroidPackageUtils.getRealPackageOrNull(pkg);
+            return AndroidPackageUtils.getRealPackageOrNull(pkg, isSystemApp);
         }
         return null;
     }
@@ -825,7 +825,17 @@
     public static void applyPolicy(ParsedPackage parsedPackage,
             final @PackageManagerService.ScanFlags int scanFlags,
             @Nullable AndroidPackage platformPkg, boolean isUpdatedSystemApp) {
+        // TODO: In the real APIs, an updated system app is always a system app, but that may not
+        //  hold true during scan because PMS doesn't propagate the SCAN_AS_SYSTEM flag for the data
+        //  directory. This tries to emulate that behavior by using either the flag or the boolean,
+        //  but this logic is fragile. Specifically, it may affect the PackageBackwardCompatibility
+        //  checker, which switches branches based on whether an app is a system app. When install
+        //  is refactored, the scan policy flags should not be read this late and instead passed
+        //  around in the PackageSetting or a temporary object which infers these values early, so
+        //  that all further consumers agree on their values.
+        boolean isSystemApp = isUpdatedSystemApp;
         if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
+            isSystemApp = true;
             parsedPackage.setSystem(true);
             // TODO(b/135203078): Can this be done in PackageParser? Or just inferred when the flag
             //  is set during parse.
@@ -871,13 +881,14 @@
                 ) == PackageManager.SIGNATURE_MATCH))
         );
 
-        if (!parsedPackage.isSystem()) {
+        if (!isSystemApp) {
             // Only system apps can use these features.
             parsedPackage.clearOriginalPackages()
                     .clearAdoptPermissions();
         }
 
-        PackageBackwardCompatibility.modifySharedLibraries(parsedPackage, isUpdatedSystemApp);
+        PackageBackwardCompatibility.modifySharedLibraries(parsedPackage, isSystemApp,
+                isUpdatedSystemApp);
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 61a251e..9054c85 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.pm.ApplicationInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.pm.permission.LegacyPermissionState;
@@ -125,34 +124,13 @@
     }
 
     public SettingBase setFlags(int pkgFlags) {
-        this.mPkgFlags = pkgFlags
-                & (ApplicationInfo.FLAG_SYSTEM
-                        | ApplicationInfo.FLAG_EXTERNAL_STORAGE
-                        | ApplicationInfo.FLAG_TEST_ONLY);
+        this.mPkgFlags = pkgFlags;
         onChanged();
         return this;
     }
 
     public SettingBase setPrivateFlags(int pkgPrivateFlags) {
-        this.mPkgPrivateFlags = pkgPrivateFlags
-                & (ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
-                | ApplicationInfo.PRIVATE_FLAG_OEM
-                | ApplicationInfo.PRIVATE_FLAG_VENDOR
-                | ApplicationInfo.PRIVATE_FLAG_PRODUCT
-                | ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT
-                | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER
-                | ApplicationInfo.PRIVATE_FLAG_ODM);
-        onChanged();
-        return this;
-    }
-
-    /**
-     * Unconditionally set both mPkgFlags and mPkgPrivateFlags.
-     * Should not be used outside pkgSetting initialization or update.
-     */
-    SettingBase setPkgFlags(int flags, int privateFlags) {
-        this.mPkgFlags = flags;
-        this.mPkgPrivateFlags = privateFlags;
+        this.mPkgPrivateFlags = pkgPrivateFlags;
         onChanged();
         return this;
     }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 7fec0eb00f..53be787 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -165,6 +165,7 @@
 import java.util.Random;
 import java.util.Set;
 import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 
 /**
@@ -873,8 +874,8 @@
         }
         final PackageSetting dp = mDisabledSysPackages.get(name);
         // always make sure the system package code and resource paths dont change
-        if (dp == null && p.getPkg() != null && p.getPkg().isSystem()
-                && !p.getPkgState().isUpdatedSystemApp()) {
+        if (dp == null && p.getPkg() != null && p.isSystem()
+                && !p.isUpdatedSystemApp()) {
             final PackageSetting disabled;
             if (replaced) {
                 // a little trick...  when we install the new package, we don't
@@ -1057,7 +1058,8 @@
                     // Update new package state.
                     .setLastModifiedTime(codePath.lastModified())
                     .setDomainSetId(domainSetId);
-            pkgSetting.setPkgFlags(pkgFlags, pkgPrivateFlags);
+            pkgSetting.setFlags(pkgFlags)
+                    .setPrivateFlags(pkgPrivateFlags);
         } else {
             pkgSetting = new PackageSetting(pkgName, realPkgName, codePath,
                     legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
@@ -1251,7 +1253,7 @@
         newPkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
         newPkgFlags |= pkgFlags & ApplicationInfo.FLAG_SYSTEM;
         // Only set pkgFlags.
-        pkgSetting.setPkgFlags(newPkgFlags, pkgSetting.getPrivateFlags());
+        pkgSetting.setFlags(newPkgFlags);
 
         boolean wasRequiredForSystemUser = (pkgSetting.getPrivateFlags()
                 & ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0;
@@ -5634,7 +5636,7 @@
         @GuardedBy("mLock")
         // Tracking the mutations that haven't yet been written to legacy state.
         // This avoids unnecessary work when writing settings for multiple users.
-        private boolean mIsLegacyPermissionStateStale = false;
+        private AtomicBoolean mIsLegacyPermissionStateStale = new AtomicBoolean(false);
 
         @GuardedBy("mLock")
         // The mapping keys are user ids.
@@ -5725,8 +5727,8 @@
         }
 
         public void writeStateForUserAsync(int userId) {
+            mIsLegacyPermissionStateStale.set(true);
             synchronized (mLock) {
-                mIsLegacyPermissionStateStale = true;
                 final long currentTimeMillis = SystemClock.uptimeMillis();
                 final long writePermissionDelayMillis = nextWritePermissionDelayMillis();
 
@@ -5773,24 +5775,18 @@
             }
 
             Runnable writer = () -> {
-                final int version;
-                final String fingerprint;
-                final boolean isLegacyPermissionStateStale;
-                synchronized (mLock) {
-                    version = mVersions.get(userId, INITIAL_VERSION);
-                    fingerprint = mFingerprints.get(userId);
-                    isLegacyPermissionStateStale = mIsLegacyPermissionStateStale;
-                    mIsLegacyPermissionStateStale = false;
-                }
+                boolean isLegacyPermissionStateStale = mIsLegacyPermissionStateStale.getAndSet(
+                        false);
 
-                final RuntimePermissionsState runtimePermissions;
+                final Map<String, List<RuntimePermissionsState.PermissionState>>
+                        packagePermissions = new ArrayMap<>();
+                final Map<String, List<RuntimePermissionsState.PermissionState>>
+                        sharedUserPermissions = new ArrayMap<>();
                 synchronized (pmLock) {
                     if (sync || isLegacyPermissionStateStale) {
                         legacyPermissionDataProvider.writeLegacyPermissionStateTEMP();
                     }
 
-                    Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
-                            new ArrayMap<>();
                     int packagesSize = packageStates.size();
                     for (int i = 0; i < packagesSize; i++) {
                         String packageName = packageStates.keyAt(i);
@@ -5810,9 +5806,6 @@
                         }
                     }
 
-                    Map<String, List<RuntimePermissionsState.PermissionState>>
-                            sharedUserPermissions =
-                            new ArrayMap<>();
                     final int sharedUsersSize = sharedUsers.size();
                     for (int i = 0; i < sharedUsersSize; i++) {
                         String sharedUserName = sharedUsers.keyAt(i);
@@ -5822,11 +5815,13 @@
                                         sharedUserSetting.getLegacyPermissionState(), userId);
                         sharedUserPermissions.put(sharedUserName, permissions);
                     }
-
-                    runtimePermissions = new RuntimePermissionsState(version,
-                            fingerprint, packagePermissions, sharedUserPermissions);
                 }
                 synchronized (mLock) {
+                    int version = mVersions.get(userId, INITIAL_VERSION);
+                    String fingerprint = mFingerprints.get(userId);
+
+                    RuntimePermissionsState runtimePermissions = new RuntimePermissionsState(
+                            version, fingerprint, packagePermissions, sharedUserPermissions);
                     mPendingStatesToWrite.put(userId, runtimePermissions);
                 }
                 if (pmHandler != null) {
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index aa23d8d..8c2b212 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -358,7 +358,7 @@
                     continue;
                 }
 
-                if (ps.getPkg().isSystem()) {
+                if (ps.isSystem()) {
                     continue;
                 }
 
@@ -722,8 +722,8 @@
                     // it is better for the user to reinstall than to be in an limbo
                     // state. Also libs disappearing under an app should never happen
                     // - just in case.
-                    if (!pkg.isSystem() || pkgSetting.getPkgState().isUpdatedSystemApp()) {
-                        final int flags = pkgSetting.getPkgState().isUpdatedSystemApp()
+                    if (!pkgSetting.isSystem() || pkgSetting.isUpdatedSystemApp()) {
+                        final int flags = pkgSetting.isUpdatedSystemApp()
                                 ? PackageManager.DELETE_KEEP_DATA : 0;
                         synchronized (mPm.mInstallLock) {
                             mDeletePackageHelper.deletePackageLIF(pkg.getPackageName(), null, true,
@@ -842,13 +842,15 @@
         if (installRequest.getStaticSharedLibraryInfo() != null) {
             return Collections.singletonList(installRequest.getStaticSharedLibraryInfo());
         }
-        final boolean hasDynamicLibraries = parsedPackage != null && parsedPackage.isSystem()
+        boolean isSystemApp = installRequest.getScannedPackageSetting() != null
+                && installRequest.getScannedPackageSetting().isSystem();
+        final boolean hasDynamicLibraries = parsedPackage != null && isSystemApp
                 && installRequest.getDynamicSharedLibraryInfos() != null;
         if (!hasDynamicLibraries) {
             return null;
         }
         final boolean isUpdatedSystemApp = installRequest.getScannedPackageSetting() != null
-                && installRequest.getScannedPackageSetting().getPkgState().isUpdatedSystemApp();
+                && installRequest.getScannedPackageSetting().isUpdatedSystemApp();
         // We may not yet have disabled the updated package yet, so be sure to grab the
         // current setting if that's the case.
         final PackageSetting updatedSystemPs = isUpdatedSystemApp
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index fb2ba32..a7a4c4e 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -284,7 +284,7 @@
             if ((ps == null) || (ps.getPkg() == null)) {
                 continue;
             }
-            final boolean isPrivileged = isPrivileged() | ps.getPkg().isPrivileged();
+            final boolean isPrivileged = isPrivileged() | ps.isPrivileged();
             ps.getPkgState().setOverrideSeInfo(SELinuxMMAC.getSeInfo(ps.getPkg(), isPrivileged,
                     seInfoTargetSdkVersion));
             onChanged();
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e660046..b8f8c3a 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2441,9 +2441,9 @@
             mGuestRestrictions.clear();
             mGuestRestrictions.putAll(restrictions);
             final List<UserInfo> guests = getGuestUsers();
-            for (UserInfo guest : guests) {
+            for (int i = 0; i < guests.size(); i++) {
                 synchronized (mRestrictionsLock) {
-                    updateUserRestrictionsInternalLR(mGuestRestrictions, guest.id);
+                    updateUserRestrictionsInternalLR(mGuestRestrictions, guests.get(i).id);
                 }
             }
         }
@@ -3688,7 +3688,8 @@
             }
             // DISALLOW_CONFIG_WIFI was made a default guest restriction some time during version 6.
             final List<UserInfo> guestUsers = getGuestUsers();
-            for (UserInfo guestUser : guestUsers) {
+            for (int i = 0; i < guestUsers.size(); i++) {
+                final UserInfo guestUser = guestUsers.get(i);
                 if (guestUser != null && !hasUserRestriction(
                         UserManager.DISALLOW_CONFIG_WIFI, guestUser.id)) {
                     setUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, true, guestUser.id);
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 2d876ed..12c9e98 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -352,10 +352,11 @@
         final String notSystemFmt = "%s is allowlisted and present but not a system package.";
         final String overlayFmt = "%s is allowlisted unnecessarily since it's a static overlay.";
         for (String pkgName : allWhitelistedPackages) {
-            final AndroidPackage pkg = pmInt.getPackage(pkgName);
+            var packageState = pmInt.getPackageStateInternal(pkgName);
+            var pkg = packageState == null ? null : packageState.getAndroidPackage();
             if (pkg == null) {
                 warnings.add(String.format(notPresentFmt, pkgName));
-            } else if (!pkg.isSystem()) {
+            } else if (!packageState.isSystem()) {
                 warnings.add(String.format(notSystemFmt, pkgName));
             } else if (shouldUseOverlayTargetName(pkg)) {
                 warnings.add(String.format(overlayFmt, pkgName));
@@ -380,8 +381,9 @@
         // Check whether all system packages are indeed allowlisted.
         final String logMessageFmt = "System package %s is not whitelisted using "
                 + "'install-in-user-type' in SystemConfig for any user types!";
-        pmInt.forEachPackage(pkg -> {
-            if (!pkg.isSystem() || pkg.isApex()) return;
+        pmInt.forEachPackageState(packageState -> {
+            var pkg = packageState.getAndroidPackage();
+            if (pkg == null || !packageState.isSystem() || pkg.isApex()) return;
             final String pkgName = pkg.getManifestPackageName();
             if (!allWhitelistedPackages.contains(pkgName)
                     && !shouldUseOverlayTargetName(pmInt.getPackage(pkgName))) {
@@ -523,8 +525,9 @@
 
         final Set<String> installPackages = new ArraySet<>();
         final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
-        pmInt.forEachPackage(pkg -> {
-            if (!pkg.isSystem()) {
+        pmInt.forEachPackageState(packageState -> {
+            var pkg = packageState.getAndroidPackage();
+            if (pkg == null || !packageState.isSystem()) {
                 return;
             }
             if (shouldInstallPackage(pkg, mWhitelistedPackagesForUserTypes,
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 046db92..e19ebce 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -56,6 +56,7 @@
 import com.android.server.pm.PackageManagerServiceCompilerMapping;
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 
 import dalvik.system.DexFile;
 import dalvik.system.VMRuntime;
@@ -487,14 +488,14 @@
     /**
      * Compile layout resources in a given package.
      */
-    public boolean compileLayouts(AndroidPackage pkg) {
+    public boolean compileLayouts(@NonNull PackageState packageState, @NonNull AndroidPackage pkg) {
         try {
             final String packageName = pkg.getPackageName();
-            final String apkPath = pkg.getBaseApkPath();
+            final String apkPath = pkg.getSplits().get(0).getPath();
             // TODO(b/143971007): Use a cross-user directory
             File dataDir = PackageInfoUtils.getDataDir(pkg, UserHandle.myUserId());
             final String outDexFile = dataDir.getAbsolutePath() + "/code_cache/compiled_view.dex";
-            if (pkg.isPrivileged() || pkg.isUseEmbeddedDex()
+            if (packageState.isPrivileged() || pkg.isUseEmbeddedDex()
                     || pkg.isDefaultToDeviceProtectedStorage()) {
                 // Privileged apps prefer to load trusted code so they don't use compiled views.
                 // If the app is not privileged but prefers code integrity, also avoid compiling
diff --git a/services/core/java/com/android/server/pm/dex/ArtUtils.java b/services/core/java/com/android/server/pm/dex/ArtUtils.java
index 160add6..223469a 100644
--- a/services/core/java/com/android/server/pm/dex/ArtUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtUtils.java
@@ -23,7 +23,7 @@
 import com.android.server.pm.PackageDexOptimizer;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.pkg.AndroidPackage;
-import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.PackageState;
 
 import java.io.File;
 import java.util.Arrays;
@@ -39,18 +39,17 @@
      * Create the ART-representation of package info.
      */
     public static ArtPackageInfo createArtPackageInfo(
-            AndroidPackage pkg, PackageStateInternal pkgSetting) {
+            AndroidPackage pkg, PackageState packageState) {
         return new ArtPackageInfo(
                 pkg.getPackageName(),
-                Arrays.asList(getAppDexInstructionSets(pkgSetting.getPrimaryCpuAbi(),
-                        pkgSetting.getSecondaryCpuAbi())),
+                Arrays.asList(getAppDexInstructionSets(packageState.getPrimaryCpuAbi(),
+                        packageState.getSecondaryCpuAbi())),
                 AndroidPackageUtils.getAllCodePaths(pkg),
-                getOatDir(pkg, pkgSetting));
+                getOatDir(pkg, packageState));
     }
 
-    private static String getOatDir(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting) {
-        if (!AndroidPackageUtils.canHaveOatDir(pkg,
-                pkgSetting.getTransientState().isUpdatedSystemApp())) {
+    private static String getOatDir(AndroidPackage pkg, @NonNull PackageState packageState) {
+        if (!AndroidPackageUtils.canHaveOatDir(packageState, pkg)) {
             return null;
         }
         File codePath = new File(pkg.getPath());
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index a3fa25d..87805e0 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -274,7 +274,7 @@
                 final ActivityInfo[] res = new ActivityInfo[N];
                 for (int i = 0; i < N; i++) {
                     final ParsedActivity a = pkg.getActivities().get(i);
-                    if (ComponentParseUtils.isMatch(state, pkg.isSystem(), pkg.isEnabled(), a,
+                    if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), a,
                             flags)) {
                         if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(
                                 a.getName())) {
@@ -294,7 +294,7 @@
                 final ActivityInfo[] res = new ActivityInfo[size];
                 for (int i = 0; i < size; i++) {
                     final ParsedActivity a = pkg.getReceivers().get(i);
-                    if (ComponentParseUtils.isMatch(state, pkg.isSystem(), pkg.isEnabled(), a,
+                    if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), a,
                             flags)) {
                         res[num++] = generateActivityInfo(pkg, a, flags, state, applicationInfo,
                                 userId, pkgSetting);
@@ -310,7 +310,7 @@
                 final ServiceInfo[] res = new ServiceInfo[size];
                 for (int i = 0; i < size; i++) {
                     final ParsedService s = pkg.getServices().get(i);
-                    if (ComponentParseUtils.isMatch(state, pkg.isSystem(), pkg.isEnabled(), s,
+                    if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), s,
                             flags)) {
                         res[num++] = generateServiceInfo(pkg, s, flags, state, applicationInfo,
                                 userId, pkgSetting);
@@ -327,7 +327,7 @@
                 for (int i = 0; i < size; i++) {
                     final ParsedProvider pr = pkg.getProviders()
                             .get(i);
-                    if (ComponentParseUtils.isMatch(state, pkg.isSystem(), pkg.isEnabled(), pr,
+                    if (ComponentParseUtils.isMatch(state, pkgSetting.isSystem(), pkg.isEnabled(), pr,
                             flags)) {
                         res[num++] = generateProviderInfo(pkg, pr, flags, state, applicationInfo,
                                 userId, pkgSetting);
@@ -425,7 +425,7 @@
         }
 
         if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)
-                || !AndroidPackageUtils.isMatchForSystemOnly(pkg, flags)) {
+                || !AndroidPackageUtils.isMatchForSystemOnly(pkgSetting, flags)) {
             return null;
         }
 
@@ -793,7 +793,7 @@
         // If available for the target user, or trying to match uninstalled packages and it's
         // a system app.
         return PackageUserStateUtils.isAvailable(state, flags)
-                || (pkg.isSystem()
+                || (pkgSetting.isSystem()
                 && ((flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0
                 || (flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) != 0));
     }
@@ -893,7 +893,7 @@
                 | flag(pkg.isSupportsExtraLargeScreens(), ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS)
                 | flag(pkg.isResizeable(), ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS)
                 | flag(pkg.isAnyDensity(), ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)
-                | flag(pkg.isSystem(), ApplicationInfo.FLAG_SYSTEM)
+                | flag(AndroidPackageUtils.isSystem(pkg), ApplicationInfo.FLAG_SYSTEM)
                 | flag(pkg.isFactoryTest(), ApplicationInfo.FLAG_FACTORY_TEST);
 
         return appInfoFlags(pkgWithoutStateFlags, pkgSetting);
@@ -906,7 +906,7 @@
         // @formatter:off
         int flags = pkgWithoutStateFlags;
         if (pkgSetting != null) {
-            flags |= flag(pkgSetting.getTransientState().isUpdatedSystemApp(), ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
+            flags |= flag(pkgSetting.isUpdatedSystemApp(), ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
         }
         return flags;
         // @formatter:on
@@ -934,12 +934,12 @@
                 | flag(pkg.isCantSaveState(), ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)
                 | flag(pkg.isResizeableActivityViaSdkVersion(), ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION)
                 | flag(pkg.isAllowNativeHeapPointerTagging(), ApplicationInfo.PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING)
-                | flag(pkg.isSystemExt(), ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT)
-                | flag(pkg.isPrivileged(), ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
-                | flag(pkg.isOem(), ApplicationInfo.PRIVATE_FLAG_OEM)
-                | flag(pkg.isVendor(), ApplicationInfo.PRIVATE_FLAG_VENDOR)
-                | flag(pkg.isProduct(), ApplicationInfo.PRIVATE_FLAG_PRODUCT)
-                | flag(pkg.isOdm(), ApplicationInfo.PRIVATE_FLAG_ODM)
+                | flag(AndroidPackageUtils.isSystemExt(pkg), ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT)
+                | flag(AndroidPackageUtils.isPrivileged(pkg), ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
+                | flag(AndroidPackageUtils.isOem(pkg), ApplicationInfo.PRIVATE_FLAG_OEM)
+                | flag(AndroidPackageUtils.isVendor(pkg), ApplicationInfo.PRIVATE_FLAG_VENDOR)
+                | flag(AndroidPackageUtils.isProduct(pkg), ApplicationInfo.PRIVATE_FLAG_PRODUCT)
+                | flag(AndroidPackageUtils.isOdm(pkg), ApplicationInfo.PRIVATE_FLAG_ODM)
                 | flag(pkg.isSignedWithPlatformKey(), ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY);
 
         Boolean resizeableActivity = pkg.getResizeableActivity();
diff --git a/services/core/java/com/android/server/pm/parsing/library/AndroidHidlUpdater.java b/services/core/java/com/android/server/pm/parsing/library/AndroidHidlUpdater.java
index ebb96bb..8b5719a 100644
--- a/services/core/java/com/android/server/pm/parsing/library/AndroidHidlUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/AndroidHidlUpdater.java
@@ -35,13 +35,13 @@
 public class AndroidHidlUpdater extends PackageSharedLibraryUpdater {
 
     @Override
-    public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
+    public void updatePackage(ParsedPackage parsedPackage, boolean isSystemApp,
+            boolean isUpdatedSystemApp) {
         // This was the default <= P and is maintained for backwards compatibility.
         boolean isLegacy = parsedPackage.getTargetSdkVersion() <= Build.VERSION_CODES.P;
         // Only system apps use these libraries
-        boolean isSystem = parsedPackage.isSystem() || isUpdatedSystemApp;
 
-        if (isLegacy && isSystem) {
+        if (isLegacy && (isSystemApp || isUpdatedSystemApp)) {
             prefixRequiredLibrary(parsedPackage, ANDROID_HIDL_BASE);
             prefixRequiredLibrary(parsedPackage, ANDROID_HIDL_MANAGER);
         } else {
diff --git a/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java b/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java
index 6cdd4df..adaa04c 100644
--- a/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdater.java
@@ -29,7 +29,8 @@
     private static final String LIBRARY_NAME = "android.net.ipsec.ike";
 
     @Override
-    public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
+    public void updatePackage(ParsedPackage parsedPackage, boolean isSystemApp,
+            boolean isUpdatedSystemApp) {
         removeLibrary(parsedPackage, LIBRARY_NAME);
     }
 }
diff --git a/services/core/java/com/android/server/pm/parsing/library/AndroidTestBaseUpdater.java b/services/core/java/com/android/server/pm/parsing/library/AndroidTestBaseUpdater.java
index 7031dc3..3b29d1f 100644
--- a/services/core/java/com/android/server/pm/parsing/library/AndroidTestBaseUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/AndroidTestBaseUpdater.java
@@ -18,6 +18,7 @@
 import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_BASE;
 import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
 
+import android.annotation.NonNull;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
 import android.content.Context;
@@ -56,10 +57,10 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
     private static final long REMOVE_ANDROID_TEST_BASE = 133396946L;
 
-    private static boolean isChangeEnabled(AndroidPackage pkg) {
+    private static boolean isChangeEnabled(@NonNull AndroidPackage pkg, boolean isSystemApp) {
         // Do not ask platform compat for system apps to prevent a boot time regression in tests.
         // b/142558883.
-        if (!pkg.isSystem()) {
+        if (!isSystemApp) {
             IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
                     ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
             try {
@@ -74,11 +75,12 @@
     }
 
     @Override
-    public void updatePackage(ParsedPackage pkg, boolean isUpdatedSystemApp) {
+    public void updatePackage(ParsedPackage pkg, boolean isSystemApp,
+            boolean isUpdatedSystemApp) {
         // Packages targeted at <= Q expect the classes in the android.test.base library
         // to be accessible so this maintains backward compatibility by adding the
         // android.test.base library to those packages.
-        if (!isChangeEnabled(pkg)) {
+        if (!isChangeEnabled(pkg, isSystemApp)) {
             prefixRequiredLibrary(pkg, ANDROID_TEST_BASE);
         } else {
             // If a package already depends on android.test.runner then add a dependency on
diff --git a/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java b/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
index 1a2ff26..041b77b 100644
--- a/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
@@ -43,7 +43,8 @@
     }
 
     @Override
-    public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
+    public void updatePackage(ParsedPackage parsedPackage, boolean isSystemApp,
+            boolean isUpdatedSystemApp) {
         final int builtInLibCount = mSharedLibraries.size();
         for (int i = 0; i < builtInLibCount; i++) {
             updateSharedLibraryForPackage(mSharedLibraries.valueAt(i), parsedPackage);
diff --git a/services/core/java/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdater.java b/services/core/java/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdater.java
index ee3c406..b47a768 100644
--- a/services/core/java/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdater.java
@@ -29,7 +29,8 @@
     private static final String LIBRARY_NAME = "com.google.android.maps";
 
     @Override
-    public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
+    public void updatePackage(ParsedPackage parsedPackage, boolean isSystemApp,
+            boolean isUpdatedSystemApp) {
         parsedPackage.removeUsesLibrary(LIBRARY_NAME);
         parsedPackage.removeUsesOptionalLibrary(LIBRARY_NAME);
     }
diff --git a/services/core/java/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdater.java b/services/core/java/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
index 96fead2..ac65c8c 100644
--- a/services/core/java/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdater.java
@@ -37,7 +37,8 @@
     }
 
     @Override
-    public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
+    public void updatePackage(ParsedPackage parsedPackage, boolean isSystemApp,
+            boolean isUpdatedSystemApp) {
         // Packages targeted at <= O_MR1 expect the classes in the org.apache.http.legacy library
         // to be accessible so this maintains backward compatibility by adding the
         // org.apache.http.legacy library to those packages.
diff --git a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
index dc3bf78..3da7141 100644
--- a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
+++ b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
@@ -21,12 +21,12 @@
 import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
 import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
 
-import com.android.server.pm.pkg.parsing.ParsingPackage;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.SystemConfig;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -130,15 +130,16 @@
      * @param parsedPackage the {@link ParsedPackage} to modify.
      */
     @VisibleForTesting
-    public static void modifySharedLibraries(ParsedPackage parsedPackage,
+    public static void modifySharedLibraries(ParsedPackage parsedPackage, boolean isSystemApp,
             boolean isUpdatedSystemApp) {
-        INSTANCE.updatePackage(parsedPackage, isUpdatedSystemApp);
+        INSTANCE.updatePackage(parsedPackage, isSystemApp, isUpdatedSystemApp);
     }
 
     @Override
-    public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
+    public void updatePackage(ParsedPackage parsedPackage, boolean isSystemApp,
+            boolean isUpdatedSystemApp) {
         for (PackageSharedLibraryUpdater packageUpdater : mPackageUpdaters) {
-            packageUpdater.updatePackage(parsedPackage, isUpdatedSystemApp);
+            packageUpdater.updatePackage(parsedPackage, isSystemApp, isUpdatedSystemApp);
         }
     }
 
@@ -163,7 +164,8 @@
     public static class AndroidTestRunnerSplitUpdater extends PackageSharedLibraryUpdater {
 
         @Override
-        public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
+        public void updatePackage(ParsedPackage parsedPackage, boolean isSystemApp,
+                boolean isUpdatedSystemApp) {
             // android.test.runner has a dependency on android.test.mock so if android.test.runner
             // is present but android.test.mock is not then add android.test.mock.
             prefixImplicitDependency(parsedPackage, ANDROID_TEST_RUNNER, ANDROID_TEST_MOCK);
@@ -179,7 +181,8 @@
             extends PackageSharedLibraryUpdater {
 
         @Override
-        public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
+        public void updatePackage(ParsedPackage parsedPackage, boolean isSystemApp,
+                boolean isUpdatedSystemApp) {
             removeLibrary(parsedPackage, ORG_APACHE_HTTP_LEGACY);
         }
 
@@ -194,7 +197,8 @@
             extends PackageSharedLibraryUpdater {
 
         @Override
-        public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
+        public void updatePackage(ParsedPackage parsedPackage, boolean isSystemApp,
+                boolean isUpdatedSystemApp) {
             removeLibrary(parsedPackage, ANDROID_TEST_BASE);
         }
     }
diff --git a/services/core/java/com/android/server/pm/parsing/library/PackageSharedLibraryUpdater.java b/services/core/java/com/android/server/pm/parsing/library/PackageSharedLibraryUpdater.java
index 123b808..a9c22d9 100644
--- a/services/core/java/com/android/server/pm/parsing/library/PackageSharedLibraryUpdater.java
+++ b/services/core/java/com/android/server/pm/parsing/library/PackageSharedLibraryUpdater.java
@@ -38,7 +38,8 @@
      *
      * @param parsedPackage the package to update.
      */
-    public abstract void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp);
+    public abstract void updatePackage(ParsedPackage parsedPackage, boolean isSystemApp,
+            boolean isUpdatedSystemApp);
 
     static void removeLibrary(ParsedPackage parsedPackage, String libraryName) {
         parsedPackage.removeUsesLibrary(libraryName)
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageHidden.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageHidden.java
index 876bf17..1fafdf9 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageHidden.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageHidden.java
@@ -59,4 +59,19 @@
 
     // TODO(b/135203078): Hide and enforce going through PackageInfoUtils
     ApplicationInfo toAppInfoWithoutState();
+
+    // TODO: Remove these booleans and store the value directly inside PackageState
+    boolean isSystem();
+
+    boolean isSystemExt();
+
+    boolean isPrivileged();
+
+    boolean isOem();
+
+    boolean isVendor();
+
+    boolean isProduct();
+
+    boolean isOdm();
 }
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 82b5fa2..4fee84f 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
@@ -161,11 +161,12 @@
         );
     }
 
-    public static boolean canHaveOatDir(AndroidPackage pkg, boolean isUpdatedSystemApp) {
+    public static boolean canHaveOatDir(@NonNull PackageState packageState,
+            @NonNull AndroidPackage pkg) {
         // The following app types CANNOT have oat directory
         // - non-updated system apps,
         // - incrementally installed apps.
-        if (pkg.isSystem() && !isUpdatedSystemApp) {
+        if (packageState.isSystem() && !packageState.isUpdatedSystemApp()) {
             return false;
         }
         if (IncrementalManager.isIncrementalPath(pkg.getPath())) {
@@ -234,14 +235,14 @@
                 || !pkg.getLibraryNames().isEmpty();
     }
 
-    public static int getHiddenApiEnforcementPolicy(@Nullable AndroidPackage pkg,
-            @NonNull PackageStateInternal pkgSetting) {
+    public static int getHiddenApiEnforcementPolicy(@NonNull AndroidPackage pkg,
+            @NonNull PackageStateInternal packageState) {
         boolean isAllowedToUseHiddenApis;
         if (pkg == null) {
             isAllowedToUseHiddenApis = false;
         } else if (pkg.isSignedWithPlatformKey()) {
             isAllowedToUseHiddenApis = true;
-        } else if (pkg.isSystem() || pkgSetting.getTransientState().isUpdatedSystemApp()) {
+        } else if (packageState.isSystem()) {
             isAllowedToUseHiddenApis = pkg.isUsesNonSdkApi()
                     || SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(
                     pkg.getPackageName());
@@ -266,9 +267,9 @@
      * Returns false iff the provided flags include the {@link PackageManager#MATCH_SYSTEM_ONLY}
      * flag and the provided package is not a system package. Otherwise returns {@code true}.
      */
-    public static boolean isMatchForSystemOnly(AndroidPackage pkg, long flags) {
+    public static boolean isMatchForSystemOnly(@NonNull PackageState packageState, long flags) {
         if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
-            return pkg.isSystem();
+            return packageState.isSystem();
         }
         return true;
     }
@@ -300,8 +301,8 @@
      * actually renamed.
      */
     @Nullable
-    public static String getRealPackageOrNull(AndroidPackage pkg) {
-        if (pkg.getOriginalPackages().isEmpty() || !pkg.isSystem()) {
+    public static String getRealPackageOrNull(@NonNull AndroidPackage pkg, boolean isSystem) {
+        if (pkg.getOriginalPackages().isEmpty() || !isSystem) {
             return null;
         }
 
@@ -312,4 +313,60 @@
         info.versionCode = ((ParsingPackageHidden) pkg).getVersionCode();
         info.versionCodeMajor = ((ParsingPackageHidden) pkg).getVersionCodeMajor();
     }
+
+    /**
+     * @deprecated Use {@link PackageState#isSystem}
+     */
+    @Deprecated
+    public static boolean isSystem(@NonNull AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).isSystem();
+    }
+
+    /**
+     * @deprecated Use {@link PackageState#isSystemExt}
+     */
+    @Deprecated
+    public static boolean isSystemExt(@NonNull AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).isSystemExt();
+    }
+
+    /**
+     * @deprecated Use {@link PackageState#isPrivileged}
+     */
+    @Deprecated
+    public static boolean isPrivileged(@NonNull AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).isPrivileged();
+    }
+
+    /**
+     * @deprecated Use {@link PackageState#isOem}
+     */
+    @Deprecated
+    public static boolean isOem(@NonNull AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).isOem();
+    }
+
+    /**
+     * @deprecated Use {@link PackageState#isVendor}
+     */
+    @Deprecated
+    public static boolean isVendor(@NonNull AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).isVendor();
+    }
+
+    /**
+     * @deprecated Use {@link PackageState#isProduct}
+     */
+    @Deprecated
+    public static boolean isProduct(@NonNull AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).isProduct();
+    }
+
+    /**
+     * @deprecated Use {@link PackageState#isOdm}
+     */
+    @Deprecated
+    public static boolean isOdm(@NonNull AndroidPackage pkg) {
+        return ((AndroidPackageHidden) pkg).isOdm();
+    }
 }
diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java
index 165c52d..74b178e 100644
--- a/services/core/java/com/android/server/pm/permission/Permission.java
+++ b/services/core/java/com/android/server/pm/permission/Permission.java
@@ -30,6 +30,7 @@
 
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.component.ParsedPermission;
 
 import libcore.util.EmptyArray;
@@ -403,31 +404,31 @@
         if (!permission.mReconciled) {
             return false;
         }
-        final AndroidPackage currentPackage = packageManagerInternal.getPackage(
+        var currentPackageState = packageManagerInternal.getPackageStateInternal(
                 permission.mPermissionInfo.packageName);
-        if (currentPackage == null) {
+        if (currentPackageState == null) {
             return false;
         }
-        return currentPackage.isSystem();
+        return currentPackageState.isSystem();
     }
 
     @NonNull
     public static Permission createOrUpdate(@Nullable Permission permission,
-            @NonNull PermissionInfo permissionInfo, @NonNull AndroidPackage pkg,
+            @NonNull PermissionInfo permissionInfo, @NonNull PackageState packageState,
             @NonNull Collection<Permission> permissionTrees, boolean isOverridingSystemPermission) {
         // Allow system apps to redefine non-system permissions
         boolean ownerChanged = false;
         if (permission != null && !Objects.equals(permission.mPermissionInfo.packageName,
                 permissionInfo.packageName)) {
-            if (pkg.isSystem()) {
+            if (packageState.isSystem()) {
                 if (permission.mType == Permission.TYPE_CONFIG && !permission.mReconciled) {
                     // It's a built-in permission and no owner, take ownership now
                     permissionInfo.flags |= PermissionInfo.FLAG_INSTALLED;
                     permission.mPermissionInfo = permissionInfo;
                     permission.mReconciled = true;
-                    permission.mUid = pkg.getUid();
+                    permission.mUid = packageState.getAppId();
                 } else if (!isOverridingSystemPermission) {
-                    Slog.w(TAG, "New decl " + pkg + " of permission  "
+                    Slog.w(TAG, "New decl " + packageState + " of permission  "
                             + permissionInfo.name + " is system; overriding "
                             + permission.mPermissionInfo.packageName);
                     ownerChanged = true;
@@ -453,7 +454,7 @@
                     permissionInfo.flags |= PermissionInfo.FLAG_INSTALLED;
                     permission.mPermissionInfo = permissionInfo;
                     permission.mReconciled = true;
-                    permission.mUid = pkg.getUid();
+                    permission.mUid = packageState.getAppId();
                     if (PackageManagerService.DEBUG_PACKAGE_SCANNING) {
                         if (r == null) {
                             r = new StringBuilder(256);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index cefe9cd..a949f75 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -75,6 +75,7 @@
 import com.android.server.pm.UserManagerService;
 import com.android.server.pm.permission.PermissionManagerServiceInternal.HotwordDetectionServiceProvider;
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -681,9 +682,9 @@
         }
 
         @Override
-        public void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp,
+        public void onPackageAdded(@NonNull PackageState packageState, boolean isInstantApp,
                 @Nullable AndroidPackage oldPkg) {
-            mPermissionManagerServiceImpl.onPackageAdded(pkg, isInstantApp, oldPkg);
+            mPermissionManagerServiceImpl.onPackageAdded(packageState, isInstantApp, oldPkg);
         }
 
         @Override
@@ -714,9 +715,9 @@
 
         @Override
         public void onPackageUninstalled(@NonNull String packageName, int appId,
-                @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs,
-                @UserIdInt int userId) {
-            mPermissionManagerServiceImpl.onPackageUninstalled(packageName, appId,
+                @NonNull PackageState packageState, @Nullable AndroidPackage pkg,
+                @NonNull List<AndroidPackage> sharedUserPkgs, @UserIdInt int userId) {
+            mPermissionManagerServiceImpl.onPackageUninstalled(packageName, appId, packageState,
                     pkg, sharedUserPkgs, userId);
         }
 
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index d2d3c3c..f6362f2 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -133,6 +133,7 @@
 import com.android.server.pm.parsing.PackageInfoUtils;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.component.ComponentMutateUtils;
 import com.android.server.pm.pkg.component.ParsedPermission;
@@ -2337,7 +2338,8 @@
         }
     }
 
-    private List<String> addAllPermissionsInternal(@NonNull AndroidPackage pkg) {
+    private List<String> addAllPermissionsInternal(@NonNull PackageState packageState,
+                    @NonNull AndroidPackage pkg) {
         final int N = ArrayUtils.size(pkg.getPermissions());
         ArrayList<String> definitionChangedPermissions = new ArrayList<>();
         for (int i=0; i<N; i++) {
@@ -2375,7 +2377,7 @@
                     oldPermission, permissionInfo, mPackageManagerInt);
             synchronized (mLock) {
                 final Permission permission = Permission.createOrUpdate(oldPermission,
-                        permissionInfo, pkg, mRegistry.getPermissionTrees(),
+                        permissionInfo, packageState, mRegistry.getPermissionTrees(),
                         isOverridingSystemPermission);
                 if (p.isTree()) {
                     mRegistry.addPermissionTree(permission);
@@ -2976,7 +2978,7 @@
 
                 if ((installPermissionsChangedForUser || replace)
                         && !userState.areInstallPermissionsFixed(ps.getPackageName())
-                        && !ps.isSystem() || ps.getTransientState().isUpdatedSystemApp()) {
+                        && !ps.isSystem() || ps.isUpdatedSystemApp()) {
                     // This is the first that we have heard about this package, so the
                     // permissions we have now selected are fixed until explicitly
                     // changed.
@@ -3304,7 +3306,7 @@
         if (Objects.equals(packageName, PLATFORM_PACKAGE_NAME)) {
             return true;
         }
-        if (!pkg.isPrivileged()) {
+        if (!packageSetting.isPrivileged()) {
             return true;
         }
         if (!mPrivilegedPermissionAllowlistSourcePackageNames
@@ -3314,13 +3316,13 @@
         final String permissionName = permission.getName();
         final String containingApexPackageName =
                 mApexManager.getActiveApexPackageNameContainingPackage(packageName);
-        final Boolean allowlistState = getPrivilegedPermissionAllowlistState(pkg, permissionName,
-                containingApexPackageName);
+        final Boolean allowlistState = getPrivilegedPermissionAllowlistState(packageSetting,
+                permissionName, containingApexPackageName);
         if (allowlistState != null) {
             return allowlistState;
         }
         // Updated system apps do not need to be allowlisted
-        if (packageSetting.getTransientState().isUpdatedSystemApp()) {
+        if (packageSetting.isUpdatedSystemApp()) {
             // Let shouldGrantPermissionByProtectionFlags() decide whether the privileged permission
             // can be granted, because an updated system app may be in a shared UID, and in case a
             // new privileged permission is requested by the updated system app but not the factory
@@ -3354,18 +3356,18 @@
     }
 
     @Nullable
-    private Boolean getPrivilegedPermissionAllowlistState(@NonNull AndroidPackage pkg,
+    private Boolean getPrivilegedPermissionAllowlistState(@NonNull PackageState packageState,
             @NonNull String permissionName, String containingApexPackageName) {
         final PermissionAllowlist permissionAllowlist =
                 SystemConfig.getInstance().getPermissionAllowlist();
-        final String packageName = pkg.getPackageName();
-        if (pkg.isVendor()) {
+        final String packageName = packageState.getPackageName();
+        if (packageState.isVendor()) {
             return permissionAllowlist.getVendorPrivilegedAppAllowlistState(packageName,
                     permissionName);
-        } else if (pkg.isProduct()) {
+        } else if (packageState.isProduct()) {
             return permissionAllowlist.getProductPrivilegedAppAllowlistState(packageName,
                     permissionName);
-        } else if (pkg.isSystemExt()) {
+        } else if (packageState.isSystemExt()) {
             return permissionAllowlist.getSystemExtPrivilegedAppAllowlistState(packageName,
                     permissionName);
         } else if (containingApexPackageName != null) {
@@ -3374,7 +3376,7 @@
             if (nonApexAllowlistState != null) {
                 // TODO(andreionea): Remove check as soon as all apk-in-apex
                 // permission allowlists are migrated.
-                Slog.w(TAG, "Package " + pkg.getPackageName() + " is an APK in APEX,"
+                Slog.w(TAG, "Package " + packageName + " is an APK in APEX,"
                         + " but has permission allowlist on the system image. Please bundle the"
                         + " allowlist in the " + containingApexPackageName + " APEX instead.");
             }
@@ -3425,17 +3427,17 @@
         boolean allowed = false;
         final boolean isPrivilegedPermission = bp.isPrivileged();
         final boolean isOemPermission = bp.isOem();
-        if (!allowed && (isPrivilegedPermission || isOemPermission) && pkg.isSystem()) {
+        if (!allowed && (isPrivilegedPermission || isOemPermission) && pkgSetting.isSystem()) {
             final String permissionName = bp.getName();
             // For updated system applications, a privileged/oem permission
             // is granted only if it had been defined by the original application.
-            if (pkgSetting.getTransientState().isUpdatedSystemApp()) {
+            if (pkgSetting.isUpdatedSystemApp()) {
                 final PackageStateInternal disabledPs = mPackageManagerInt
                         .getDisabledSystemPackage(pkg.getPackageName());
                 final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.getPkg();
                 if (disabledPkg != null
-                        && ((isPrivilegedPermission && disabledPkg.isPrivileged())
-                        || (isOemPermission && canGrantOemPermission(disabledPkg,
+                        && ((isPrivilegedPermission && disabledPs.isPrivileged())
+                        || (isOemPermission && canGrantOemPermission(disabledPs,
                                 permissionName)))) {
                     if (disabledPkg.getRequestedPermissions().contains(permissionName)) {
                         allowed = true;
@@ -3447,13 +3449,14 @@
                     }
                 }
             } else {
-                allowed = (isPrivilegedPermission && pkg.isPrivileged())
-                        || (isOemPermission && canGrantOemPermission(pkg, permissionName));
+                allowed = (isPrivilegedPermission && pkgSetting.isPrivileged())
+                        || (isOemPermission && canGrantOemPermission(pkgSetting, permissionName));
             }
             // In any case, don't grant a privileged permission to privileged vendor apps, if
             // the permission's protectionLevel does not have the extra 'vendorPrivileged'
             // flag.
-            if (allowed && isPrivilegedPermission && !bp.isVendorPrivileged() && pkg.isVendor()) {
+            if (allowed && isPrivilegedPermission && !bp.isVendorPrivileged()
+                    && pkgSetting.isVendor()) {
                 Slog.w(TAG, "Permission " + permissionName
                         + " cannot be granted to privileged vendor apk " + pkg.getPackageName()
                         + " because it isn't a 'vendorPrivileged' permission.");
@@ -3488,8 +3491,7 @@
             // this app is a verifier, then it gets the permission.
             allowed = true;
         }
-        if (!allowed && bp.isPreInstalled()
-                && pkg.isSystem()) {
+        if (!allowed && bp.isPreInstalled() && pkgSetting.isSystem()) {
             // Any pre-installed system app is allowed to get this permission.
             allowed = true;
         }
@@ -3586,16 +3588,18 @@
         return mPackageManagerInt.getPackageStateInternal(sourcePackageName);
     }
 
-    private static boolean canGrantOemPermission(AndroidPackage pkg, String permission) {
-        if (!pkg.isOem()) {
+    private static boolean canGrantOemPermission(@NonNull PackageState packageState,
+            String permission) {
+        if (!packageState.isOem()) {
             return false;
         }
+        var packageName = packageState.getPackageName();
         // all oem permissions must explicitly be granted or denied
         final Boolean granted = SystemConfig.getInstance().getPermissionAllowlist()
-                .getOemAppAllowlistState(pkg.getPackageName(), permission);
+                .getOemAppAllowlistState(packageState.getPackageName(), permission);
         if (granted == null) {
             throw new IllegalStateException("OEM permission " + permission
-                    + " requested by package " + pkg.getPackageName()
+                    + " requested by package " + packageName
                     + " must be explicitly declared granted or not");
         }
         return Boolean.TRUE == granted;
@@ -4648,8 +4652,8 @@
         }
     }
 
-    private void onPackageAddedInternal(@NonNull AndroidPackage pkg, boolean isInstantApp,
-            @Nullable AndroidPackage oldPkg) {
+    private void onPackageAddedInternal(@NonNull PackageState packageState,
+            @NonNull AndroidPackage pkg, boolean isInstantApp, @Nullable AndroidPackage oldPkg) {
         if (!pkg.getAdoptPermissions().isEmpty()) {
             // This package wants to adopt ownership of permissions from
             // another package.
@@ -4682,7 +4686,7 @@
             Slog.w(TAG, "Permissions from package " + pkg.getPackageName()
                     + " ignored: instant apps cannot define new permissions.");
         } else {
-            permissionsWithChangedDefinition = addAllPermissionsInternal(pkg);
+            permissionsWithChangedDefinition = addAllPermissionsInternal(packageState, pkg);
         }
 
         boolean hasOldPkg = oldPkg != null;
@@ -5016,14 +5020,13 @@
     }
 
     private void onPackageUninstalledInternal(@NonNull String packageName, int appId,
-            @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs,
-            @UserIdInt int[] userIds) {
+            @NonNull PackageState packageState, @Nullable AndroidPackage pkg,
+            @NonNull List<AndroidPackage> sharedUserPkgs, @UserIdInt int[] userIds) {
         // TODO: Handle the case when a system app upgrade is uninstalled and need to rejoin
         //  a shared UID permission state.
 
-        // TODO: Move these checks to check PackageState to be more reliable.
         // System packages should always have an available APK.
-        if (pkg != null && pkg.isSystem()
+        if (packageState.isSystem() && pkg != null
                 // We may be fully removing invalid system packages during boot, and in that case we
                 // do want to remove their permission state. So make sure that the package is only
                 // being marked as uninstalled instead of fully removed.
@@ -5046,8 +5049,8 @@
                 // or packages running under the shared user of the removed
                 // package if revoking the permissions requested only by the removed
                 // package is successful and this causes a change in gids.
-                revokeSharedUserPermissionsForLeavingPackageInternal(pkg, appId,
-                        sharedUserPkgs, userId);
+                revokeSharedUserPermissionsForLeavingPackageInternal(pkg, appId, sharedUserPkgs,
+                        userId);
             }
         }
     }
@@ -5229,10 +5232,12 @@
     }
 
     @Override
-    public void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp,
-            @Nullable AndroidPackage oldPkg) {
+    public void onPackageAdded(@NonNull PackageState packageState, boolean isInstantApp,
+                    @Nullable AndroidPackage oldPkg) {
+        Objects.requireNonNull(packageState);
+        var pkg = packageState.getAndroidPackage();
         Objects.requireNonNull(pkg);
-        onPackageAddedInternal(pkg, isInstantApp, oldPkg);
+        onPackageAddedInternal(packageState, pkg, isInstantApp, oldPkg);
     }
 
     @Override
@@ -5256,15 +5261,17 @@
 
     @Override
     public void onPackageUninstalled(@NonNull String packageName, int appId,
-            @Nullable AndroidPackage pkg, @NonNull List<AndroidPackage> sharedUserPkgs,
-            @UserIdInt int userId) {
+            @NonNull PackageState packageState, @Nullable AndroidPackage pkg,
+            @NonNull List<AndroidPackage> sharedUserPkgs, @UserIdInt int userId) {
+        Objects.requireNonNull(packageState, "packageState");
         Objects.requireNonNull(packageName, "packageName");
         Objects.requireNonNull(sharedUserPkgs, "sharedUserPkgs");
         Preconditions.checkArgument(userId >= UserHandle.USER_SYSTEM
                 || userId == UserHandle.USER_ALL, "userId");
         final int[] userIds = userId == UserHandle.USER_ALL ? getAllUserIds()
                 : new int[] { userId };
-        onPackageUninstalledInternal(packageName, appId, pkg, sharedUserPkgs, userIds);
+        onPackageUninstalledInternal(packageName, appId, packageState, pkg, sharedUserPkgs,
+                userIds);
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index d9caec7..08938a5 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -29,6 +29,7 @@
 import android.permission.PermissionManagerInternal;
 
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -567,11 +568,11 @@
     /**
      * Callback when a package has been added.
      *
-     * @param pkg the added package
+     * @param packageState the added package
      * @param isInstantApp whether the added package is an instant app
      * @param oldPkg the old package, or {@code null} if none
      */
-    void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp,
+    void onPackageAdded(@NonNull PackageState packageState, boolean isInstantApp,
             @Nullable AndroidPackage oldPkg);
 
     /**
@@ -598,16 +599,16 @@
      * Callback when a package has been uninstalled.
      * <p>
      * The package may have been fully removed from the system, or only marked as uninstalled for
-     * this user but still instlaled for other users.
-     *
-     * TODO: Pass PackageState instead.
+     * this user but still installed for other users.
      *
      * @param packageName the name of the uninstalled package
      * @param appId the app ID of the uninstalled package
-     * @param pkg the uninstalled package, or {@code null} if unavailable
+     * @param packageState the uninstalled package
+     * @param pkg the uninstalled package
      * @param sharedUserPkgs the packages that are in the same shared user
      * @param userId the user ID the package is uninstalled for
      */
-    void onPackageUninstalled(@NonNull String packageName, int appId, @Nullable AndroidPackage pkg,
+    void onPackageUninstalled(@NonNull String packageName, int appId,
+            @NonNull PackageState packageState, @NonNull AndroidPackage pkg,
             @NonNull List<AndroidPackage> sharedUserPkgs, @UserIdInt int userId);
 }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 97ac749..fdcf765 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -24,6 +24,7 @@
 import android.permission.PermissionManagerInternal;
 
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -249,13 +250,13 @@
     /**
      * Callback when a package has been added.
      *
-     * @param pkg the added package
+     * @param packageState the added package
      * @param isInstantApp whether the added package is an instant app
      * @param oldPkg the old package, or {@code null} if none
      */
     //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
-    void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp,
-            @Nullable AndroidPackage oldPkg);
+    void onPackageAdded(@NonNull PackageState packageState,
+            boolean isInstantApp, @Nullable AndroidPackage oldPkg);
 
     /**
      * Callback when a package has been installed for a user.
@@ -285,16 +286,15 @@
      * The package may have been fully removed from the system, or only marked as uninstalled for
      * this user but still instlaled for other users.
      *
-     * TODO: Pass PackageState instead.
-     *
      * @param packageName the name of the uninstalled package
      * @param appId the app ID of the uninstalled package
-     * @param pkg the uninstalled package, or {@code null} if unavailable
+     * @param packageState the uninstalled package, or {@code null} if unavailable
      * @param sharedUserPkgs the packages that are in the same shared user
      * @param userId the user ID the package is uninstalled for
      */
     //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
-    void onPackageUninstalled(@NonNull String packageName, int appId, @Nullable AndroidPackage pkg,
+    void onPackageUninstalled(@NonNull String packageName, int appId,
+            @Nullable PackageState packageState, @Nullable AndroidPackage pkg,
             @NonNull List<AndroidPackage> sharedUserPkgs, @UserIdInt int userId);
 
     /**
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
index 84907a5..075173d 100644
--- a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -249,8 +249,12 @@
     /**
      * @see ApplicationInfo#sourceDir
      * @see ApplicationInfo#getBaseCodePath
+     *
+     * @deprecated Use {@link #getSplits()}[0].{@link AndroidPackageSplit#getPath() getPath()}
+     *
      * @hide
      */
+    @Deprecated
     @NonNull
     String getBaseApkPath();
 
@@ -1337,18 +1341,6 @@
     boolean isNativeLibraryRootRequiresIsa();
 
     /**
-     * @see ApplicationInfo#PRIVATE_FLAG_ODM
-     * @hide
-     */
-    boolean isOdm();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_OEM
-     * @hide
-     */
-    boolean isOem();
-
-    /**
      * @see R.styleable#AndroidManifestApplication_enableOnBackInvokedCallback
      * @hide
      */
@@ -1386,18 +1378,6 @@
     boolean isPersistent();
 
     /**
-     * @see ApplicationInfo#PRIVATE_FLAG_PRIVILEGED
-     * @hide
-     */
-    boolean isPrivileged();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_PRODUCT
-     * @hide
-     */
-    boolean isProduct();
-
-    /**
      * @see ApplicationInfo#PRIVATE_FLAG_EXT_PROFILEABLE
      * @see R.styleable#AndroidManifestProfileable
      * @hide
@@ -1525,18 +1505,6 @@
     boolean isSupportsSmallScreens();
 
     /**
-     * @see ApplicationInfo#FLAG_SYSTEM
-     * @hide
-     */
-    boolean isSystem();
-
-    /**
-     * @see ApplicationInfo#PRIVATE_FLAG_SYSTEM_EXT
-     * @hide
-     */
-    boolean isSystemExt();
-
-    /**
      * @see ApplicationInfo#FLAG_TEST_ONLY
      * @see R.styleable#AndroidManifestApplication_testOnly
      * @hide
@@ -1561,12 +1529,6 @@
     boolean isUsesCleartextTraffic();
 
     /**
-     * @see ApplicationInfo#PRIVATE_FLAG_VENDOR
-     * @hide
-     */
-    boolean isVendor();
-
-    /**
      * Set if the any of components are visible to instant applications.
      *
      * @see R.styleable#AndroidManifestActivity_visibleToInstantApps
diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java
index 67b7647..7335a46 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageState.java
@@ -164,12 +164,12 @@
     List<SharedLibrary> getUsesLibraries();
 
     /**
-     * @see AndroidPackage#isPrivileged()
+     * @see ApplicationInfo#PRIVATE_FLAG_PRIVILEGED
      */
     boolean isPrivileged();
 
     /**
-     * @see AndroidPackage#isSystem()
+     * @see ApplicationInfo#FLAG_SYSTEM
      */
     boolean isSystem();
 
@@ -367,19 +367,19 @@
     boolean isInstallPermissionsFixed();
 
     /**
-     * @see AndroidPackage#isOdm()
+     * @see ApplicationInfo#PRIVATE_FLAG_ODM
      * @hide
      */
     boolean isOdm();
 
     /**
-     * @see AndroidPackage#isOem()
+     * @see ApplicationInfo#PRIVATE_FLAG_OEM
      * @hide
      */
     boolean isOem();
 
     /**
-     * @see AndroidPackage#isProduct()
+     * @see ApplicationInfo#PRIVATE_FLAG_PRODUCT
      * @hide
      */
     boolean isProduct();
@@ -391,7 +391,7 @@
     boolean isRequiredForSystemUser();
 
     /**
-     * @see AndroidPackage#isSystemExt()
+     * @see ApplicationInfo#PRIVATE_FLAG_SYSTEM_EXT
      * @hide
      */
     boolean isSystemExt();
@@ -410,7 +410,7 @@
     boolean isApkInUpdatedApex();
 
     /**
-     * @see AndroidPackage#isVendor()
+     * @see ApplicationInfo#PRIVATE_FLAG_VENDOR
      * @hide
      */
     boolean isVendor();
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
index 1940eb5..a812257 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedServiceUtils.java
@@ -100,6 +100,8 @@
                             R.styleable.AndroidManifestService_externalService, sa)
                             | flag(ServiceInfo.FLAG_USE_APP_ZYGOTE,
                             R.styleable.AndroidManifestService_useAppZygote, sa)
+                            | flag(ServiceInfo.FLAG_ALLOW_SHARED_ISOLATED_PROCESS,
+                            R.styleable.AndroidManifestService_allowSharedIsolatedProcess, sa)
                             | flag(ServiceInfo.FLAG_SINGLE_USER,
                             R.styleable.AndroidManifestService_singleUser, sa)));
 
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
index a19beea..842f685 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
@@ -498,9 +498,9 @@
         }
 
         String packageName = activity.getPackageName();
-        AndroidPackage pkg = computer.getPackage(packageName);
+        var packageState = computer.getPackageStateInternal(packageName);
 
-        final boolean privilegedApp = pkg.isPrivileged();
+        final boolean privilegedApp = packageState.isPrivileged();
         String className = activity.getClassName();
         if (!privilegedApp) {
             // non-privileged applications can never define a priority >0
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
index 75d7162..9115775 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java
@@ -290,7 +290,7 @@
                 continue;
             }
 
-            if (safeMode && !pkg.isSystem()) {
+            if (safeMode && !ps.isSystem()) {
                 continue;
             }
             if (appInfoGenerator == null) {
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index d6cac33..db44e14 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -1130,18 +1130,18 @@
                 new ActivityInterceptorCallback() {
                     @Nullable
                     @Override
-                    public ActivityInterceptorCallback.ActivityInterceptResult intercept(
-                            ActivityInterceptorInfo info) {
+                    public ActivityInterceptorCallback.ActivityInterceptResult
+                            onInterceptActivityLaunch(@NonNull ActivityInterceptorInfo info) {
                         return null;
                     }
 
                     @Override
                     public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo,
                             ActivityInterceptorInfo info) {
-                        super.onActivityLaunched(taskInfo, activityInfo, info);
                         if (!shouldShowNotificationDialogOrClearFlags(taskInfo,
-                                activityInfo.packageName, info.callingPackage, info.intent,
-                                info.checkedOptions, activityInfo.name, true)
+                                activityInfo.packageName, info.getCallingPackage(),
+                                info.getIntent(), info.getCheckedOptions(), activityInfo.name,
+                                true)
                                 || isNoDisplayActivity(activityInfo)) {
                             return;
                         }
@@ -1318,12 +1318,12 @@
                     ACTION_REQUEST_PERMISSIONS_FOR_OTHER);
             grantPermission.putExtra(Intent.EXTRA_PACKAGE_NAME, pkgName);
 
-            final boolean remoteAnimation = info != null && info.checkedOptions != null
-                    && info.checkedOptions.getAnimationType() == ANIM_REMOTE_ANIMATION
-                    && info.clearOptionsAnimation != null;
+            final boolean remoteAnimation = info != null && info.getCheckedOptions() != null
+                    && info.getCheckedOptions().getAnimationType() == ANIM_REMOTE_ANIMATION
+                    && info.getClearOptionsAnimationRunnable() != null;
             ActivityOptions options = remoteAnimation ? ActivityOptions.makeRemoteAnimation(
-                        info.checkedOptions.getRemoteAnimationAdapter(),
-                        info.checkedOptions.getRemoteTransition())
+                        info.getCheckedOptions().getRemoteAnimationAdapter(),
+                        info.getCheckedOptions().getRemoteTransition())
                     : new ActivityOptions(new Bundle());
             options.setTaskOverlay(true, false);
             options.setLaunchTaskId(taskId);
@@ -1333,7 +1333,7 @@
                 // animation from the intercepted activity and its siblings to prevent duplication.
                 // This should trigger ActivityRecord#clearOptionsAnimationForSiblings for the
                 // intercepted activity.
-                info.clearOptionsAnimation.run();
+                info.getClearOptionsAnimationRunnable().run();
             }
             try {
                 mContext.startActivityAsUser(grantPermission, options.toBundle(), user);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 81b4865..f88adab 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1056,13 +1056,6 @@
             return;
         }
 
-        // Don't dream if the user isn't user zero.
-        // TODO(b/261907079): Move this check to DreamManagerService#canStartDreamingInternal().
-        if (ActivityManager.getCurrentUser() != UserHandle.USER_SYSTEM) {
-            noDreamAction.run();
-            return;
-        }
-
         final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal();
         if (dreamManagerInternal == null || !dreamManagerInternal.canStartDreaming(isScreenOn)) {
             noDreamAction.run();
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index f5ce461..88ec691 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -136,10 +136,6 @@
     @IntDef({NAV_BAR_LEFT, NAV_BAR_RIGHT, NAV_BAR_BOTTOM})
     @interface NavigationBarPosition {}
 
-    @Retention(SOURCE)
-    @IntDef({ALT_BAR_UNKNOWN, ALT_BAR_LEFT, ALT_BAR_RIGHT, ALT_BAR_BOTTOM, ALT_BAR_TOP})
-    @interface AltBarPosition {}
-
     /**
      * Pass this event to the user / app.  To be returned from
      * {@link #interceptKeyBeforeQueueing}.
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 09a7b29..326d709 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -638,7 +638,7 @@
         } catch (RemoteException ex) {
             // Ignore
         }
-        FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_WAKE_REPORTED, reason);
+        FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_WAKE_REPORTED, reason, reasonUid);
     }
 
     /**
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 6b2c6e3..514caf2 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -746,6 +746,8 @@
                         }
                         ret.add(new Temperature(t.value, t.type, t.name, t.throttlingStatus));
                     }
+                } catch (IllegalArgumentException | IllegalStateException e) {
+                    Slog.e(TAG, "Couldn't getCurrentCoolingDevices due to invalid status", e);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting", e);
                     connectToHal();
@@ -776,6 +778,8 @@
                         }
                         ret.add(new CoolingDevice(t.value, t.type, t.name));
                     }
+                } catch (IllegalArgumentException | IllegalStateException e) {
+                    Slog.e(TAG, "Couldn't getCurrentCoolingDevices due to invalid status", e);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Couldn't getCurrentCoolingDevices, reconnecting", e);
                     connectToHal();
@@ -799,6 +803,8 @@
 
                     return Arrays.stream(halRet).filter(t -> t.type == type).collect(
                             Collectors.toList());
+                } catch (IllegalArgumentException | IllegalStateException e) {
+                    Slog.e(TAG, "Couldn't getTemperatureThresholds due to invalid status", e);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Couldn't getTemperatureThresholds, reconnecting...", e);
                     connectToHal();
@@ -824,15 +830,30 @@
                     mInstance = IThermal.Stub.asInterface(binder);
                     try {
                         binder.linkToDeath(this, 0);
-                        mInstance.registerThermalChangedCallback(mThermalChangedCallback);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "Unable to connect IThermal AIDL instance", e);
                         mInstance = null;
                     }
+                    if (mInstance != null) {
+                        registerThermalChangedCallback();
+                    }
                 }
             }
         }
 
+        @VisibleForTesting
+        void registerThermalChangedCallback() {
+            try {
+                mInstance.registerThermalChangedCallback(mThermalChangedCallback);
+            } catch (IllegalArgumentException | IllegalStateException e) {
+                Slog.e(TAG, "Couldn't registerThermalChangedCallback due to invalid status",
+                        e);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Unable to connect IThermal AIDL instance", e);
+                mInstance = null;
+            }
+        }
+
         @Override
         protected void dump(PrintWriter pw, String prefix) {
             synchronized (mHalLock) {
diff --git a/services/core/java/com/android/server/power/stats/AmbientDisplayPowerCalculator.java b/services/core/java/com/android/server/power/stats/AmbientDisplayPowerCalculator.java
index ae4bad5..d14197f 100644
--- a/services/core/java/com/android/server/power/stats/AmbientDisplayPowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/AmbientDisplayPowerCalculator.java
@@ -52,12 +52,12 @@
     @Override
     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
             long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
-        final long measuredEnergyUC = batteryStats.getScreenDozeMeasuredBatteryConsumptionUC();
-        final int powerModel = getPowerModel(measuredEnergyUC, query);
+        final long energyConsumerUC = batteryStats.getScreenDozeEnergyConsumptionUC();
+        final int powerModel = getPowerModel(energyConsumerUC, query);
         final long durationMs = calculateDuration(batteryStats, rawRealtimeUs,
                 BatteryStats.STATS_SINCE_CHARGED);
         final double powerMah = calculateTotalPower(powerModel, batteryStats, rawRealtimeUs,
-                measuredEnergyUC);
+                energyConsumerUC);
         builder.getAggregateBatteryConsumerBuilder(
                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
                 .setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY, durationMs)
@@ -72,7 +72,7 @@
     private double calculateTotalPower(@BatteryConsumer.PowerModel int powerModel,
             BatteryStats batteryStats, long rawRealtimeUs, long consumptionUC) {
         switch (powerModel) {
-            case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY:
+            case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION:
                 return uCtoMah(consumptionUC);
             case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
             default:
diff --git a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
index 49ac559..60dbbdd 100644
--- a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
@@ -43,7 +43,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.power.MeasuredEnergyStats;
+import com.android.internal.power.EnergyConsumerStats;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
 
@@ -156,11 +156,13 @@
      * unless it is of {@link EnergyConsumer#type}=={@link EnergyConsumerType#OTHER}
      */
     @GuardedBy("mWorkerLock")
-    private @Nullable SparseArray<int[]> mEnergyConsumerTypeToIdMap = null;
+    @Nullable
+    private SparseArray<int[]> mEnergyConsumerTypeToIdMap = null;
 
-    /** Snapshot of measured energies, or null if no measured energies are supported. */
+    /** Snapshot of energy consumers, or null if no EnergyConsumers are supported. */
     @GuardedBy("mWorkerLock")
-    private @Nullable MeasuredEnergySnapshot mMeasuredEnergySnapshot = null;
+    @Nullable
+    private EnergyConsumerSnapshot mEnergyConsumerSnapshot = null;
 
     /**
      * Timestamp at which all external stats were last collected in
@@ -218,14 +220,14 @@
                 final SparseArray<EnergyConsumer> idToConsumer
                         = populateEnergyConsumerSubsystemMapsLocked();
                 if (idToConsumer != null) {
-                    mMeasuredEnergySnapshot = new MeasuredEnergySnapshot(idToConsumer);
+                    mEnergyConsumerSnapshot = new EnergyConsumerSnapshot(idToConsumer);
                     try {
                         final EnergyConsumerResult[] initialEcrs = getEnergyConsumptionData().get(
                                 EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
                         // According to spec, initialEcrs will include 0s for consumers that haven't
                         // used any energy yet, as long as they are supported; however,
                         // attributed uid energies will be absent if their energy is 0.
-                        mMeasuredEnergySnapshot.updateAndGetDelta(initialEcrs, voltageMv);
+                        mEnergyConsumerSnapshot.updateAndGetDelta(initialEcrs, voltageMv);
                     } catch (TimeoutException | InterruptedException e) {
                         Slog.w(TAG, "timeout or interrupt reading initial getEnergyConsumedAsync: "
                                 + e);
@@ -235,12 +237,12 @@
                                 + e.getCause());
                         // Continue running, later attempts to query may be successful.
                     }
-                    customBucketNames = mMeasuredEnergySnapshot.getOtherOrdinalNames();
+                    customBucketNames = mEnergyConsumerSnapshot.getOtherOrdinalNames();
                     supportedStdBuckets = getSupportedEnergyBuckets(idToConsumer);
                 }
             }
             synchronized (mStats) {
-                mStats.initMeasuredEnergyStatsLocked(supportedStdBuckets, customBucketNames);
+                mStats.initEnergyConsumerStatsLocked(supportedStdBuckets, customBucketNames);
             }
         }
     }
@@ -520,7 +522,8 @@
         CompletableFuture<ModemActivityInfo> modemFuture = CompletableFuture.completedFuture(null);
         boolean railUpdated = false;
 
-        CompletableFuture<EnergyConsumerResult[]> futureECRs = getMeasuredEnergyLocked(updateFlags);
+        CompletableFuture<EnergyConsumerResult[]> futureECRs =
+                getEnergyConsumersLocked(updateFlags);
 
         if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
             // We were asked to fetch WiFi data.
@@ -626,9 +629,9 @@
             Slog.w(TAG, "exception reading modem stats: " + e.getCause());
         }
 
-        final MeasuredEnergySnapshot.MeasuredEnergyDeltaData measuredEnergyDeltas;
-        if (mMeasuredEnergySnapshot == null || futureECRs == null) {
-            measuredEnergyDeltas = null;
+        final EnergyConsumerSnapshot.EnergyConsumerDeltaData energyConsumerDeltas;
+        if (mEnergyConsumerSnapshot == null || futureECRs == null) {
+            energyConsumerDeltas = null;
         } else {
             final int voltageMv;
             synchronized (mStats) {
@@ -639,7 +642,7 @@
             try {
                 ecrs = futureECRs.get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
             } catch (TimeoutException | InterruptedException e) {
-                // TODO (b/180519623): Invalidate the MeasuredEnergy derived data until next reset.
+                // TODO (b/180519623): Invalidate the EnergyConsumer derived data until next reset.
                 Slog.w(TAG, "timeout or interrupt reading getEnergyConsumedAsync: " + e);
                 ecrs = null;
             } catch (ExecutionException e) {
@@ -647,7 +650,7 @@
                 ecrs = null;
             }
 
-            measuredEnergyDeltas = mMeasuredEnergySnapshot.updateAndGetDelta(ecrs, voltageMv);
+            energyConsumerDeltas = mEnergyConsumerSnapshot.updateAndGetDelta(ecrs, voltageMv);
         }
 
         final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -663,9 +666,10 @@
                     BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS,
                     reason, 0);
 
-            if (measuredEnergyDeltas != null && !measuredEnergyDeltas.isEmpty()) {
-                mStats.recordMeasuredEnergyDetailsLocked(elapsedRealtime, uptime,
-                        mMeasuredEnergySnapshot.getMeasuredEnergyDetails(measuredEnergyDeltas));
+            if (energyConsumerDeltas != null && !energyConsumerDeltas.isEmpty()
+                    && mStats.isUsageHistoryEnabled()) {
+                mStats.recordEnergyConsumerDetailsLocked(elapsedRealtime, uptime,
+                        mEnergyConsumerSnapshot.getEnergyConsumerDetails(energyConsumerDeltas));
             }
 
             if ((updateFlags & UPDATE_CPU) != 0) {
@@ -675,10 +679,10 @@
                 }
 
                 final long[] cpuClusterChargeUC;
-                if (measuredEnergyDeltas == null) {
+                if (energyConsumerDeltas == null) {
                     cpuClusterChargeUC = null;
                 } else {
-                    cpuClusterChargeUC = measuredEnergyDeltas.cpuClusterChargeUC;
+                    cpuClusterChargeUC = energyConsumerDeltas.cpuClusterChargeUC;
                 }
                 mStats.updateCpuTimeLocked(onBattery, onBatteryScreenOff, cpuClusterChargeUC);
             }
@@ -692,37 +696,37 @@
                 mStats.updateRpmStatsLocked(elapsedRealtimeUs);
             }
 
-            // Inform mStats about each applicable measured energy (unless addressed elsewhere).
-            if (measuredEnergyDeltas != null) {
-                final long[] displayChargeUC = measuredEnergyDeltas.displayChargeUC;
+            // Inform mStats about each applicable energy consumers (unless addressed elsewhere).
+            if (energyConsumerDeltas != null) {
+                final long[] displayChargeUC = energyConsumerDeltas.displayChargeUC;
                 if (displayChargeUC != null && displayChargeUC.length > 0) {
                     // If updating, pass in what BatteryExternalStatsWorker thinks
                     // displayScreenStates is.
-                    mStats.updateDisplayMeasuredEnergyStatsLocked(displayChargeUC,
+                    mStats.updateDisplayEnergyConsumerStatsLocked(displayChargeUC,
                             displayScreenStates, elapsedRealtime);
                 }
 
-                final long gnssChargeUC = measuredEnergyDeltas.gnssChargeUC;
-                if (gnssChargeUC != MeasuredEnergySnapshot.UNAVAILABLE) {
-                    mStats.updateGnssMeasuredEnergyStatsLocked(gnssChargeUC, elapsedRealtime);
+                final long gnssChargeUC = energyConsumerDeltas.gnssChargeUC;
+                if (gnssChargeUC != EnergyConsumerSnapshot.UNAVAILABLE) {
+                    mStats.updateGnssEnergyConsumerStatsLocked(gnssChargeUC, elapsedRealtime);
                 }
             }
             // Inform mStats about each applicable custom energy bucket.
-            if (measuredEnergyDeltas != null
-                    && measuredEnergyDeltas.otherTotalChargeUC != null) {
+            if (energyConsumerDeltas != null
+                    && energyConsumerDeltas.otherTotalChargeUC != null) {
                 // Iterate over the custom (EnergyConsumerType.OTHER) ordinals.
-                for (int ord = 0; ord < measuredEnergyDeltas.otherTotalChargeUC.length; ord++) {
-                    long totalEnergy = measuredEnergyDeltas.otherTotalChargeUC[ord];
-                    SparseLongArray uidEnergies = measuredEnergyDeltas.otherUidChargesUC[ord];
-                    mStats.updateCustomMeasuredEnergyStatsLocked(ord, totalEnergy, uidEnergies);
+                for (int ord = 0; ord < energyConsumerDeltas.otherTotalChargeUC.length; ord++) {
+                    long totalEnergy = energyConsumerDeltas.otherTotalChargeUC[ord];
+                    SparseLongArray uidEnergies = energyConsumerDeltas.otherUidChargesUC[ord];
+                    mStats.updateCustomEnergyConsumerStatsLocked(ord, totalEnergy, uidEnergies);
                 }
             }
 
             if (bluetoothInfo != null) {
                 if (bluetoothInfo.isValid()) {
-                    final long btChargeUC = measuredEnergyDeltas != null
-                            ? measuredEnergyDeltas.bluetoothChargeUC
-                            : MeasuredEnergySnapshot.UNAVAILABLE;
+                    final long btChargeUC = energyConsumerDeltas != null
+                            ? energyConsumerDeltas.bluetoothChargeUC
+                            : EnergyConsumerSnapshot.UNAVAILABLE;
                     mStats.updateBluetoothStateLocked(bluetoothInfo,
                             btChargeUC, elapsedRealtime, uptime);
                 } else {
@@ -736,8 +740,9 @@
 
         if (wifiInfo != null) {
             if (wifiInfo.isValid()) {
-                final long wifiChargeUC = measuredEnergyDeltas != null ?
-                        measuredEnergyDeltas.wifiChargeUC : MeasuredEnergySnapshot.UNAVAILABLE;
+                final long wifiChargeUC =
+                        energyConsumerDeltas != null ? energyConsumerDeltas.wifiChargeUC
+                                : EnergyConsumerSnapshot.UNAVAILABLE;
                 final NetworkStatsManager networkStatsManager = mInjector.getSystemService(
                         NetworkStatsManager.class);
                 mStats.updateWifiState(extractDeltaLocked(wifiInfo),
@@ -748,8 +753,8 @@
         }
 
         if (modemInfo != null) {
-            final long mobileRadioChargeUC = measuredEnergyDeltas != null
-                    ? measuredEnergyDeltas.mobileRadioChargeUC : MeasuredEnergySnapshot.UNAVAILABLE;
+            final long mobileRadioChargeUC = energyConsumerDeltas != null
+                    ? energyConsumerDeltas.mobileRadioChargeUC : EnergyConsumerSnapshot.UNAVAILABLE;
             final NetworkStatsManager networkStatsManager = mInjector.getSystemService(
                     NetworkStatsManager.class);
             mStats.noteModemControllerActivity(modemInfo, mobileRadioChargeUC, elapsedRealtime,
@@ -864,7 +869,7 @@
 
     /**
      * Map the {@link EnergyConsumerType}s in the given energyArray to
-     * their corresponding {@link MeasuredEnergyStats.StandardPowerBucket}s.
+     * their corresponding {@link EnergyConsumerStats.StandardPowerBucket}s.
      * Does not include custom energy buckets (which are always, by definition, supported).
      *
      * @return array with true for index i if standard energy bucket i is supported.
@@ -874,30 +879,30 @@
         if (idToConsumer == null) {
             return null;
         }
-        final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
+        final boolean[] buckets = new boolean[EnergyConsumerStats.NUMBER_STANDARD_POWER_BUCKETS];
         final int size = idToConsumer.size();
         for (int idx = 0; idx < size; idx++) {
             final EnergyConsumer consumer = idToConsumer.valueAt(idx);
             switch (consumer.type) {
                 case EnergyConsumerType.BLUETOOTH:
-                    buckets[MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH] = true;
+                    buckets[EnergyConsumerStats.POWER_BUCKET_BLUETOOTH] = true;
                     break;
                 case EnergyConsumerType.CPU_CLUSTER:
-                    buckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
+                    buckets[EnergyConsumerStats.POWER_BUCKET_CPU] = true;
                     break;
                 case EnergyConsumerType.GNSS:
-                    buckets[MeasuredEnergyStats.POWER_BUCKET_GNSS] = true;
+                    buckets[EnergyConsumerStats.POWER_BUCKET_GNSS] = true;
                     break;
                 case EnergyConsumerType.MOBILE_RADIO:
-                    buckets[MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO] = true;
+                    buckets[EnergyConsumerStats.POWER_BUCKET_MOBILE_RADIO] = true;
                     break;
                 case EnergyConsumerType.DISPLAY:
-                    buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON] = true;
-                    buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE] = true;
-                    buckets[MeasuredEnergyStats.POWER_BUCKET_SCREEN_OTHER] = true;
+                    buckets[EnergyConsumerStats.POWER_BUCKET_SCREEN_ON] = true;
+                    buckets[EnergyConsumerStats.POWER_BUCKET_SCREEN_DOZE] = true;
+                    buckets[EnergyConsumerStats.POWER_BUCKET_SCREEN_OTHER] = true;
                     break;
                 case EnergyConsumerType.WIFI:
-                    buckets[MeasuredEnergyStats.POWER_BUCKET_WIFI] = true;
+                    buckets[EnergyConsumerStats.POWER_BUCKET_WIFI] = true;
                     break;
             }
         }
@@ -925,9 +930,9 @@
     @VisibleForTesting
     @GuardedBy("mWorkerLock")
     @Nullable
-    public CompletableFuture<EnergyConsumerResult[]> getMeasuredEnergyLocked(
+    public CompletableFuture<EnergyConsumerResult[]> getEnergyConsumersLocked(
             @ExternalUpdateFlag int flags) {
-        if (mMeasuredEnergySnapshot == null || mPowerStatsInternal == null) return null;
+        if (mEnergyConsumerSnapshot == null || mPowerStatsInternal == null) return null;
 
         if (flags == UPDATE_ALL) {
             // Gotta catch 'em all... including custom (non-specific) subsystems
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 855fcaf..c559436 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -123,8 +123,8 @@
 import com.android.internal.os.PowerProfile;
 import com.android.internal.os.RailStats;
 import com.android.internal.os.RpmStats;
-import com.android.internal.power.MeasuredEnergyStats;
-import com.android.internal.power.MeasuredEnergyStats.StandardPowerBucket;
+import com.android.internal.power.EnergyConsumerStats;
+import com.android.internal.power.EnergyConsumerStats.StandardPowerBucket;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.XmlUtils;
@@ -211,7 +211,7 @@
     public static final int RESET_REASON_CORRUPT_FILE = 1;
     public static final int RESET_REASON_ADB_COMMAND = 2;
     public static final int RESET_REASON_FULL_CHARGE = 3;
-    public static final int RESET_REASON_MEASURED_ENERGY_BUCKETS_CHANGE = 4;
+    public static final int RESET_REASON_ENERGY_CONSUMER_BUCKETS_CHANGE = 4;
 
     protected Clock mClock;
 
@@ -278,10 +278,10 @@
     }
 
     private static final int[] SUPPORTED_PER_PROCESS_STATE_STANDARD_ENERGY_BUCKETS = {
-            MeasuredEnergyStats.POWER_BUCKET_CPU,
-            MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
-            MeasuredEnergyStats.POWER_BUCKET_WIFI,
-            MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
+            EnergyConsumerStats.POWER_BUCKET_CPU,
+            EnergyConsumerStats.POWER_BUCKET_MOBILE_RADIO,
+            EnergyConsumerStats.POWER_BUCKET_WIFI,
+            EnergyConsumerStats.POWER_BUCKET_BLUETOOTH,
     };
 
     // TimeInState counters need NUM_PROCESS_STATE states in order to accommodate
@@ -384,7 +384,7 @@
     }
 
     /** interface to update rail information for power monitor */
-    public interface MeasuredEnergyRetriever {
+    public interface EnergyStatsRetriever {
         /** Function to fill the map for the rail data stats
          * Used for power monitoring feature
          * @param railStats
@@ -426,7 +426,7 @@
         }
     };
 
-    public final MeasuredEnergyRetriever mMeasuredEnergyRetriever;
+    public final EnergyStatsRetriever mEnergyConsumerRetriever;
 
     /**
      * This handler is running on {@link BackgroundThread}.
@@ -839,7 +839,7 @@
         public StopwatchTimer[] screenBrightnessTimers =
                 new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS];
         /**
-         * Per display screen state the last time {@link #updateDisplayMeasuredEnergyStatsLocked}
+         * Per display screen state the last time {@link #updateDisplayEnergyConsumerStatsLocked}
          * was called.
          */
         public int screenStateAtLastEnergyMeasurement = Display.STATE_UNKNOWN;
@@ -1340,7 +1340,7 @@
 
     @GuardedBy("this")
     @VisibleForTesting
-    protected @Nullable MeasuredEnergyStats.Config mMeasuredEnergyStatsConfig;
+    protected @Nullable EnergyConsumerStats.Config mEnergyConsumerStatsConfig;
 
     /**
      * Accumulated global (generally, device-wide total) charge consumption of various consumers
@@ -1353,14 +1353,15 @@
      */
     @GuardedBy("this")
     @VisibleForTesting
-    protected @Nullable MeasuredEnergyStats mGlobalMeasuredEnergyStats;
-    /** Bluetooth Power calculator for attributing measured bluetooth charge consumption to uids */
+    @Nullable
+    protected EnergyConsumerStats mGlobalEnergyConsumerStats;
+    /** Bluetooth Power calculator for attributing bluetooth EnergyConsumer to uids */
     @Nullable BluetoothPowerCalculator mBluetoothPowerCalculator = null;
-    /** Cpu Power calculator for attributing measured cpu charge consumption to uids */
+    /** Cpu Power calculator for attributing cpu EnergyConsumer to uids */
     @Nullable CpuPowerCalculator mCpuPowerCalculator = null;
-    /** Mobile Radio Power calculator for attributing measured radio charge consumption to uids */
+    /** Mobile Radio Power calculator for attributing radio EnergyConsumer to uids */
     @Nullable MobileRadioPowerCalculator mMobileRadioPowerCalculator = null;
-    /** Wifi Power calculator for attributing measured wifi charge consumption to uids */
+    /** Wifi Power calculator for attributing wifi EnergyConsumer to uids */
     @Nullable WifiPowerCalculator mWifiPowerCalculator = null;
 
     /**
@@ -1635,7 +1636,7 @@
                     mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator, mClock);
         }
         mPlatformIdleStateCallback = null;
-        mMeasuredEnergyRetriever = null;
+        mEnergyConsumerRetriever = null;
         mUserInfoProvider = null;
     }
 
@@ -5237,10 +5238,10 @@
             }
 
             if (shouldScheduleSync
-                    && mGlobalMeasuredEnergyStats != null
-                    && mGlobalMeasuredEnergyStats.isStandardBucketSupported(
-                    MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON)) {
-                // Display measured energy stats is available. Prepare to schedule an
+                    && mGlobalEnergyConsumerStats != null
+                    && mGlobalEnergyConsumerStats.isStandardBucketSupported(
+                    EnergyConsumerStats.POWER_BUCKET_SCREEN_ON)) {
+                // Display energy consumption stats is available. Prepare to schedule an
                 // external sync.
                 externalUpdateFlag |= ExternalStatsSync.UPDATE_DISPLAY;
             }
@@ -7373,44 +7374,44 @@
 
     @GuardedBy("this")
     @Override
-    public long getBluetoothMeasuredBatteryConsumptionUC() {
-        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH);
+    public long getBluetoothEnergyConsumptionUC() {
+        return getPowerBucketConsumptionUC(EnergyConsumerStats.POWER_BUCKET_BLUETOOTH);
     }
 
     @GuardedBy("this")
     @Override
-    public long getCpuMeasuredBatteryConsumptionUC() {
-        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
+    public long getCpuEnergyConsumptionUC() {
+        return getPowerBucketConsumptionUC(EnergyConsumerStats.POWER_BUCKET_CPU);
     }
 
     @GuardedBy("this")
     @Override
-    public long getGnssMeasuredBatteryConsumptionUC() {
-        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_GNSS);
+    public long getGnssEnergyConsumptionUC() {
+        return getPowerBucketConsumptionUC(EnergyConsumerStats.POWER_BUCKET_GNSS);
     }
 
     @GuardedBy("this")
     @Override
-    public long getMobileRadioMeasuredBatteryConsumptionUC() {
-        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO);
+    public long getMobileRadioEnergyConsumptionUC() {
+        return getPowerBucketConsumptionUC(EnergyConsumerStats.POWER_BUCKET_MOBILE_RADIO);
     }
 
     @GuardedBy("this")
     @Override
-    public long getScreenOnMeasuredBatteryConsumptionUC() {
-        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
+    public long getScreenOnEnergyConsumptionUC() {
+        return getPowerBucketConsumptionUC(EnergyConsumerStats.POWER_BUCKET_SCREEN_ON);
     }
 
     @GuardedBy("this")
     @Override
-    public long getScreenDozeMeasuredBatteryConsumptionUC() {
-        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE);
+    public long getScreenDozeEnergyConsumptionUC() {
+        return getPowerBucketConsumptionUC(EnergyConsumerStats.POWER_BUCKET_SCREEN_DOZE);
     }
 
     @GuardedBy("this")
     @Override
-    public long getWifiMeasuredBatteryConsumptionUC() {
-        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI);
+    public long getWifiEnergyConsumptionUC() {
+        return getPowerBucketConsumptionUC(EnergyConsumerStats.POWER_BUCKET_WIFI);
     }
 
     /**
@@ -7422,19 +7423,19 @@
      */
     @GuardedBy("this")
     private long getPowerBucketConsumptionUC(@StandardPowerBucket int bucket) {
-        if (mGlobalMeasuredEnergyStats == null) {
+        if (mGlobalEnergyConsumerStats == null) {
             return POWER_DATA_UNAVAILABLE;
         }
-        return mGlobalMeasuredEnergyStats.getAccumulatedStandardBucketCharge(bucket);
+        return mGlobalEnergyConsumerStats.getAccumulatedStandardBucketCharge(bucket);
     }
 
     @GuardedBy("this")
     @Override
-    public @Nullable long[] getCustomConsumerMeasuredBatteryConsumptionUC() {
-        if (mGlobalMeasuredEnergyStats == null) {
+    public @Nullable long[] getCustomEnergyConsumerBatteryConsumptionUC() {
+        if (mGlobalEnergyConsumerStats == null) {
             return null;
         }
-        return mGlobalMeasuredEnergyStats.getAccumulatedCustomBucketCharges();
+        return mGlobalEnergyConsumerStats.getAccumulatedCustomBucketCharges();
     }
 
     /**
@@ -7443,10 +7444,10 @@
     @GuardedBy("this")
     @Override
     public @NonNull String[] getCustomEnergyConsumerNames() {
-        if (mMeasuredEnergyStatsConfig == null) {
+        if (mEnergyConsumerStatsConfig == null) {
             return new String[0];
         }
-        final String[] names = mMeasuredEnergyStatsConfig.getCustomBucketNames();
+        final String[] names = mEnergyConsumerStatsConfig.getCustomBucketNames();
         for (int i = 0; i < names.length; i++) {
             if (TextUtils.isEmpty(names[i])) {
                 names[i] = "CUSTOM_" + BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i;
@@ -7456,14 +7457,14 @@
     }
 
     /**
-     * Adds measured energy delta to battery history.
+     * Adds energy consumer delta to battery history.
      */
     @GuardedBy("this")
-    public void recordMeasuredEnergyDetailsLocked(long elapsedRealtimeMs,
-            long uptimeMs, MeasuredEnergyDetails measuredEnergyDetails) {
+    public void recordEnergyConsumerDetailsLocked(long elapsedRealtimeMs,
+            long uptimeMs, EnergyConsumerDetails energyConsumerDetails) {
         if (isUsageHistoryEnabled()) {
-            mHistory.recordMeasuredEnergyDetails(elapsedRealtimeMs, uptimeMs,
-                    measuredEnergyDetails);
+            mHistory.recordEnergyConsumerDetails(elapsedRealtimeMs, uptimeMs,
+                    energyConsumerDetails);
         }
     }
 
@@ -7815,16 +7816,16 @@
         private final ArraySet<BinderCallStats> mBinderCallStats = new ArraySet<>();
 
         /**
-         * Measured charge consumption by this uid while on battery.
+         * EnergyConsumer consumption by this uid while on battery.
          * Its '<b>custom</b> power buckets' correspond to the
          * {@link android.hardware.power.stats.EnergyConsumer.ordinal}s of (custom) energy consumer
          * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
          *
          * Will be null if energy consumer data is completely unavailable (in which case
-         * {@link #mGlobalMeasuredEnergyStats} will also be null) or if the power usage by this uid
+         * {@link #mGlobalEnergyConsumerStats} will also be null) or if the power usage by this uid
          * is 0 for every bucket.
          */
-        private MeasuredEnergyStats mUidMeasuredEnergyStats;
+        private EnergyConsumerStats mUidEnergyConsumerStats;
 
         /**
          * Estimated total time spent by the system server handling requests from this uid.
@@ -7905,8 +7906,8 @@
             if (bluetoothControllerActivity != null) {
                 bluetoothControllerActivity.setState(batteryConsumerProcessState, elapsedTimeMs);
             }
-            final MeasuredEnergyStats energyStats =
-                    getOrCreateMeasuredEnergyStatsIfSupportedLocked();
+            final EnergyConsumerStats energyStats =
+                    getOrCreateEnergyConsumerStatsIfSupportedLocked();
             if (energyStats != null) {
                 energyStats.setState(batteryConsumerProcessState, elapsedTimeMs);
             }
@@ -8281,34 +8282,34 @@
         }
 
         @GuardedBy("mBsi")
-        private MeasuredEnergyStats getOrCreateMeasuredEnergyStatsLocked() {
-            if (mUidMeasuredEnergyStats == null) {
-                mUidMeasuredEnergyStats = new MeasuredEnergyStats(mBsi.mMeasuredEnergyStatsConfig);
+        private EnergyConsumerStats getOrCreateEnergyConsumerStatsLocked() {
+            if (mUidEnergyConsumerStats == null) {
+                mUidEnergyConsumerStats = new EnergyConsumerStats(mBsi.mEnergyConsumerStatsConfig);
             }
-            return mUidMeasuredEnergyStats;
+            return mUidEnergyConsumerStats;
         }
 
         @GuardedBy("mBsi")
-        private MeasuredEnergyStats getOrCreateMeasuredEnergyStatsIfSupportedLocked() {
-            if (mUidMeasuredEnergyStats == null && mBsi.mMeasuredEnergyStatsConfig != null) {
-                mUidMeasuredEnergyStats = new MeasuredEnergyStats(mBsi.mMeasuredEnergyStatsConfig);
+        private EnergyConsumerStats getOrCreateEnergyConsumerStatsIfSupportedLocked() {
+            if (mUidEnergyConsumerStats == null && mBsi.mEnergyConsumerStatsConfig != null) {
+                mUidEnergyConsumerStats = new EnergyConsumerStats(mBsi.mEnergyConsumerStatsConfig);
             }
-            return mUidMeasuredEnergyStats;
+            return mUidEnergyConsumerStats;
         }
 
         /** Adds the given charge to the given standard power bucket for this uid. */
         @GuardedBy("mBsi")
         private void addChargeToStandardBucketLocked(long chargeDeltaUC,
                 @StandardPowerBucket int powerBucket, long timestampMs) {
-            final MeasuredEnergyStats measuredEnergyStats =
-                    getOrCreateMeasuredEnergyStatsLocked();
-            measuredEnergyStats.updateStandardBucket(powerBucket, chargeDeltaUC, timestampMs);
+            final EnergyConsumerStats energyConsumerStats =
+                    getOrCreateEnergyConsumerStatsLocked();
+            energyConsumerStats.updateStandardBucket(powerBucket, chargeDeltaUC, timestampMs);
         }
 
         /** Adds the given charge to the given custom power bucket for this uid. */
         @GuardedBy("mBsi")
         private void addChargeToCustomBucketLocked(long chargeDeltaUC, int powerBucket) {
-            getOrCreateMeasuredEnergyStatsLocked().updateCustomBucket(powerBucket, chargeDeltaUC,
+            getOrCreateEnergyConsumerStatsLocked().updateCustomBucket(powerBucket, chargeDeltaUC,
                     mBsi.mClock.elapsedRealtime());
         }
 
@@ -8319,15 +8320,15 @@
          * @return consumption (in microcolombs) used by this uid for this power bucket
          */
         @GuardedBy("mBsi")
-        public long getMeasuredBatteryConsumptionUC(@StandardPowerBucket int bucket) {
-            if (mBsi.mGlobalMeasuredEnergyStats == null
-                    || !mBsi.mGlobalMeasuredEnergyStats.isStandardBucketSupported(bucket)) {
+        public long getEnergyConsumptionUC(@StandardPowerBucket int bucket) {
+            if (mBsi.mGlobalEnergyConsumerStats == null
+                    || !mBsi.mGlobalEnergyConsumerStats.isStandardBucketSupported(bucket)) {
                 return POWER_DATA_UNAVAILABLE;
             }
-            if (mUidMeasuredEnergyStats == null) {
+            if (mUidEnergyConsumerStats == null) {
                 return 0L; // It is supported, but was never filled, so it must be 0
             }
-            return mUidMeasuredEnergyStats.getAccumulatedStandardBucketCharge(bucket);
+            return mUidEnergyConsumerStats.getAccumulatedStandardBucketCharge(bucket);
         }
 
         /**
@@ -8335,94 +8336,94 @@
          * bucket and a process state, such as Uid.PROCESS_STATE_TOP.
          */
         @GuardedBy("mBsi")
-        public long getMeasuredBatteryConsumptionUC(@StandardPowerBucket int bucket,
+        public long getEnergyConsumptionUC(@StandardPowerBucket int bucket,
                 int processState) {
-            if (mBsi.mGlobalMeasuredEnergyStats == null
-                    || !mBsi.mGlobalMeasuredEnergyStats.isStandardBucketSupported(bucket)) {
+            if (mBsi.mGlobalEnergyConsumerStats == null
+                    || !mBsi.mGlobalEnergyConsumerStats.isStandardBucketSupported(bucket)) {
                 return POWER_DATA_UNAVAILABLE;
             }
-            if (mUidMeasuredEnergyStats == null) {
+            if (mUidEnergyConsumerStats == null) {
                 return 0L; // It is supported, but was never filled, so it must be 0
             }
-            return mUidMeasuredEnergyStats.getAccumulatedStandardBucketCharge(bucket, processState);
+            return mUidEnergyConsumerStats.getAccumulatedStandardBucketCharge(bucket, processState);
         }
 
         @GuardedBy("mBsi")
         @Override
-        public long[] getCustomConsumerMeasuredBatteryConsumptionUC() {
-            if (mBsi.mGlobalMeasuredEnergyStats == null) {
+        public long[] getCustomEnergyConsumerBatteryConsumptionUC() {
+            if (mBsi.mGlobalEnergyConsumerStats == null) {
                 return null;
             }
-            if (mUidMeasuredEnergyStats == null) {
+            if (mUidEnergyConsumerStats == null) {
                 // Custom buckets may exist. But all values for this uid are 0 so we report all 0s.
-                return new long[mBsi.mGlobalMeasuredEnergyStats.getNumberCustomPowerBuckets()];
+                return new long[mBsi.mGlobalEnergyConsumerStats.getNumberCustomPowerBuckets()];
             }
-            return mUidMeasuredEnergyStats.getAccumulatedCustomBucketCharges();
+            return mUidEnergyConsumerStats.getAccumulatedCustomBucketCharges();
         }
 
         @GuardedBy("mBsi")
         @Override
-        public long getBluetoothMeasuredBatteryConsumptionUC() {
-            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH);
+        public long getBluetoothEnergyConsumptionUC() {
+            return getEnergyConsumptionUC(EnergyConsumerStats.POWER_BUCKET_BLUETOOTH);
         }
 
         @GuardedBy("mBsi")
         @Override
-        public long getBluetoothMeasuredBatteryConsumptionUC(
+        public long getBluetoothEnergyConsumptionUC(
                 @BatteryConsumer.ProcessState int processState) {
-            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
+            return getEnergyConsumptionUC(EnergyConsumerStats.POWER_BUCKET_BLUETOOTH,
                     processState);
         }
 
         @GuardedBy("mBsi")
         @Override
-        public long getCpuMeasuredBatteryConsumptionUC() {
-            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
+        public long getCpuEnergyConsumptionUC() {
+            return getEnergyConsumptionUC(EnergyConsumerStats.POWER_BUCKET_CPU);
         }
 
         @GuardedBy("mBsi")
         @Override
-        public long getCpuMeasuredBatteryConsumptionUC(
+        public long getCpuEnergyConsumptionUC(
                 @BatteryConsumer.ProcessState int processState) {
-            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU,
+            return getEnergyConsumptionUC(EnergyConsumerStats.POWER_BUCKET_CPU,
                     processState);
         }
 
         @GuardedBy("mBsi")
         @Override
-        public long getGnssMeasuredBatteryConsumptionUC() {
-            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_GNSS);
+        public long getGnssEnergyConsumptionUC() {
+            return getEnergyConsumptionUC(EnergyConsumerStats.POWER_BUCKET_GNSS);
         }
 
         @GuardedBy("mBsi")
         @Override
-        public long getMobileRadioMeasuredBatteryConsumptionUC() {
-            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO);
+        public long getMobileRadioEnergyConsumptionUC() {
+            return getEnergyConsumptionUC(EnergyConsumerStats.POWER_BUCKET_MOBILE_RADIO);
         }
 
         @GuardedBy("mBsi")
         @Override
-        public long getMobileRadioMeasuredBatteryConsumptionUC(int processState) {
-            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
+        public long getMobileRadioEnergyConsumptionUC(int processState) {
+            return getEnergyConsumptionUC(EnergyConsumerStats.POWER_BUCKET_MOBILE_RADIO,
                     processState);
         }
 
         @GuardedBy("mBsi")
         @Override
-        public long getScreenOnMeasuredBatteryConsumptionUC() {
-            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
+        public long getScreenOnEnergyConsumptionUC() {
+            return getEnergyConsumptionUC(EnergyConsumerStats.POWER_BUCKET_SCREEN_ON);
         }
 
         @GuardedBy("mBsi")
         @Override
-        public long getWifiMeasuredBatteryConsumptionUC() {
-            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI);
+        public long getWifiEnergyConsumptionUC() {
+            return getEnergyConsumptionUC(EnergyConsumerStats.POWER_BUCKET_WIFI);
         }
 
         @GuardedBy("mBsi")
         @Override
-        public long getWifiMeasuredBatteryConsumptionUC(int processState) {
-            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_WIFI,
+        public long getWifiEnergyConsumptionUC(int processState) {
+            return getEnergyConsumptionUC(EnergyConsumerStats.POWER_BUCKET_WIFI,
                     processState);
         }
 
@@ -9222,10 +9223,10 @@
             resetIfNotNull(mBluetoothControllerActivity, false, realtimeUs);
             resetIfNotNull(mModemControllerActivity, false, realtimeUs);
 
-            if (resetReason == RESET_REASON_MEASURED_ENERGY_BUCKETS_CHANGE) {
-                mUidMeasuredEnergyStats = null;
+            if (resetReason == RESET_REASON_ENERGY_CONSUMER_BUCKETS_CHANGE) {
+                mUidEnergyConsumerStats = null;
             } else {
-                MeasuredEnergyStats.resetIfNotNull(mUidMeasuredEnergyStats);
+                EnergyConsumerStats.resetIfNotNull(mUidEnergyConsumerStats);
             }
 
             resetIfNotNull(mUserCpuTime, false, realtimeUs);
@@ -10365,8 +10366,8 @@
                             elapsedRealtimeMs);
                 }
 
-                final MeasuredEnergyStats energyStats =
-                        getOrCreateMeasuredEnergyStatsIfSupportedLocked();
+                final EnergyConsumerStats energyStats =
+                        getOrCreateEnergyConsumerStatsIfSupportedLocked();
                 if (energyStats != null) {
                     energyStats.setState(batteryConsumerProcessState, elapsedRealtimeMs);
                 }
@@ -10701,12 +10702,12 @@
     }
 
     public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb,
-            MeasuredEnergyRetriever energyStatsCb, UserInfoProvider userInfoProvider) {
+            EnergyStatsRetriever energyStatsCb, UserInfoProvider userInfoProvider) {
         this(Clock.SYSTEM_CLOCK, systemDir, handler, cb, energyStatsCb, userInfoProvider);
     }
 
     private BatteryStatsImpl(Clock clock, File systemDir, Handler handler,
-            PlatformIdleStateCallback cb, MeasuredEnergyRetriever energyStatsCb,
+            PlatformIdleStateCallback cb, EnergyStatsRetriever energyStatsCb,
             UserInfoProvider userInfoProvider) {
         init(clock);
 
@@ -10733,7 +10734,7 @@
         initDischarge(realtimeUs);
         updateDailyDeadlineLocked();
         mPlatformIdleStateCallback = cb;
-        mMeasuredEnergyRetriever = energyStatsCb;
+        mEnergyConsumerRetriever = energyStatsCb;
         mUserInfoProvider = userInfoProvider;
 
         // Notify statsd that the system is initially not in doze.
@@ -11477,7 +11478,7 @@
 
         mTmpRailStats.reset();
 
-        MeasuredEnergyStats.resetIfNotNull(mGlobalMeasuredEnergyStats);
+        EnergyConsumerStats.resetIfNotNull(mGlobalEnergyConsumerStats);
 
         resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs);
 
@@ -11636,7 +11637,7 @@
             }
 
             final SparseDoubleArray uidEstimatedConsumptionMah =
-                    (mGlobalMeasuredEnergyStats != null
+                    (mGlobalEnergyConsumerStats != null
                             && mWifiPowerCalculator != null && consumedChargeUC > 0) ?
                             new SparseDoubleArray() : null;
             double totalEstimatedConsumptionMah = 0;
@@ -11948,10 +11949,10 @@
                 }
             }
 
-            // Update the MeasuredEnergyStats information.
+            // Update the EnergyConsumerStats information.
             if (uidEstimatedConsumptionMah != null) {
-                mGlobalMeasuredEnergyStats.updateStandardBucket(
-                        MeasuredEnergyStats.POWER_BUCKET_WIFI, consumedChargeUC);
+                mGlobalEnergyConsumerStats.updateStandardBucket(
+                        EnergyConsumerStats.POWER_BUCKET_WIFI, consumedChargeUC);
 
                 // Now calculate the consumption for each uid, according to its proportional usage.
                 if (!mHasWifiReporting) {
@@ -11961,7 +11962,7 @@
                     totalEstimatedConsumptionMah = mWifiPowerCalculator
                             .calcGlobalPowerWithoutControllerDataMah(globalTimeMs);
                 }
-                distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_WIFI,
+                distributeEnergyToUidsLocked(EnergyConsumerStats.POWER_BUCKET_WIFI,
                         consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedConsumptionMah,
                         elapsedRealtimeMs);
             }
@@ -12003,9 +12004,9 @@
 
             final SparseDoubleArray uidEstimatedConsumptionMah;
             if (consumedChargeUC > 0 && mMobileRadioPowerCalculator != null
-                    && mGlobalMeasuredEnergyStats != null) {
-                mGlobalMeasuredEnergyStats.updateStandardBucket(
-                        MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO, consumedChargeUC);
+                    && mGlobalEnergyConsumerStats != null) {
+                mGlobalEnergyConsumerStats.updateStandardBucket(
+                        EnergyConsumerStats.POWER_BUCKET_MOBILE_RADIO, consumedChargeUC);
                 uidEstimatedConsumptionMah = new SparseDoubleArray();
             } else {
                 uidEstimatedConsumptionMah = null;
@@ -12120,7 +12121,7 @@
                                 (totalAppRadioTimeUs * appPackets) / totalPackets;
                         u.noteMobileRadioActiveTimeLocked(appRadioTimeUs, elapsedRealtimeMs);
 
-                        // Distribute measured mobile radio charge consumption based on app radio
+                        // Distribute mobile radio charge consumption based on app radio
                         // active time
                         if (uidEstimatedConsumptionMah != null) {
                             uidEstimatedConsumptionMah.incrementValue(u.getUid(),
@@ -12164,7 +12165,7 @@
                 }
 
 
-                // Update the MeasuredEnergyStats information.
+                // Update the EnergyConsumerStats information.
                 if (uidEstimatedConsumptionMah != null) {
                     double totalEstimatedConsumptionMah = 0.0;
 
@@ -12197,7 +12198,7 @@
                     totalEstimatedConsumptionMah +=
                             mMobileRadioPowerCalculator.calcScanTimePowerMah(scanTimeMs);
 
-                    distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO,
+                    distributeEnergyToUidsLocked(EnergyConsumerStats.POWER_BUCKET_MOBILE_RADIO,
                             consumedChargeUC, uidEstimatedConsumptionMah,
                             totalEstimatedConsumptionMah, elapsedRealtimeMs);
                 }
@@ -12407,7 +12408,7 @@
         }
 
         final SparseDoubleArray uidEstimatedConsumptionMah =
-                (mGlobalMeasuredEnergyStats != null
+                (mGlobalEnergyConsumerStats != null
                         && mBluetoothPowerCalculator != null && consumedChargeUC > 0) ?
                         new SparseDoubleArray() : null;
 
@@ -12590,15 +12591,15 @@
             mBluetoothActivity.getPowerCounter().addCountLocked((long) controllerMaMs);
         }
 
-        // Update the MeasuredEnergyStats information.
+        // Update the EnergyConsumerStats information.
         if (uidEstimatedConsumptionMah != null) {
-            mGlobalMeasuredEnergyStats.updateStandardBucket(
-                    MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH, consumedChargeUC);
+            mGlobalEnergyConsumerStats.updateStandardBucket(
+                    EnergyConsumerStats.POWER_BUCKET_BLUETOOTH, consumedChargeUC);
 
             double totalEstimatedMah
                     = mBluetoothPowerCalculator.calculatePowerMah(rxTimeMs, txTimeMs, idleTimeMs);
             totalEstimatedMah = Math.max(totalEstimatedMah, controllerMaMs / MILLISECONDS_IN_HOUR);
-            distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH,
+            distributeEnergyToUidsLocked(EnergyConsumerStats.POWER_BUCKET_BLUETOOTH,
                     consumedChargeUC, uidEstimatedConsumptionMah, totalEstimatedMah,
                     elapsedRealtimeMs);
         }
@@ -12686,12 +12687,12 @@
      */
     @GuardedBy("this")
     @SuppressWarnings("GuardedBy") // errorprone false positive on u.addChargeToStandardBucketLocked
-    private void updateCpuMeasuredEnergyStatsLocked(@NonNull long[] clusterChargeUC,
+    private void updateCpuEnergyConsumerStatsLocked(@NonNull long[] clusterChargeUC,
             @NonNull CpuDeltaPowerAccumulator accumulator) {
         if (DEBUG_ENERGY) {
             Slog.d(TAG, "Updating cpu cluster stats: " + Arrays.toString(clusterChargeUC));
         }
-        if (mGlobalMeasuredEnergyStats == null) {
+        if (mGlobalEnergyConsumerStats == null) {
             return;
         }
 
@@ -12704,10 +12705,10 @@
 
         final long timestampMs = mClock.elapsedRealtime();
 
-        mGlobalMeasuredEnergyStats.updateStandardBucket(MeasuredEnergyStats.POWER_BUCKET_CPU,
+        mGlobalEnergyConsumerStats.updateStandardBucket(EnergyConsumerStats.POWER_BUCKET_CPU,
                 totalCpuChargeUC, timestampMs);
 
-        // Calculate the measured microcoulombs/calculated milliamp-hour charge ratio for each
+        // Calculate the microcoulombs/milliamp-hour charge ratio for each
         // cluster to normalize  each uid's estimated power usage against actual power usage for
         // a given cluster.
         final double[] clusterChargeRatio = new double[numClusters];
@@ -12730,14 +12731,14 @@
             final Uid uid = accumulator.perUidCpuClusterChargesMah.keyAt(i);
             final double[] uidClusterChargesMah = accumulator.perUidCpuClusterChargesMah.valueAt(i);
 
-            // Iterate each cpu cluster and sum the proportional measured cpu cluster charge to
+            // Iterate each cpu cluster and sum the proportional cpu cluster charge to
             // get the total cpu charge consumed by a uid.
             long uidCpuChargeUC = 0;
             for (int cluster = 0; cluster < numClusters; cluster++) {
                 final double uidClusterChargeMah = uidClusterChargesMah[cluster];
 
-                // Proportionally allocate the measured cpu cluster charge to a uid using the
-                // measured charge/calculated charge ratio. Add 0.5 to round the proportional
+                // Proportionally allocate the cpu cluster charge to a uid using the
+                // cluster charge/charge ratio. Add 0.5 to round the proportional
                 // charge double to the nearest long value.
                 final long uidClusterChargeUC =
                         (long) (uidClusterChargeMah * clusterChargeRatio[cluster]
@@ -12747,14 +12748,13 @@
             }
 
             if (uidCpuChargeUC < 0) {
-                Slog.wtf(TAG,
-                        "Unexpected proportional measured charge (" + uidCpuChargeUC + ") for uid "
-                                + uid.mUid);
+                Slog.wtf(TAG, "Unexpected proportional EnergyConsumer charge "
+                        + "(" + uidCpuChargeUC + ") for uid " + uid.mUid);
                 continue;
             }
 
             uid.addChargeToStandardBucketLocked(uidCpuChargeUC,
-                    MeasuredEnergyStats.POWER_BUCKET_CPU, timestampMs);
+                    EnergyConsumerStats.POWER_BUCKET_CPU, timestampMs);
         }
     }
 
@@ -12770,10 +12770,10 @@
      * @param screenStates each screen state at the time this data collection was scheduled
      */
     @GuardedBy("this")
-    public void updateDisplayMeasuredEnergyStatsLocked(long[] chargesUC, int[] screenStates,
+    public void updateDisplayEnergyConsumerStatsLocked(long[] chargesUC, int[] screenStates,
             long elapsedRealtimeMs) {
         if (DEBUG_ENERGY) Slog.d(TAG, "Updating display stats: " + Arrays.toString(chargesUC));
-        if (mGlobalMeasuredEnergyStats == null) {
+        if (mGlobalEnergyConsumerStats == null) {
             return;
         }
 
@@ -12825,9 +12825,9 @@
             }
 
             final @StandardPowerBucket int powerBucket =
-                    MeasuredEnergyStats.getDisplayPowerBucket(oldScreenStates[i]);
-            mGlobalMeasuredEnergyStats.updateStandardBucket(powerBucket, chargeUC);
-            if (powerBucket == MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON) {
+                    EnergyConsumerStats.getDisplayPowerBucket(oldScreenStates[i]);
+            mGlobalEnergyConsumerStats.updateStandardBucket(powerBucket, chargeUC);
+            if (powerBucket == EnergyConsumerStats.POWER_BUCKET_SCREEN_ON) {
                 totalScreenOnChargeUC += chargeUC;
             }
         }
@@ -12853,7 +12853,7 @@
             if (fgTimeUs == 0) continue;
             fgTimeUsArray.put(uid.getUid(), (double) fgTimeUs);
         }
-        distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON,
+        distributeEnergyToUidsLocked(EnergyConsumerStats.POWER_BUCKET_SCREEN_ON,
                 totalScreenOnChargeUC, fgTimeUsArray, 0, elapsedRealtimeMs);
     }
 
@@ -12863,9 +12863,9 @@
      * @param chargeUC amount of charge (microcoulombs) used by GNSS since this was last called.
      */
     @GuardedBy("this")
-    public void updateGnssMeasuredEnergyStatsLocked(long chargeUC, long elapsedRealtimeMs) {
+    public void updateGnssEnergyConsumerStatsLocked(long chargeUC, long elapsedRealtimeMs) {
         if (DEBUG_ENERGY) Slog.d(TAG, "Updating gnss stats: " + chargeUC);
-        if (mGlobalMeasuredEnergyStats == null) {
+        if (mGlobalEnergyConsumerStats == null) {
             return;
         }
 
@@ -12884,7 +12884,7 @@
             return;
         }
 
-        mGlobalMeasuredEnergyStats.updateStandardBucket(MeasuredEnergyStats.POWER_BUCKET_GNSS,
+        mGlobalEnergyConsumerStats.updateStandardBucket(EnergyConsumerStats.POWER_BUCKET_GNSS,
                 chargeUC);
 
         // Collect the per uid time since mark so that we can normalize power.
@@ -12897,7 +12897,7 @@
             if (gnssTimeUs == 0) continue;
             gnssTimeUsArray.put(uid.getUid(), (double) gnssTimeUs);
         }
-        distributeEnergyToUidsLocked(MeasuredEnergyStats.POWER_BUCKET_GNSS, chargeUC,
+        distributeEnergyToUidsLocked(EnergyConsumerStats.POWER_BUCKET_GNSS, chargeUC,
                 gnssTimeUsArray, 0, elapsedRealtimeMs);
     }
 
@@ -12911,18 +12911,18 @@
      */
     @GuardedBy("this")
     @SuppressWarnings("GuardedBy") // errorprone false positive on u.addChargeToCustomBucketLocked
-    public void updateCustomMeasuredEnergyStatsLocked(int customPowerBucket,
+    public void updateCustomEnergyConsumerStatsLocked(int customPowerBucket,
             long totalChargeUC, @Nullable SparseLongArray uidCharges) {
         if (DEBUG_ENERGY) {
-            Slog.d(TAG, "Updating attributed measured charge stats for custom bucket "
+            Slog.d(TAG, "Updating attributed EnergyConsumer stats for custom bucket "
                     + customPowerBucket
                     + " with total charge " + totalChargeUC
-                    + " and uid charges " + String.valueOf(uidCharges));
+                    + " and uid charges " + uidCharges);
         }
-        if (mGlobalMeasuredEnergyStats == null) return;
+        if (mGlobalEnergyConsumerStats == null) return;
         if (!mOnBatteryInternal || mIgnoreNextExternalStats || totalChargeUC <= 0) return;
 
-        mGlobalMeasuredEnergyStats.updateCustomBucket(customPowerBucket, totalChargeUC,
+        mGlobalEnergyConsumerStats.updateCustomBucket(customPowerBucket, totalChargeUC,
                 mClock.elapsedRealtime());
 
         if (uidCharges == null) return;
@@ -12942,8 +12942,9 @@
                 // Recently removed uids (especially common for isolated uids) can reach this path
                 // and are ignored.
                 if (!Process.isIsolated(uidInt)) {
-                    Slog.w(TAG, "Received measured charge " + totalChargeUC + " for custom bucket "
-                            + customPowerBucket + " for non-existent uid " + uidInt);
+                    Slog.w(TAG, "Received EnergyConsumer charge " + totalChargeUC
+                            + " for custom bucket " + customPowerBucket + " for non-existent uid "
+                            + uidInt);
                 }
             }
         }
@@ -12993,10 +12994,10 @@
      * Read and record Rail Energy data.
      */
     public void updateRailStatsLocked() {
-        if (mMeasuredEnergyRetriever == null || !mTmpRailStats.isRailStatsAvailable()) {
+        if (mEnergyConsumerRetriever == null || !mTmpRailStats.isRailStatsAvailable()) {
             return;
         }
-        mMeasuredEnergyRetriever.fillRailDataStats(mTmpRailStats);
+        mEnergyConsumerRetriever.fillRailDataStats(mTmpRailStats);
     }
 
     /** Informs that external stats data has been completely flushed. */
@@ -13166,7 +13167,7 @@
      */
     @GuardedBy("this")
     public void updateCpuTimeLocked(boolean onBattery, boolean onBatteryScreenOff,
-            long[] measuredCpuClusterChargeUC) {
+            long[] cpuClusterChargeUC) {
         if (mPowerProfile == null) {
             return;
         }
@@ -13222,16 +13223,16 @@
                 ? null : new SparseLongArray();
 
         final CpuDeltaPowerAccumulator powerAccumulator;
-        if (mGlobalMeasuredEnergyStats != null
-                && mGlobalMeasuredEnergyStats.isStandardBucketSupported(
-                MeasuredEnergyStats.POWER_BUCKET_CPU) && mCpuPowerCalculator != null) {
-            if (measuredCpuClusterChargeUC == null) {
+        if (mGlobalEnergyConsumerStats != null
+                && mGlobalEnergyConsumerStats.isStandardBucketSupported(
+                EnergyConsumerStats.POWER_BUCKET_CPU) && mCpuPowerCalculator != null) {
+            if (cpuClusterChargeUC == null) {
                 Slog.wtf(TAG,
-                        "POWER_BUCKET_CPU supported but no measured Cpu Cluster charge reported "
-                                + "on updateCpuTimeLocked!");
+                        "POWER_BUCKET_CPU supported but no EnergyConsumer Cpu Cluster charge "
+                                + "reported on updateCpuTimeLocked!");
                 powerAccumulator = null;
             } else {
-                // Cpu Measured Energy is supported, create an object to accumulate the estimated
+                // Cpu EnergyConsumer is supported, create an object to accumulate the estimated
                 // charge consumption since the last cpu update
                 final int numClusters = mPowerProfile.getNumCpuClusters();
                 powerAccumulator = new CpuDeltaPowerAccumulator(mCpuPowerCalculator, numClusters);
@@ -13250,7 +13251,7 @@
                 powerAccumulator);
         mNumAllUidCpuTimeReads += 2;
         if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
-            // Cpu Active times do not get any info ony how to attribute measured Cpu Cluster
+            // Cpu Active times do not get any info ony how to attribute Cpu Cluster
             // charge, so not need to provide the powerAccumulator
             readKernelUidCpuActiveTimesLocked(onBattery);
             readKernelUidCpuClusterTimesLocked(onBattery, powerAccumulator);
@@ -13260,7 +13261,7 @@
         updateSystemServerThreadStats();
 
         if (powerAccumulator != null) {
-            updateCpuMeasuredEnergyStatsLocked(measuredCpuClusterChargeUC, powerAccumulator);
+            updateCpuEnergyConsumerStatsLocked(cpuClusterChargeUC, powerAccumulator);
         }
     }
 
@@ -14691,7 +14692,7 @@
     }
 
     @GuardedBy("this")
-    private boolean isUsageHistoryEnabled() {
+    boolean isUsageHistoryEnabled() {
         return mConstants.RECORD_USAGE_HISTORY;
     }
 
@@ -14702,7 +14703,7 @@
     }
 
     /**
-     * Initialize the measured charge stats data structures.
+     * Initialize the EnergyConsumer stats data structures.
      *
      * @param supportedStandardBuckets boolean array indicating which {@link StandardPowerBucket}s
      *                                 are currently supported. If null, none are supported
@@ -14710,7 +14711,7 @@
      * @param customBucketNames        names of custom (OTHER) EnergyConsumers on this device
      */
     @GuardedBy("this")
-    public void initMeasuredEnergyStatsLocked(@Nullable boolean[] supportedStandardBuckets,
+    public void initEnergyConsumerStatsLocked(@Nullable boolean[] supportedStandardBuckets,
             String[] customBucketNames) {
         final int numDisplays = mPerDisplayBatteryStats.length;
         for (int i = 0; i < numDisplays; i++) {
@@ -14720,44 +14721,44 @@
 
         final boolean compatibleConfig;
         if (supportedStandardBuckets != null) {
-            final MeasuredEnergyStats.Config config = new MeasuredEnergyStats.Config(
+            final EnergyConsumerStats.Config config = new EnergyConsumerStats.Config(
                     supportedStandardBuckets, customBucketNames,
                     SUPPORTED_PER_PROCESS_STATE_STANDARD_ENERGY_BUCKETS,
                     getBatteryConsumerProcessStateNames());
 
-            if (mMeasuredEnergyStatsConfig == null) {
+            if (mEnergyConsumerStatsConfig == null) {
                 compatibleConfig = true;
             } else {
-                compatibleConfig = mMeasuredEnergyStatsConfig.isCompatible(config);
+                compatibleConfig = mEnergyConsumerStatsConfig.isCompatible(config);
             }
 
-            mMeasuredEnergyStatsConfig = config;
-            mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(config);
+            mEnergyConsumerStatsConfig = config;
+            mGlobalEnergyConsumerStats = new EnergyConsumerStats(config);
 
-            if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_BLUETOOTH]) {
+            if (supportedStandardBuckets[EnergyConsumerStats.POWER_BUCKET_BLUETOOTH]) {
                 mBluetoothPowerCalculator = new BluetoothPowerCalculator(mPowerProfile);
             }
-            if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU]) {
+            if (supportedStandardBuckets[EnergyConsumerStats.POWER_BUCKET_CPU]) {
                 mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile);
             }
-            if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_MOBILE_RADIO]) {
+            if (supportedStandardBuckets[EnergyConsumerStats.POWER_BUCKET_MOBILE_RADIO]) {
                 mMobileRadioPowerCalculator = new MobileRadioPowerCalculator(mPowerProfile);
             }
-            if (supportedStandardBuckets[MeasuredEnergyStats.POWER_BUCKET_WIFI]) {
+            if (supportedStandardBuckets[EnergyConsumerStats.POWER_BUCKET_WIFI]) {
                 mWifiPowerCalculator = new WifiPowerCalculator(mPowerProfile);
             }
         } else {
-            compatibleConfig = (mMeasuredEnergyStatsConfig == null);
-            // Measured energy no longer supported, wipe out the existing data.
-            mMeasuredEnergyStatsConfig = null;
-            mGlobalMeasuredEnergyStats = null;
+            compatibleConfig = (mEnergyConsumerStatsConfig == null);
+            // EnergyConsumer no longer supported, wipe out the existing data.
+            mEnergyConsumerStatsConfig = null;
+            mGlobalEnergyConsumerStats = null;
         }
 
         if (!compatibleConfig) {
             // Supported power buckets changed since last boot.
             // Existing data is no longer reliable.
             resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime(),
-                    RESET_REASON_MEASURED_ENERGY_BUCKETS_CHANGE);
+                    RESET_REASON_ENERGY_CONSUMER_BUCKETS_CHANGE);
         }
     }
 
@@ -15059,31 +15060,31 @@
     }
 
     /**
-     * Dump measured charge stats
+     * Dump EnergyConsumer stats
      */
     @GuardedBy("this")
-    public void dumpMeasuredEnergyStatsLocked(PrintWriter pw) {
-        pw.printf("On battery measured charge stats (microcoulombs) \n");
-        if (mGlobalMeasuredEnergyStats == null) {
+    public void dumpEnergyConsumerStatsLocked(PrintWriter pw) {
+        pw.printf("On-battery energy consumer stats (microcoulombs) \n");
+        if (mGlobalEnergyConsumerStats == null) {
             pw.printf("    Not supported on this device.\n");
             return;
         }
 
-        dumpMeasuredEnergyStatsLocked(pw, "global usage", mGlobalMeasuredEnergyStats);
+        dumpEnergyConsumerStatsLocked(pw, "global usage", mGlobalEnergyConsumerStats);
 
         int size = mUidStats.size();
         for (int i = 0; i < size; i++) {
             final int u = mUidStats.keyAt(i);
             final Uid uid = mUidStats.get(u);
             final String name = "uid " + uid.mUid;
-            dumpMeasuredEnergyStatsLocked(pw, name, uid.mUidMeasuredEnergyStats);
+            dumpEnergyConsumerStatsLocked(pw, name, uid.mUidEnergyConsumerStats);
         }
     }
 
-    /** Dump measured charge stats for the given uid */
+    /** Dump EnergyConsumer stats for the given uid */
     @GuardedBy("this")
-    private void dumpMeasuredEnergyStatsLocked(PrintWriter pw, String name,
-            MeasuredEnergyStats stats) {
+    private void dumpEnergyConsumerStatsLocked(PrintWriter pw, String name,
+            EnergyConsumerStats stats) {
         if (stats == null) return;
         final IndentingPrintWriter iPw = new IndentingPrintWriter(pw, "    ");
         iPw.increaseIndent();
@@ -15292,17 +15293,17 @@
         mNextMaxDailyDeadlineMs = in.readLong();
         mBatteryTimeToFullSeconds = in.readLong();
 
-        final MeasuredEnergyStats.Config config = MeasuredEnergyStats.Config.createFromParcel(in);
-        final MeasuredEnergyStats measuredEnergyStats =
-                MeasuredEnergyStats.createAndReadSummaryFromParcel(mMeasuredEnergyStatsConfig, in);
+        final EnergyConsumerStats.Config config = EnergyConsumerStats.Config.createFromParcel(in);
+        final EnergyConsumerStats energyConsumerStats =
+                EnergyConsumerStats.createAndReadSummaryFromParcel(mEnergyConsumerStatsConfig, in);
         if (config != null && Arrays.equals(config.getStateNames(),
                 getBatteryConsumerProcessStateNames())) {
             /**
              * WARNING: Supported buckets may have changed across boots. Bucket mismatch is handled
-             *          later when {@link #initMeasuredEnergyStatsLocked} is called.
+             *          later when {@link #initEnergyConsumerStatsLocked} is called.
              */
-            mMeasuredEnergyStatsConfig = config;
-            mGlobalMeasuredEnergyStats = measuredEnergyStats;
+            mEnergyConsumerStatsConfig = config;
+            mGlobalEnergyConsumerStats = energyConsumerStats;
         }
 
         mStartCount++;
@@ -15631,8 +15632,8 @@
                 u.mWifiRadioApWakeupCount = null;
             }
 
-            u.mUidMeasuredEnergyStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(
-                    mMeasuredEnergyStatsConfig, in);
+            u.mUidEnergyConsumerStats = EnergyConsumerStats.createAndReadSummaryFromParcel(
+                    mEnergyConsumerStatsConfig, in);
 
             int NW = in.readInt();
             if (NW > (MAX_WAKELOCKS_PER_UID+1)) {
@@ -15806,8 +15807,8 @@
         out.writeLong(mNextMaxDailyDeadlineMs);
         out.writeLong(mBatteryTimeToFullSeconds);
 
-        MeasuredEnergyStats.Config.writeToParcel(mMeasuredEnergyStatsConfig, out);
-        MeasuredEnergyStats.writeSummaryToParcel(mGlobalMeasuredEnergyStats, out);
+        EnergyConsumerStats.Config.writeToParcel(mEnergyConsumerStatsConfig, out);
+        EnergyConsumerStats.writeSummaryToParcel(mGlobalEnergyConsumerStats, out);
 
         mScreenOnTimer.writeSummaryFromParcelLocked(out, nowRealtime);
         mScreenDozeTimer.writeSummaryFromParcelLocked(out, nowRealtime);
@@ -16157,7 +16158,7 @@
                 out.writeInt(0);
             }
 
-            MeasuredEnergyStats.writeSummaryToParcel(u.mUidMeasuredEnergyStats, out);
+            EnergyConsumerStats.writeSummaryToParcel(u.mUidEnergyConsumerStats, out);
 
             final ArrayMap<String, Uid.Wakelock> wakeStats = u.mWakelockStats.getMap();
             int NW = wakeStats.size();
@@ -16393,7 +16394,7 @@
         dumpCpuPowerBracketsLocked(pw);
 
         pw.println();
-        dumpMeasuredEnergyStatsLocked(pw);
+        dumpEnergyConsumerStatsLocked(pw);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index 7da9197..ebd4aec 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -91,7 +91,7 @@
                 mPowerCalculators.add(new ScreenPowerCalculator(mPowerProfile));
                 mPowerCalculators.add(new AmbientDisplayPowerCalculator(mPowerProfile));
                 mPowerCalculators.add(new IdlePowerCalculator(mPowerProfile));
-                mPowerCalculators.add(new CustomMeasuredPowerCalculator(mPowerProfile));
+                mPowerCalculators.add(new CustomEnergyConsumerPowerCalculator(mPowerProfile));
                 mPowerCalculators.add(new UserPowerCalculator());
 
                 // It is important that SystemServicePowerCalculator be applied last,
diff --git a/services/core/java/com/android/server/power/stats/BluetoothPowerCalculator.java b/services/core/java/com/android/server/power/stats/BluetoothPowerCalculator.java
index 0d4d4f2..2d96fcc 100644
--- a/services/core/java/com/android/server/power/stats/BluetoothPowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/BluetoothPowerCalculator.java
@@ -93,11 +93,11 @@
             calculateApp(app, powerAndDuration, query);
         }
 
-        final long measuredChargeUC = batteryStats.getBluetoothMeasuredBatteryConsumptionUC();
-        final int powerModel = getPowerModel(measuredChargeUC, query);
+        final long consumedEnergyUC = batteryStats.getBluetoothEnergyConsumptionUC();
+        final int powerModel = getPowerModel(consumedEnergyUC, query);
         final ControllerActivityCounter activityCounter =
                 batteryStats.getBluetoothControllerActivity();
-        calculatePowerAndDuration(null, powerModel, measuredChargeUC,
+        calculatePowerAndDuration(null, powerModel, consumedEnergyUC,
                 activityCounter, query.shouldForceUsePowerProfileModel(), powerAndDuration);
 
         // Subtract what the apps used, but clamp to 0.
@@ -127,12 +127,12 @@
 
     private void calculateApp(UidBatteryConsumer.Builder app, PowerAndDuration powerAndDuration,
             BatteryUsageStatsQuery query) {
-        final long measuredChargeUC =
-                app.getBatteryStatsUid().getBluetoothMeasuredBatteryConsumptionUC();
-        final int powerModel = getPowerModel(measuredChargeUC, query);
+        final long consumedEnergyUC =
+                app.getBatteryStatsUid().getBluetoothEnergyConsumptionUC();
+        final int powerModel = getPowerModel(consumedEnergyUC, query);
         final ControllerActivityCounter activityCounter =
                 app.getBatteryStatsUid().getBluetoothControllerActivity();
-        calculatePowerAndDuration(app.getBatteryStatsUid(), powerModel, measuredChargeUC,
+        calculatePowerAndDuration(app.getBatteryStatsUid(), powerModel, consumedEnergyUC,
                 activityCounter, query.shouldForceUsePowerProfileModel(), powerAndDuration);
 
         app.setUsageDurationMillis(
@@ -163,7 +163,7 @@
     /** Returns bluetooth power usage based on the best data available. */
     private void calculatePowerAndDuration(@Nullable BatteryStats.Uid uid,
             @BatteryConsumer.PowerModel int powerModel,
-            long measuredChargeUC, ControllerActivityCounter counter, boolean ignoreReportedPower,
+            long consumedEnergyUC, ControllerActivityCounter counter, boolean ignoreReportedPower,
             PowerAndDuration powerAndDuration) {
         if (counter == null) {
             powerAndDuration.durationMs = 0;
@@ -183,8 +183,8 @@
 
         powerAndDuration.durationMs = idleTimeMs + rxTimeMs + txTimeMs;
 
-        if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
-            powerAndDuration.powerMah = uCtoMah(measuredChargeUC);
+        if (powerModel == BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) {
+            powerAndDuration.powerMah = uCtoMah(consumedEnergyUC);
             if (uid != null && powerAndDuration.keys != null) {
                 for (int i = 0; i < powerAndDuration.keys.length; i++) {
                     BatteryConsumer.Key key = powerAndDuration.keys[i];
@@ -195,7 +195,7 @@
                     }
 
                     powerAndDuration.powerPerKeyMah[i] =
-                            uCtoMah(uid.getBluetoothMeasuredBatteryConsumptionUC(processState));
+                            uCtoMah(uid.getBluetoothEnergyConsumptionUC(processState));
                 }
             }
         } else {
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerCalculator.java b/services/core/java/com/android/server/power/stats/CpuPowerCalculator.java
index 8b38a35..5074838 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerCalculator.java
@@ -124,7 +124,7 @@
             }
         }
 
-        final long consumptionUC = batteryStats.getCpuMeasuredBatteryConsumptionUC();
+        final long consumptionUC = batteryStats.getCpuEnergyConsumptionUC();
         final int powerModel = getPowerModel(consumptionUC, query);
 
         builder.getAggregateBatteryConsumerBuilder(
@@ -133,13 +133,13 @@
         builder.getAggregateBatteryConsumerBuilder(
                 BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE)
                 .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU,
-                        powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY
+                        powerModel == BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION
                                 ? uCtoMah(consumptionUC) : totalPowerMah, powerModel);
     }
 
     private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
             BatteryUsageStatsQuery query, Result result, BatteryConsumer.Key[] keys) {
-        final long consumptionUC = u.getCpuMeasuredBatteryConsumptionUC();
+        final long consumptionUC = u.getCpuEnergyConsumptionUC();
         final int powerModel = getPowerModel(consumptionUC, query);
         calculatePowerAndDuration(u, powerModel, consumptionUC, BatteryStats.STATS_SINCE_CHARGED,
                 result);
@@ -150,8 +150,8 @@
 
         if (query.isProcessStateDataNeeded() && keys != null) {
             switch (powerModel) {
-                case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY:
-                    calculateMeasuredPowerPerProcessState(app, u, keys);
+                case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION:
+                    calculateEnergyConsumptionPerProcessState(app, u, keys);
                     break;
                 case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
                     calculateModeledPowerPerProcessState(app, u, keys, result);
@@ -160,7 +160,7 @@
         }
     }
 
-    private void calculateMeasuredPowerPerProcessState(UidBatteryConsumer.Builder app,
+    private void calculateEnergyConsumptionPerProcessState(UidBatteryConsumer.Builder app,
             BatteryStats.Uid u, BatteryConsumer.Key[] keys) {
         for (BatteryConsumer.Key key : keys) {
             // The key for PROCESS_STATE_UNSPECIFIED aka PROCESS_STATE_ANY has already been
@@ -171,10 +171,10 @@
                 continue;
             }
 
-            final long consumptionUC = u.getCpuMeasuredBatteryConsumptionUC(key.processState);
+            final long consumptionUC = u.getCpuEnergyConsumptionUC(key.processState);
             if (consumptionUC != 0) {
                 app.setConsumedPower(key, uCtoMah(consumptionUC),
-                        BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                        BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
             }
         }
     }
@@ -226,7 +226,7 @@
 
         final double powerMah;
         switch (powerModel) {
-            case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY:
+            case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION:
                 powerMah = uCtoMah(consumptionUC);
                 break;
             case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
diff --git a/services/core/java/com/android/server/power/stats/CustomMeasuredPowerCalculator.java b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerCalculator.java
similarity index 69%
rename from services/core/java/com/android/server/power/stats/CustomMeasuredPowerCalculator.java
rename to services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerCalculator.java
index a0f9d61..5b7467e 100644
--- a/services/core/java/com/android/server/power/stats/CustomMeasuredPowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/CustomEnergyConsumerPowerCalculator.java
@@ -32,10 +32,10 @@
  * Calculates the amount of power consumed by custom energy consumers (i.e. consumers of type
  * {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
  */
-public class CustomMeasuredPowerCalculator extends PowerCalculator {
-    private static final String TAG = "CustomMeasuredPowerCalc";
+public class CustomEnergyConsumerPowerCalculator extends PowerCalculator {
+    private static final String TAG = "CustomEnergyCsmrPowerCalc";
 
-    public CustomMeasuredPowerCalculator(PowerProfile powerProfile) {
+    public CustomEnergyConsumerPowerCalculator(PowerProfile powerProfile) {
     }
 
     @Override
@@ -55,16 +55,16 @@
             totalAppPowerMah = calculateApp(app, app.getBatteryStatsUid(), totalAppPowerMah);
         }
 
-        final double[] customMeasuredPowerMah = calculateMeasuredEnergiesMah(
-                batteryStats.getCustomConsumerMeasuredBatteryConsumptionUC());
-        if (customMeasuredPowerMah != null) {
+        final double[] customEnergyConsumerPowerMah = uCtoMah(
+                batteryStats.getCustomEnergyConsumerBatteryConsumptionUC());
+        if (customEnergyConsumerPowerMah != null) {
             final AggregateBatteryConsumer.Builder deviceBatteryConsumerBuilder =
                     builder.getAggregateBatteryConsumerBuilder(
                             BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
-            for (int i = 0; i < customMeasuredPowerMah.length; i++) {
+            for (int i = 0; i < customEnergyConsumerPowerMah.length; i++) {
                 deviceBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i,
-                        customMeasuredPowerMah[i]);
+                        customEnergyConsumerPowerMah[i]);
             }
         }
         if (totalAppPowerMah != null) {
@@ -82,38 +82,39 @@
     private double[] calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
             double[] totalPowerMah) {
         double[] newTotalPowerMah = null;
-        final double[] customMeasuredPowerMah = calculateMeasuredEnergiesMah(
-                u.getCustomConsumerMeasuredBatteryConsumptionUC());
-        if (customMeasuredPowerMah != null) {
+        final double[] customEnergyConsumerPowerMah =
+                uCtoMah(u.getCustomEnergyConsumerBatteryConsumptionUC());
+        if (customEnergyConsumerPowerMah != null) {
             if (totalPowerMah == null) {
-                newTotalPowerMah = new double[customMeasuredPowerMah.length];
-            } else if (totalPowerMah.length != customMeasuredPowerMah.length) {
+                newTotalPowerMah = new double[customEnergyConsumerPowerMah.length];
+            } else if (totalPowerMah.length != customEnergyConsumerPowerMah.length) {
                 Slog.wtf(TAG, "Number of custom energy components is not the same for all apps: "
-                        + totalPowerMah.length + ", " + customMeasuredPowerMah.length);
-                newTotalPowerMah = Arrays.copyOf(totalPowerMah, customMeasuredPowerMah.length);
+                        + totalPowerMah.length + ", " + customEnergyConsumerPowerMah.length);
+                newTotalPowerMah = Arrays.copyOf(totalPowerMah,
+                        customEnergyConsumerPowerMah.length);
             } else {
                 newTotalPowerMah = totalPowerMah;
             }
-            for (int i = 0; i < customMeasuredPowerMah.length; i++) {
+            for (int i = 0; i < customEnergyConsumerPowerMah.length; i++) {
                 app.setConsumedPowerForCustomComponent(
                         BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID + i,
-                        customMeasuredPowerMah[i]);
+                        customEnergyConsumerPowerMah[i]);
                 if (!app.isVirtualUid()) {
-                    newTotalPowerMah[i] += customMeasuredPowerMah[i];
+                    newTotalPowerMah[i] += customEnergyConsumerPowerMah[i];
                 }
             }
         }
         return newTotalPowerMah;
     }
 
-    private double[] calculateMeasuredEnergiesMah(long[] measuredChargeUC) {
-        if (measuredChargeUC == null) {
+    private double[] uCtoMah(long[] chargeUC) {
+        if (chargeUC == null) {
             return null;
         }
-        final double[] measuredEnergiesMah = new double[measuredChargeUC.length];
-        for (int i = 0; i < measuredChargeUC.length; i++) {
-            measuredEnergiesMah[i] = uCtoMah(measuredChargeUC[i]);
+        final double[] mah = new double[chargeUC.length];
+        for (int i = 0; i < chargeUC.length; i++) {
+            mah[i] = uCtoMah(chargeUC[i]);
         }
-        return measuredEnergiesMah;
+        return mah;
     }
 }
diff --git a/services/core/java/com/android/server/power/stats/MeasuredEnergySnapshot.java b/services/core/java/com/android/server/power/stats/EnergyConsumerSnapshot.java
similarity index 89%
rename from services/core/java/com/android/server/power/stats/MeasuredEnergySnapshot.java
rename to services/core/java/com/android/server/power/stats/EnergyConsumerSnapshot.java
index c8b4e36..18595ca 100644
--- a/services/core/java/com/android/server/power/stats/MeasuredEnergySnapshot.java
+++ b/services/core/java/com/android/server/power/stats/EnergyConsumerSnapshot.java
@@ -23,7 +23,7 @@
 import android.hardware.power.stats.EnergyConsumerAttribution;
 import android.hardware.power.stats.EnergyConsumerResult;
 import android.hardware.power.stats.EnergyConsumerType;
-import android.os.BatteryStats.MeasuredEnergyDetails;
+import android.os.BatteryStats.EnergyConsumerDetails;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -34,8 +34,8 @@
 /**
  * Keeps snapshots of data from previously pulled EnergyConsumerResults.
  */
-public class MeasuredEnergySnapshot {
-    private static final String TAG = "MeasuredEnergySnapshot";
+public class EnergyConsumerSnapshot {
+    private static final String TAG = "EnergyConsumerSnapshot";
 
     private static final int MILLIVOLTS_PER_VOLT = 1000;
 
@@ -63,13 +63,13 @@
      *
      * If an id is not present yet, it is treated as uninitialized (energy {@link #UNAVAILABLE}).
      */
-    private final SparseLongArray mMeasuredEnergySnapshots;
+    private final SparseLongArray mEnergyConsumerSnapshots;
 
     /**
      * Voltage snapshots, mapping {@link EnergyConsumer#id} to voltage (mV) from the last time
      * each {@link EnergyConsumer} was updated.
      *
-     * see {@link mMeasuredEnergySnapshots}.
+     * see {@link #mEnergyConsumerSnapshots}.
      */
     private final SparseIntArray mVoltageSnapshots;
 
@@ -85,15 +85,15 @@
      */
     private final SparseArray<SparseLongArray> mAttributionSnapshots;
 
-    private MeasuredEnergyDetails mMeasuredEnergyDetails;
+    private EnergyConsumerDetails mEnergyConsumerDetails;
 
     /**
      * Constructor that initializes to the given id->EnergyConsumer map, indicating which consumers
      * exist and what their details are.
      */
-    MeasuredEnergySnapshot(@NonNull SparseArray<EnergyConsumer> idToConsumerMap) {
+    EnergyConsumerSnapshot(@NonNull SparseArray<EnergyConsumer> idToConsumerMap) {
         mEnergyConsumers = idToConsumerMap;
-        mMeasuredEnergySnapshots = new SparseLongArray(mEnergyConsumers.size());
+        mEnergyConsumerSnapshots = new SparseLongArray(mEnergyConsumers.size());
         mVoltageSnapshots = new SparseIntArray(mEnergyConsumers.size());
 
         mNumCpuClusterOrdinals = calculateNumOrdinals(EnergyConsumerType.CPU_CLUSTER,
@@ -103,8 +103,8 @@
         mAttributionSnapshots = new SparseArray<>(mNumOtherOrdinals);
     }
 
-    /** Class for returning the relevant data calculated from the measured energy delta */
-    static class MeasuredEnergyDeltaData {
+    /** Class for returning the relevant data calculated from the energy consumer delta */
+    static class EnergyConsumerDeltaData {
         /** The chargeUC for {@link EnergyConsumerType#BLUETOOTH}. */
         public long bluetoothChargeUC = UNAVAILABLE;
 
@@ -153,14 +153,14 @@
     }
 
     /**
-     * Update with the some freshly measured energies and return the difference (delta)
+     * Update with the freshly retrieved energy consumers and return the difference (delta)
      * between the previously stored values and the passed-in values.
      *
      * @param ecrs EnergyConsumerResults for some (possibly not all) {@link EnergyConsumer}s.
      *             Consumers that are not present are ignored (they are *not* treated as 0).
      * @param voltageMV current voltage.
      *
-     * @return a MeasuredEnergyDeltaData, containing maps from the updated consumers to
+     * @return an EnergyConsumerDeltaData, containing maps from the updated consumers to
      *         their corresponding charge deltas.
      *         Fields with no interesting data (consumers not present in ecrs or with no energy
      *         difference) will generally be left as their default values.
@@ -168,19 +168,19 @@
      *         length {@link #getOtherOrdinalNames().length}.
      *         Returns null, if ecrs is null or empty.
      */
-    public @Nullable MeasuredEnergyDeltaData updateAndGetDelta(EnergyConsumerResult[] ecrs,
-            int voltageMV) {
+    @Nullable
+    public EnergyConsumerDeltaData updateAndGetDelta(EnergyConsumerResult[] ecrs, int voltageMV) {
         if (ecrs == null || ecrs.length == 0) {
             return null;
         }
         if (voltageMV <= 0) {
             Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMV
-                    + " mV) when taking measured energy snapshot");
+                    + " mV) when taking energy consumer snapshot");
             // TODO (b/181685156): consider adding the nominal voltage to power profile and
             //  falling back to it if measured voltage is unavailable.
             return null;
         }
-        final MeasuredEnergyDeltaData output = new MeasuredEnergyDeltaData();
+        final EnergyConsumerDeltaData output = new EnergyConsumerDeltaData();
 
         for (final EnergyConsumerResult ecr : ecrs) {
             // Extract the new energy data for the current consumer.
@@ -198,9 +198,9 @@
             final int ordinal = consumer.ordinal;
 
             // Look up, and update, the old energy and voltage information about this consumer.
-            final long oldEnergyUJ = mMeasuredEnergySnapshots.get(consumerId, UNAVAILABLE);
+            final long oldEnergyUJ = mEnergyConsumerSnapshots.get(consumerId, UNAVAILABLE);
             final int oldVoltageMV = mVoltageSnapshots.get(consumerId);
-            mMeasuredEnergySnapshots.put(consumerId, newEnergyUJ);
+            mEnergyConsumerSnapshots.put(consumerId, newEnergyUJ);
             mVoltageSnapshots.put(consumerId, voltageMV);
 
             final int avgVoltageMV = (oldVoltageMV + voltageMV + 1) / 2;
@@ -275,8 +275,8 @@
 
     /**
      * For a consumer of type {@link EnergyConsumerType#OTHER}, updates
-     * {@link #mAttributionSnapshots} with freshly measured energies (per uid) and returns the
-     * charge consumed (in microcoulombs) between the previously stored values and the passed-in
+     * {@link #mAttributionSnapshots} with freshly retrieved energy consumers (per uid) and returns
+     * the charge consumed (in microcoulombs) between the previously stored values and the passed-in
      * values.
      *
      * @param consumerInfo a consumer of type {@link EnergyConsumerType#OTHER}.
@@ -284,7 +284,7 @@
      *                        Any uid not present is treated as having energy 0.
      *                        If null or empty, all uids are treated as having energy 0.
      * @param avgVoltageMV The average voltage since the last snapshot.
-     * @return A map (in the sense of {@link MeasuredEnergyDeltaData#otherUidChargesUC} for this
+     * @return A map (in the sense of {@link EnergyConsumerDeltaData#otherUidChargesUC} for this
      *         consumer) of uid -> chargeDelta, with all uids that have a non-zero chargeDelta.
      *         Returns null if no delta available to calculate.
      */
@@ -342,7 +342,7 @@
 
     /** Dump debug data. */
     public void dump(PrintWriter pw) {
-        pw.println("Measured energy snapshot");
+        pw.println("Energy consumer snapshot");
         pw.println("List of EnergyConsumers:");
         for (int i = 0; i < mEnergyConsumers.size(); i++) {
             final int id = mEnergyConsumers.keyAt(i);
@@ -351,9 +351,9 @@
                     consumer.id, consumer.ordinal, consumer.type, consumer.name));
         }
         pw.println("Map of consumerIds to energy (in microjoules):");
-        for (int i = 0; i < mMeasuredEnergySnapshots.size(); i++) {
-            final int id = mMeasuredEnergySnapshots.keyAt(i);
-            final long energyUJ = mMeasuredEnergySnapshots.valueAt(i);
+        for (int i = 0; i < mEnergyConsumerSnapshots.size(); i++) {
+            final int id = mEnergyConsumerSnapshots.keyAt(i);
+            final long energyUJ = mEnergyConsumerSnapshots.valueAt(i);
             final long voltageMV = mVoltageSnapshots.valueAt(i);
             pw.println(String.format("    Consumer %d has energy %d uJ at %d mV", id, energyUJ,
                     voltageMV));
@@ -418,19 +418,19 @@
     }
 
     /**
-     * Converts the MeasuredEnergyDeltaData object to MeasuredEnergyDetails, which can
+     * Converts the EnergyConsumerDeltaData object to EnergyConsumerDetails, which can
      * be saved in battery history.
      */
-    MeasuredEnergyDetails getMeasuredEnergyDetails(
-            MeasuredEnergySnapshot.MeasuredEnergyDeltaData delta) {
-        if (mMeasuredEnergyDetails == null) {
-            mMeasuredEnergyDetails = createMeasuredEnergyDetails();
+    EnergyConsumerDetails getEnergyConsumerDetails(
+            EnergyConsumerDeltaData delta) {
+        if (mEnergyConsumerDetails == null) {
+            mEnergyConsumerDetails = createEnergyConsumerDetails();
         }
 
-        final long[] chargeUC = mMeasuredEnergyDetails.chargeUC;
-        for (int i = 0; i < mMeasuredEnergyDetails.consumers.length; i++) {
-            MeasuredEnergyDetails.EnergyConsumer energyConsumer =
-                    mMeasuredEnergyDetails.consumers[i];
+        final long[] chargeUC = mEnergyConsumerDetails.chargeUC;
+        for (int i = 0; i < mEnergyConsumerDetails.consumers.length; i++) {
+            EnergyConsumerDetails.EnergyConsumer energyConsumer =
+                    mEnergyConsumerDetails.consumers[i];
             switch (energyConsumer.type) {
                 case EnergyConsumerType.BLUETOOTH:
                     chargeUC[i] = delta.bluetoothChargeUC;
@@ -470,17 +470,17 @@
                     break;
             }
         }
-        return mMeasuredEnergyDetails;
+        return mEnergyConsumerDetails;
     }
 
-    private MeasuredEnergyDetails createMeasuredEnergyDetails() {
-        MeasuredEnergyDetails details = new MeasuredEnergyDetails();
+    private EnergyConsumerDetails createEnergyConsumerDetails() {
+        EnergyConsumerDetails details = new EnergyConsumerDetails();
         details.consumers =
-                new MeasuredEnergyDetails.EnergyConsumer[mEnergyConsumers.size()];
+                new EnergyConsumerDetails.EnergyConsumer[mEnergyConsumers.size()];
         for (int i = 0; i < mEnergyConsumers.size(); i++) {
             EnergyConsumer energyConsumer = mEnergyConsumers.valueAt(i);
-            MeasuredEnergyDetails.EnergyConsumer consumer =
-                    new MeasuredEnergyDetails.EnergyConsumer();
+            EnergyConsumerDetails.EnergyConsumer consumer =
+                    new EnergyConsumerDetails.EnergyConsumer();
             consumer.type = energyConsumer.type;
             consumer.ordinal = energyConsumer.ordinal;
             switch (consumer.type) {
diff --git a/services/core/java/com/android/server/power/stats/GnssPowerCalculator.java b/services/core/java/com/android/server/power/stats/GnssPowerCalculator.java
index f9356ab..ab22e3e 100644
--- a/services/core/java/com/android/server/power/stats/GnssPowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/GnssPowerCalculator.java
@@ -58,7 +58,7 @@
         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
             final long consumptionUC =
-                    app.getBatteryStatsUid().getGnssMeasuredBatteryConsumptionUC();
+                    app.getBatteryStatsUid().getGnssEnergyConsumptionUC();
             final int powerModel = getPowerModel(consumptionUC, query);
             final double powerMah = calculateApp(app, app.getBatteryStatsUid(), powerModel,
                     rawRealtimeUs, averageGnssPowerMa, consumptionUC);
@@ -67,10 +67,10 @@
             }
         }
 
-        final long consumptionUC = batteryStats.getGnssMeasuredBatteryConsumptionUC();
+        final long consumptionUC = batteryStats.getGnssEnergyConsumptionUC();
         final int powerModel = getPowerModel(consumptionUC, query);
         double powerMah;
-        if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
+        if (powerModel == BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) {
             powerMah = uCtoMah(consumptionUC);
         } else {
             powerMah = appsPowerMah;
@@ -85,12 +85,12 @@
 
     private double calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
             @BatteryConsumer.PowerModel int powerModel, long rawRealtimeUs,
-            double averageGnssPowerMa, long measuredChargeUC) {
+            double averageGnssPowerMa, long consumedEnergyUC) {
         final long durationMs = computeDuration(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
         final double powerMah;
         switch (powerModel) {
-            case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY:
-                powerMah = uCtoMah(measuredChargeUC);
+            case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION:
+                powerMah = uCtoMah(consumedEnergyUC);
                 break;
             case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
             default:
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java
index 2c7aea9..4608e9a 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerCalculator.java
@@ -128,20 +128,20 @@
 
         PowerAndDuration total = new PowerAndDuration();
 
-        final long totalConsumptionUC = batteryStats.getMobileRadioMeasuredBatteryConsumptionUC();
+        final long totalConsumptionUC = batteryStats.getMobileRadioEnergyConsumptionUC();
         final int powerModel = getPowerModel(totalConsumptionUC, query);
 
         final double totalActivePowerMah;
         final ArrayList<UidBatteryConsumer.Builder> apps;
         final LongArrayQueue appDurationsMs;
-        if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
-            // Measured energy is available, don't bother calculating power.
+        if (powerModel == BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) {
+            // EnergyConsumer is available, don't bother calculating power.
             totalActivePowerMah = Double.NaN;
             apps = null;
             appDurationsMs = null;
         } else {
             totalActivePowerMah = calculateActiveModemPowerMah(batteryStats, rawRealtimeUs);
-            apps = new ArrayList();
+            apps = new ArrayList<>();
             appDurationsMs = new LongArrayQueue();
         }
 
@@ -169,9 +169,9 @@
             app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
                     radioActiveDurationMs);
 
-            if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
-                // Measured energy is available, populate the consumed power now.
-                final long appConsumptionUC = uid.getMobileRadioMeasuredBatteryConsumptionUC();
+            if (powerModel == BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) {
+                // EnergyConsumer is available, populate the consumed power now.
+                final long appConsumptionUC = uid.getMobileRadioEnergyConsumptionUC();
                 if (appConsumptionUC != BatteryStats.POWER_DATA_UNAVAILABLE) {
                     final double appConsumptionMah = uCtoMah(appConsumptionUC);
                     if (!app.isVirtualUid()) {
@@ -188,7 +188,7 @@
                                 continue;
                             }
                             final long consumptionInStateUc =
-                                    uid.getMobileRadioMeasuredBatteryConsumptionUC(processState);
+                                    uid.getMobileRadioEnergyConsumptionUC(processState);
                             final double powerInStateMah = uCtoMah(consumptionInStateUc);
                             app.setConsumedPower(key, powerInStateMah, powerModel);
                         }
@@ -207,7 +207,7 @@
             totalActiveDurationMs = total.totalAppDurationMs;
         }
 
-        if (powerModel != BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
+        if (powerModel != BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) {
             // Need to smear the calculated total active power across the apps based on app
             // active durations.
             final int appSize = apps.size();
@@ -259,7 +259,7 @@
         total.remainingDurationMs = totalActiveDurationMs - total.totalAppDurationMs;
 
         // Calculate remaining power consumption.
-        if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
+        if (powerModel == BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) {
             total.remainingPowerMah = uCtoMah(totalConsumptionUC) - total.totalAppPowerMah;
             if (total.remainingPowerMah < 0) total.remainingPowerMah = 0;
         } else {
diff --git a/services/core/java/com/android/server/power/stats/PowerCalculator.java b/services/core/java/com/android/server/power/stats/PowerCalculator.java
index 1652f73..fe68d8f 100644
--- a/services/core/java/com/android/server/power/stats/PowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/PowerCalculator.java
@@ -79,17 +79,17 @@
     }
 
     protected static @BatteryConsumer.PowerModel int getPowerModel(
-            long measuredEnergyUC, @NonNull BatteryUsageStatsQuery query) {
-        if (measuredEnergyUC != BatteryStats.POWER_DATA_UNAVAILABLE
+            long consumedEnergyUC, @NonNull BatteryUsageStatsQuery query) {
+        if (consumedEnergyUC != BatteryStats.POWER_DATA_UNAVAILABLE
                 && !query.shouldForceUsePowerProfileModel()) {
-            return BatteryConsumer.POWER_MODEL_MEASURED_ENERGY;
+            return BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION;
         }
         return BatteryConsumer.POWER_MODEL_POWER_PROFILE;
     }
 
-    protected static @BatteryConsumer.PowerModel int getPowerModel(long measuredEnergyUC) {
-        return measuredEnergyUC != BatteryStats.POWER_DATA_UNAVAILABLE
-                ? BatteryConsumer.POWER_MODEL_MEASURED_ENERGY
+    protected static @BatteryConsumer.PowerModel int getPowerModel(long consumedEnergyUC) {
+        return consumedEnergyUC != BatteryStats.POWER_DATA_UNAVAILABLE
+                ? BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION
                 : BatteryConsumer.POWER_MODEL_POWER_PROFILE;
     }
 
diff --git a/services/core/java/com/android/server/power/stats/ScreenPowerCalculator.java b/services/core/java/com/android/server/power/stats/ScreenPowerCalculator.java
index ddcbb04..25dc9b1 100644
--- a/services/core/java/com/android/server/power/stats/ScreenPowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/ScreenPowerCalculator.java
@@ -73,7 +73,7 @@
             long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
         final PowerAndDuration totalPowerAndDuration = new PowerAndDuration();
 
-        final long consumptionUC = batteryStats.getScreenOnMeasuredBatteryConsumptionUC();
+        final long consumptionUC = batteryStats.getScreenOnEnergyConsumptionUC();
         final int powerModel = getPowerModel(consumptionUC, query);
         calculateTotalDurationAndPower(totalPowerAndDuration, powerModel, batteryStats,
                 rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED, consumptionUC);
@@ -87,12 +87,12 @@
         final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
                 builder.getUidBatteryConsumerBuilders();
         switch (powerModel) {
-            case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY:
+            case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION:
                 final PowerAndDuration appPowerAndDuration = new PowerAndDuration();
                 for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
                     final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
-                    calculateAppUsingMeasuredEnergy(appPowerAndDuration, app.getBatteryStatsUid(),
-                            rawRealtimeUs);
+                    calculateAppUsingEnergyConsumption(appPowerAndDuration,
+                            app.getBatteryStatsUid(), rawRealtimeUs);
                     app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN,
                                     appPowerAndDuration.durationMs)
                             .setConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN,
@@ -134,7 +134,7 @@
                 statsType);
 
         switch (powerModel) {
-            case BatteryConsumer.POWER_MODEL_MEASURED_ENERGY:
+            case BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION:
                 totalPowerAndDuration.powerMah = uCtoMah(consumptionUC);
                 break;
             case BatteryConsumer.POWER_MODEL_POWER_PROFILE:
@@ -144,11 +144,11 @@
         }
     }
 
-    private void calculateAppUsingMeasuredEnergy(PowerAndDuration appPowerAndDuration,
+    private void calculateAppUsingEnergyConsumption(PowerAndDuration appPowerAndDuration,
             BatteryStats.Uid u, long rawRealtimeUs) {
         appPowerAndDuration.durationMs = getProcessForegroundTimeMs(u, rawRealtimeUs);
 
-        final long chargeUC = u.getScreenOnMeasuredBatteryConsumptionUC();
+        final long chargeUC = u.getScreenOnEnergyConsumptionUC();
         if (chargeUC < 0) {
             Slog.wtf(TAG, "Screen energy not supported, so calculateApp shouldn't de called");
             appPowerAndDuration.powerMah = 0;
diff --git a/services/core/java/com/android/server/power/stats/SystemServicePowerCalculator.java b/services/core/java/com/android/server/power/stats/SystemServicePowerCalculator.java
index cd4b586..aa17d4fb 100644
--- a/services/core/java/com/android/server/power/stats/SystemServicePowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/SystemServicePowerCalculator.java
@@ -73,12 +73,12 @@
             return;
         }
 
-        final long consumptionUC = systemUid.getCpuMeasuredBatteryConsumptionUC();
+        final long consumptionUC = systemUid.getCpuEnergyConsumptionUC();
         final int powerModel = getPowerModel(consumptionUC, query);
 
         double systemServicePowerMah;
-        if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
-            systemServicePowerMah = calculatePowerUsingMeasuredConsumption(batteryStats,
+        if (powerModel == BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) {
+            systemServicePowerMah = calculatePowerUsingEnergyConsumption(batteryStats,
                     systemUid, consumptionUC);
         } else {
             systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats);
@@ -120,11 +120,11 @@
                         systemServicePowerMah);
     }
 
-    private double calculatePowerUsingMeasuredConsumption(BatteryStats batteryStats,
+    private double calculatePowerUsingEnergyConsumption(BatteryStats batteryStats,
             BatteryStats.Uid systemUid, long consumptionUC) {
         // Use the PowerProfile based model to estimate the ratio between the power consumed
         // while handling incoming binder calls and the entire System UID power consumption.
-        // Apply that ratio to the _measured_ system UID power consumption to get a more
+        // Apply that ratio to the _EnergyConsumer_ system UID power consumption to get a more
         // accurate estimate of the power consumed by incoming binder calls.
         final double systemServiceModeledPowerMah = calculatePowerUsingPowerProfile(batteryStats);
         final double systemUidModeledPowerMah = mCpuPowerCalculator.calculateUidModeledPowerMah(
diff --git a/services/core/java/com/android/server/power/stats/WifiPowerCalculator.java b/services/core/java/com/android/server/power/stats/WifiPowerCalculator.java
index 4f5c8a5..4d055b4 100644
--- a/services/core/java/com/android/server/power/stats/WifiPowerCalculator.java
+++ b/services/core/java/com/android/server/power/stats/WifiPowerCalculator.java
@@ -107,7 +107,7 @@
             }
 
             final long consumptionUC =
-                    app.getBatteryStatsUid().getWifiMeasuredBatteryConsumptionUC();
+                    app.getBatteryStatsUid().getWifiEnergyConsumptionUC();
             final int powerModel = getPowerModel(consumptionUC, query);
 
             calculateApp(powerDurationAndTraffic, app.getBatteryStatsUid(), powerModel,
@@ -138,7 +138,7 @@
             }
         }
 
-        final long consumptionUC = batteryStats.getWifiMeasuredBatteryConsumptionUC();
+        final long consumptionUC = batteryStats.getWifiEnergyConsumptionUC();
         final int powerModel = getPowerModel(consumptionUC, query);
         calculateRemaining(powerDurationAndTraffic, powerModel, batteryStats, rawRealtimeUs,
                 BatteryStats.STATS_SINCE_CHARGED, batteryStats.hasWifiActivityReporting(),
@@ -214,7 +214,7 @@
                                             idleTimeCounter.getCountForProcessState(processState));
                         } else {
                             powerDurationAndTraffic.powerPerKeyMah[i] =
-                                    uCtoMah(u.getWifiMeasuredBatteryConsumptionUC(processState));
+                                    uCtoMah(u.getWifiEnergyConsumptionUC(processState));
                         }
                     }
                 }
@@ -264,7 +264,7 @@
         long totalDurationMs;
         double totalPowerMah = 0;
 
-        if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
+        if (powerModel == BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) {
             totalPowerMah = uCtoMah(consumptionUC);
         }
 
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index b95d372..863ee75 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -44,6 +44,7 @@
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.media.PlaybackParams;
+import android.media.tv.AdBuffer;
 import android.media.tv.AdRequest;
 import android.media.tv.AdResponse;
 import android.media.tv.AitInfo;
@@ -2520,7 +2521,30 @@
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
-            };
+            }
+        }
+
+        @Override
+        public void notifyAdBuffer(
+                IBinder sessionToken, AdBuffer buffer, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid,
+                    userId, "notifyAdBuffer");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifyAdBuffer(buffer);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slog.e(TAG, "error in notifyAdBuffer", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
 
         @Override
@@ -3624,7 +3648,7 @@
         }
 
         @Override
-        public void onBroadcastInfoResponse (BroadcastInfoResponse response) {
+        public void onBroadcastInfoResponse(BroadcastInfoResponse response) {
             synchronized (mLock) {
                 if (DEBUG) {
                     Slog.d(TAG, "onBroadcastInfoResponse()");
@@ -3641,7 +3665,7 @@
         }
 
         @Override
-        public void onAdResponse (AdResponse response) {
+        public void onAdResponse(AdResponse response) {
             synchronized (mLock) {
                 if (DEBUG) {
                     Slog.d(TAG, "onAdResponse()");
@@ -3656,6 +3680,24 @@
                 }
             }
         }
+
+        @Override
+        public void onAdBufferConsumed(AdBuffer buffer) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slog.d(TAG, "onAdBufferConsumed()");
+                }
+                if (mSessionState.session == null || mSessionState.client == null) {
+                    return;
+                }
+                try {
+                    mSessionState.client.onAdBufferConsumed(
+                            buffer, mSessionState.seq);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "error in onAdBufferConsumed", e);
+                }
+            }
+        }
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index 2c8fd96..f173f7a 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -29,6 +29,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.graphics.Rect;
+import android.media.tv.AdBuffer;
 import android.media.tv.AdRequest;
 import android.media.tv.AdResponse;
 import android.media.tv.BroadcastInfoRequest;
@@ -1513,6 +1514,29 @@
         }
 
         @Override
+        public void notifyAdBufferConsumed(
+                IBinder sessionToken, AdBuffer buffer, int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final int callingPid = Binder.getCallingPid();
+            final int resolvedUserId = resolveCallingUserId(callingPid, callingUid, userId,
+                    "notifyAdBufferConsumed");
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    try {
+                        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid,
+                                resolvedUserId);
+                        getSessionLocked(sessionState).notifyAdBufferConsumed(buffer);
+                    } catch (RemoteException | SessionNotFoundException e) {
+                        Slogf.e(TAG, "error in notifyAdBufferConsumed", e);
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public void registerCallback(final ITvInteractiveAppManagerCallback callback, int userId) {
             int callingPid = Binder.getCallingPid();
             int callingUid = Binder.getCallingUid();
@@ -2378,6 +2402,23 @@
             }
         }
 
+        @Override
+        public void onAdBuffer(AdBuffer buffer) {
+            synchronized (mLock) {
+                if (DEBUG) {
+                    Slogf.d(TAG, "onAdBuffer(buffer=" + buffer + ")");
+                }
+                if (mSessionState.mSession == null || mSessionState.mClient == null) {
+                    return;
+                }
+                try {
+                    mSessionState.mClient.onAdBuffer(buffer, mSessionState.mSeq);
+                } catch (RemoteException e) {
+                    Slogf.e(TAG, "error in onAdBuffer", e);
+                }
+            }
+        }
+
         @GuardedBy("mLock")
         private boolean addSessionTokenToClientStateLocked(ITvInteractiveAppSession session) {
             try {
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
index 48e6f97..ff1d442 100644
--- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.app.ActivityOptions;
 import android.app.TaskInfo;
 import android.content.Intent;
@@ -31,119 +32,423 @@
 /**
  * Callback to intercept activity starts and possibly block/redirect them. The callback methods will
  * be called with the WindowManagerGlobalLock held.
+ * @hide
  */
-public abstract class ActivityInterceptorCallback {
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public interface ActivityInterceptorCallback {
     /**
-     * Intercept the launch intent based on various signals. If an interception happened, returns
-     * a new/existing non-null {@link ActivityInterceptResult} which may redirect to another
-     * activity or with new {@link ActivityOptions}.
+     * Called to allow intercepting activity launching based on the provided launch parameters and
+     * intent resolution.
      *
-     * @return null if no interception occurred, or a non-null result which replaces the existing
-     * intent and activity options.
+     * <p>If the interceptor decides to change the {@link Intent} or return different {@link
+     * ActivityOptions}, it should return a non-{@code null} {@link ActivityInterceptResult} which
+     * may redirect to another activity or use new {@link ActivityOptions}. Otherwise, the
+     * interceptor should return {@code null} to indicate that passed {@link Intent} should not be
+     * changed.
+     *
+     * @param info the information about the {@link Intent} that is being intercepted to launch an
+     *             {@link android.app.Activity}.
+     * @return {@code null} if the interceptor decides not to change the existing intent, or a non-
+     * {@code null} result which replaces the existing intent and activity options.
      */
-    public abstract @Nullable ActivityInterceptResult intercept(ActivityInterceptorInfo info);
+    @Nullable
+    ActivityInterceptResult onInterceptActivityLaunch(@NonNull ActivityInterceptorInfo info);
 
     /**
-     * Called when an activity is successfully launched. The intent included in the
-     * ActivityInterceptorInfo may have changed from the one sent in
-     * {@link #intercept(ActivityInterceptorInfo)}, due to the return from
-     * {@link #intercept(ActivityInterceptorInfo)}.
+     * Called when an activity is successfully launched.
+     *
+     * <p>The intent included in the ActivityInterceptorInfo may have changed from the one sent in
+     * {@link #onInterceptActivityLaunch(ActivityInterceptorInfo)}, due to the changes might applied
+     * during internception.
+     *
+     * <p>There is no callback in case that the {@link android.app.Activity} is failed to launch,
+     * and this is not necessary to be added for the known use-cases.
+     *
+     * @param taskInfo the information about the @{@link Task} holds the launched
+     *                 {@link android.app.Activity}.
+     * @param activityInfo the information about the launched {@link android.app.Activity}.
+     * @param info the information about the {@link Intent} after calling {@link
+     *             #onInterceptActivityLaunch(ActivityInterceptorInfo)}.
      */
-    public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo,
-            ActivityInterceptorInfo info) {
-    }
+    default void onActivityLaunched(@NonNull TaskInfo taskInfo, @NonNull ActivityInfo activityInfo,
+            @NonNull ActivityInterceptorInfo info) {}
 
     /**
-     * The unique id of each interceptor which determines the order it will execute in.
+     * The unique id of each interceptor registered by a system service which determines the order
+     * it will execute in.
+     * @hide
      */
     @IntDef(suffix = { "_ORDERED_ID" }, value = {
-            FIRST_ORDERED_ID,
+            // Order Ids for system services
+            SYSTEM_FIRST_ORDERED_ID,
             PERMISSION_POLICY_ORDERED_ID,
             VIRTUAL_DEVICE_SERVICE_ORDERED_ID,
             DREAM_MANAGER_ORDERED_ID,
-            LAST_ORDERED_ID // Update this when adding new ids
+            SYSTEM_LAST_ORDERED_ID, // Update this when adding new ids
+            // Order Ids for mainline module services
+            MAINLINE_FIRST_ORDERED_ID,
+            MAINLINE_SDK_SANDBOX_ORDER_ID,
+            MAINLINE_LAST_ORDERED_ID  // Update this when adding new mainline module ids
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface OrderedId {}
+    @interface OrderedId {}
 
     /**
      * The first id, used by the framework to determine the valid range of ids.
+     * @hide
      */
-    static final int FIRST_ORDERED_ID = 0;
+    int SYSTEM_FIRST_ORDERED_ID = 0;
 
     /**
      * The identifier for {@link com.android.server.policy.PermissionPolicyService} interceptor
+     * @hide
      */
-    public static final int PERMISSION_POLICY_ORDERED_ID = 1;
+    int PERMISSION_POLICY_ORDERED_ID = 1;
 
     /**
      * The identifier for {@link com.android.server.companion.virtual.VirtualDeviceManagerService}
      * interceptor.
+     * @hide
      */
-    public static final int VIRTUAL_DEVICE_SERVICE_ORDERED_ID = 3;
+    int VIRTUAL_DEVICE_SERVICE_ORDERED_ID = 3;
 
     /**
      * The identifier for {@link com.android.server.dreams.DreamManagerService} interceptor.
+     * @hide
      */
-    public static final int DREAM_MANAGER_ORDERED_ID = 4;
+    int DREAM_MANAGER_ORDERED_ID = 4;
 
     /**
      * The final id, used by the framework to determine the valid range of ids. Update this when
      * adding new ids.
+     * @hide
      */
-    static final int LAST_ORDERED_ID = DREAM_MANAGER_ORDERED_ID;
+    int SYSTEM_LAST_ORDERED_ID = DREAM_MANAGER_ORDERED_ID;
+
+    /**
+     * The first mainline module id, used by the framework to determine the valid range of ids
+     * could be used by mainline modules.
+     * @hide
+     */
+    int MAINLINE_FIRST_ORDERED_ID = 1000;
+
+    /**
+     * The identifier for {@link com.android.server.sdksandbox.SdkSandboxManagerService.Lifecycle}
+     * interceptor.
+     */
+    int MAINLINE_SDK_SANDBOX_ORDER_ID = 1001;
+
+    /**
+     * The final mainline module id, used by the framework to determine the valid range of ids
+     * could be used by mainline modules. Update this when adding new ids for mainline modules.
+     * @hide
+     */
+    int MAINLINE_LAST_ORDERED_ID = MAINLINE_SDK_SANDBOX_ORDER_ID;
+
+    /**
+     * Returns {@code true} if the id is in the range of valid system services including mainline
+     * module services.
+     * @hide
+     */
+    static boolean isValidOrderId(int id) {
+        return isValidMainlineOrderId(id)
+                || (id >= SYSTEM_FIRST_ORDERED_ID && id <= SYSTEM_LAST_ORDERED_ID);
+    }
+
+    /**
+     * Returns {@code true} if the id is in the range of valid mainline module services.
+     * @hide
+     */
+    static boolean isValidMainlineOrderId(int id) {
+        return id >= MAINLINE_FIRST_ORDERED_ID && id <= MAINLINE_LAST_ORDERED_ID;
+    }
 
     /**
      * Data class for storing the various arguments needed for activity interception.
+     * @hide
      */
-    public static final class ActivityInterceptorInfo {
-        public final int realCallingUid;
-        public final int realCallingPid;
-        public final int userId;
-        public final String callingPackage;
-        public final String callingFeatureId;
-        public final Intent intent;
-        public final ResolveInfo rInfo;
-        public final ActivityInfo aInfo;
-        public final String resolvedType;
-        public final int callingPid;
-        public final int callingUid;
-        public final ActivityOptions checkedOptions;
-        public final @Nullable Runnable clearOptionsAnimation;
+    @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+    final class ActivityInterceptorInfo {
+        private final int mCallingUid;
+        private final int mCallingPid;
+        private final int mRealCallingUid;
+        private final int mRealCallingPid;
+        private final int mUserId;
+        private final Intent mIntent;
+        @NonNull
+        private final ResolveInfo mResolveInfo;
+        @NonNull
+        private final ActivityInfo mActivityInfo;
+        @Nullable
+        private final String mResolvedType;
+        @Nullable
+        private final String mCallingPackage;
+        @Nullable
+        private final String mCallingFeatureId;
+        @Nullable
+        private final ActivityOptions mCheckedOptions;
+        @Nullable
+        private final Runnable mClearOptionsAnimation;
 
-        public ActivityInterceptorInfo(int realCallingUid, int realCallingPid, int userId,
-                String callingPackage, String callingFeatureId, Intent intent,
-                ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType, int callingPid,
-                int callingUid, ActivityOptions checkedOptions,
-                @Nullable Runnable clearOptionsAnimation) {
-            this.realCallingUid = realCallingUid;
-            this.realCallingPid = realCallingPid;
-            this.userId = userId;
-            this.callingPackage = callingPackage;
-            this.callingFeatureId = callingFeatureId;
-            this.intent = intent;
-            this.rInfo = rInfo;
-            this.aInfo = aInfo;
-            this.resolvedType = resolvedType;
-            this.callingPid = callingPid;
-            this.callingUid = callingUid;
-            this.checkedOptions = checkedOptions;
-            this.clearOptionsAnimation = clearOptionsAnimation;
+        /**
+         * @hide
+         */
+        public ActivityInterceptorInfo(Builder builder) {
+            this.mCallingUid = builder.mCallingUid;
+            this.mCallingPid = builder.mCallingPid;
+            this.mRealCallingUid = builder.mRealCallingUid;
+            this.mRealCallingPid = builder.mRealCallingPid;
+            this.mUserId = builder.mUserId;
+            this.mIntent = builder.mIntent;
+            this.mResolveInfo = builder.mResolveInfo;
+            this.mActivityInfo = builder.mActivityInfo;
+            this.mResolvedType = builder.mResolvedType;
+            this.mCallingPackage = builder.mCallingPackage;
+            this.mCallingFeatureId = builder.mCallingFeatureId;
+            this.mCheckedOptions = builder.mCheckedOptions;
+            this.mClearOptionsAnimation = builder.mClearOptionsAnimation;
+        }
+
+        /**
+         * Builder class to build instances of {@link ActivityInterceptorInfo}.
+         */
+        public static final class Builder {
+            private final int mCallingUid;
+            private final int mCallingPid;
+            private final int mRealCallingUid;
+            private final int mRealCallingPid;
+            private final int mUserId;
+            private final Intent mIntent;
+            @NonNull
+            private final ResolveInfo mResolveInfo;
+            @NonNull
+            private final ActivityInfo mActivityInfo;
+            @Nullable
+            private String mResolvedType;
+            @Nullable
+            private String mCallingPackage = null;
+            @Nullable
+            private String mCallingFeatureId = null;
+            @Nullable
+            private ActivityOptions mCheckedOptions = null;
+            @Nullable
+            private Runnable mClearOptionsAnimation = null;
+
+            /**
+             * Constructor of {@link ActivityInterceptorInfo.Builder}.
+             */
+            public Builder(int callingUid, int callingPid, int realCallingUid,
+                    int realCallingPid, int userId, @NonNull Intent intent,
+                    @NonNull ResolveInfo rInfo, @NonNull ActivityInfo aInfo) {
+                this.mCallingUid = callingUid;
+                this.mCallingPid = callingPid;
+                this.mRealCallingUid = realCallingUid;
+                this.mRealCallingPid = realCallingPid;
+                this.mUserId = userId;
+                this.mIntent = intent;
+                this.mResolveInfo = rInfo;
+                this.mActivityInfo = aInfo;
+            }
+
+            /**
+             * Returns a new instance of {@link ActivityInterceptorInfo} based on the {@link
+             * Builder} fields.
+             *
+             * @return a new instance of {@link ActivityInterceptorInfo}.
+             */
+            @NonNull
+            public ActivityInterceptorInfo build() {
+                return new ActivityInterceptorInfo(this);
+            }
+
+            /**
+             * Sets the value for the resolved type.
+             * @param resolvedType the resolved type.
+             */
+            @NonNull
+            public Builder setResolvedType(@NonNull String resolvedType) {
+                mResolvedType = resolvedType;
+                return this;
+            }
+
+            /**
+             * Sets the value for the calling package.
+             * @param callingPackage the calling package.
+             */
+            @NonNull
+            public Builder setCallingPackage(@NonNull String callingPackage) {
+                mCallingPackage = callingPackage;
+                return this;
+            }
+
+            /**
+             * Sets the value for the calling feature id.
+             * @param callingFeatureId the calling feature id.
+             */
+            @NonNull
+            public Builder setCallingFeatureId(@NonNull String callingFeatureId) {
+                mCallingFeatureId = callingFeatureId;
+                return this;
+            }
+
+            /**
+             * Sets the value for the {@link ActivityOptions}.
+             * @param checkedOptions the {@link ActivityOptions}.
+             */
+            @NonNull
+            public Builder setCheckedOptions(@NonNull ActivityOptions checkedOptions) {
+                mCheckedOptions = checkedOptions;
+                return this;
+            }
+
+            /**
+             * Sets the value for the {@link Runnable} object to clear options Animation.
+             * @param clearOptionsAnimationRunnable the calling package.
+             */
+            @NonNull
+            public Builder setClearOptionsAnimationRunnable(@NonNull
+                    Runnable clearOptionsAnimationRunnable) {
+                mClearOptionsAnimation = clearOptionsAnimationRunnable;
+                return this;
+            }
+        }
+
+        /** Returns the calling uid. */
+        public int getCallingUid() {
+            return mCallingUid;
+        }
+
+        /** Returns the calling pid. */
+        public int getCallingPid() {
+            return mCallingPid;
+        }
+
+        /** Returns the real calling uid. */
+        public int getRealCallingUid() {
+            return mRealCallingUid;
+        }
+
+        /** Returns the real calling pid. */
+        public int getRealCallingPid() {
+            return mRealCallingPid;
+        }
+
+        /** Returns the user id. */
+        public int getUserId() {
+            return mUserId;
+        }
+
+        /** Returns the {@link Intent}. */
+        @SuppressWarnings("IntentBuilderName")
+        @NonNull
+        public Intent getIntent() {
+            return mIntent;
+        }
+
+        /** Returns the {@link ResolveInfo}. */
+        @NonNull
+        public ResolveInfo getResolveInfo() {
+            return mResolveInfo;
+        }
+
+        /** Returns the {@link ActivityInfo}. */
+        @NonNull
+        public ActivityInfo getActivityInfo() {
+            return mActivityInfo;
+        }
+
+        /** Returns the real resolved type. */
+        @Nullable
+        public String getResolvedType() {
+            return mResolvedType;
+        }
+
+        /** Returns the calling package. */
+        @Nullable
+        public String getCallingPackage() {
+            return mCallingPackage;
+        }
+
+        /** Returns the calling feature id. */
+        @Nullable
+        public String getCallingFeatureId() {
+            return mCallingFeatureId;
+        }
+
+        /** Returns the {@link ActivityOptions}. */
+        @Nullable
+        public ActivityOptions getCheckedOptions() {
+            return mCheckedOptions;
+        }
+
+        /** Returns the {@link Runnable} object to clear options Animation. */
+        @Nullable
+        public Runnable getClearOptionsAnimationRunnable() {
+            return mClearOptionsAnimation;
         }
     }
 
     /**
      * Data class for storing the intercept result.
+     * @hide
      */
-    public static final class ActivityInterceptResult {
-        @NonNull public final Intent intent;
-        @NonNull public final ActivityOptions activityOptions;
+    @SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+    final class ActivityInterceptResult {
+        @NonNull
+        private final Intent mIntent;
 
-        public ActivityInterceptResult(
-                @NonNull Intent intent,
+        @NonNull
+        private final ActivityOptions mActivityOptions;
+
+        private final boolean mActivityResolved;
+
+        /**
+         * This constructor should only be used if both {@link ActivityInfo} and {@link ResolveInfo}
+         * did not get resolved while interception.
+         * @hide
+         */
+        public ActivityInterceptResult(@NonNull Intent intent,
                 @NonNull ActivityOptions activityOptions) {
-            this.intent = intent;
-            this.activityOptions = activityOptions;
+            this(intent, activityOptions, false /* activityResolved */);
+        }
+
+        /**
+         * Generates the result of intercepting launching the {@link android.app.Activity}
+         *
+         * <p>Interceptor should return non-{@code null} result when {@link
+         * #onInterceptActivityLaunch(ActivityInterceptorInfo)} gets called as an indicator that
+         * interception has happened.
+         *
+         * @param intent is the modified {@link Intent} after interception.
+         * @param activityOptions holds the {@link ActivityOptions} after interception.
+         * @param activityResolved should be {@code true} only if {@link ActivityInfo} or {@link
+         *                         ResolveInfo} gets resolved, otherwise should be {@code false}.
+         */
+        public ActivityInterceptResult(@NonNull Intent intent,
+                @NonNull ActivityOptions activityOptions, boolean activityResolved) {
+            this.mIntent = intent;
+            this.mActivityOptions = activityOptions;
+            this.mActivityResolved = activityResolved;
+        }
+
+        /** Returns the intercepted {@link Intent} */
+        @SuppressWarnings("IntentBuilderName")
+        @NonNull
+        public Intent getIntent() {
+            return mIntent;
+        }
+
+        /** Returns the intercepted {@link ActivityOptions} */
+        @NonNull
+        public ActivityOptions getActivityOptions() {
+            return mActivityOptions;
+        }
+
+        /**
+         * Returns if the {@link ActivityInfo} or {@link ResolveInfo} gets resolved.
+         */
+        public boolean isActivityResolved() {
+            return mActivityResolved;
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallbackRegistry.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallbackRegistry.java
new file mode 100644
index 0000000..bb9462f
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallbackRegistry.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Binder;
+import android.os.Process;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+
+/**
+ * This class should be used by system services which are part of mainline modules to register
+ * {@link ActivityInterceptorCallback}. For other system services, this function should be used
+ * instead {@link ActivityTaskManagerInternal#registerActivityStartInterceptor(
+ * int, ActivityInterceptorCallback)}.
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public class ActivityInterceptorCallbackRegistry {
+
+    private static final ActivityInterceptorCallbackRegistry sInstance =
+            new ActivityInterceptorCallbackRegistry();
+
+    private ActivityInterceptorCallbackRegistry() {}
+
+    /** Returns an already initialised singleton instance of this class. */
+    @NonNull
+    public static ActivityInterceptorCallbackRegistry getInstance() {
+        return sInstance;
+    }
+
+    /**
+     * Registers a callback which can intercept activity launching flow.
+     *
+     * <p>Only system services which are part of mainline modules should call this function.
+     *
+     * <p>To avoid Activity launch delays, the callbacks must execute quickly and avoid acquiring
+     * other system process locks.
+     *
+     * @param mainlineOrderId has to be one of the following [{@link
+     *                        ActivityInterceptorCallback#MAINLINE_FIRST_ORDERED_ID}].
+     * @param callback the {@link ActivityInterceptorCallback} to register.
+     * @throws IllegalArgumentException if duplicate ids are provided, the provided id is not the
+     * mainline module range or the provided {@code callback} is null.
+     */
+    // ExecutorRegistration is suppressed as the callback is called synchronously in the system
+    // server.
+    @SuppressWarnings("ExecutorRegistration")
+    public void registerActivityInterceptorCallback(
+            @ActivityInterceptorCallback.OrderedId int mainlineOrderId,
+            @NonNull ActivityInterceptorCallback callback) {
+        if (getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only system server can register "
+                    + "ActivityInterceptorCallback");
+        }
+        if (!ActivityInterceptorCallback.isValidMainlineOrderId(mainlineOrderId)) {
+            throw new IllegalArgumentException("id is not in the mainline modules range, please use"
+                    + "ActivityTaskManagerInternal.registerActivityStartInterceptor(OrderedId, "
+                    + "ActivityInterceptorCallback) instead.");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("The passed ActivityInterceptorCallback can not be "
+                    + "null");
+        }
+        ActivityTaskManagerInternal activityTaskManagerInternal =
+                LocalServices.getService(ActivityTaskManagerInternal.class);
+        activityTaskManagerInternal.registerActivityStartInterceptor(mainlineOrderId, callback);
+    }
+
+    /**
+     * Unregisters an already registered {@link ActivityInterceptorCallback}.
+     *
+     * @param mainlineOrderId the order id of the {@link ActivityInterceptorCallback} should be
+     *                        unregistered, this callback should be registered before by calling
+     *                        {@link #registerActivityInterceptorCallback(int,
+     *                        ActivityInterceptorCallback)} using the same order id.
+     * @throws IllegalArgumentException if the provided id is not the mainline module range or is
+     * not registered
+     */
+    public void unregisterActivityInterceptorCallback(
+            @ActivityInterceptorCallback.OrderedId int mainlineOrderId) {
+        if (getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only system server can register "
+                    + "ActivityInterceptorCallback");
+        }
+        if (!ActivityInterceptorCallback.isValidMainlineOrderId(mainlineOrderId)) {
+            throw new IllegalArgumentException("id is not in the mainline modules range, please use"
+                    + "ActivityTaskManagerInternal.unregisterActivityStartInterceptor(OrderedId) "
+                    + "instead.");
+        }
+        ActivityTaskManagerInternal activityTaskManagerInternal =
+                LocalServices.getService(ActivityTaskManagerInternal.class);
+        activityTaskManagerInternal.unregisterActivityStartInterceptor(mainlineOrderId);
+    }
+
+    /**
+     * This hidden function is for unit tests as a way to behave like as if they are called from
+     * system server process uid by mocking it and returning {@link Process#SYSTEM_UID}.
+     * Do not make this {@code public} to apps as apps should not have a way to change the uid.
+     * @hide
+     */
+    @VisibleForTesting
+    int getCallingUid() {
+        return Binder.getCallingUid();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 064f4ee..d4b9267 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2003,7 +2003,7 @@
 
             mOverrideTaskTransition = options.getOverrideTaskTransition();
             mDismissKeyguard = options.getDismissKeyguard();
-            mShareIdentity = options.getShareIdentity();
+            mShareIdentity = options.isShareIdentityEnabled();
         }
 
         ColorDisplayService.ColorDisplayServiceInternal cds = LocalServices.getService(
@@ -9537,7 +9537,7 @@
 
         final ActivityTaskManagerService service = taskSupervisor.mService;
         final ActivityInfo aInfo = taskSupervisor.resolveActivity(intent, resolvedType, 0, null,
-                userId, Binder.getCallingUid());
+                userId, Binder.getCallingUid(), 0);
         if (aInfo == null) {
             throw new XmlPullParserException("restoreActivity resolver error. Intent=" + intent +
                     " resolvedType=" + resolvedType);
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 05ec3b5..43d3111 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -417,7 +417,8 @@
 
                 // Collect information about the target of the Intent.
                 ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i],
-                        0 /* startFlags */, null /* profilerInfo */, userId, filterCallingUid);
+                        0 /* startFlags */, null /* profilerInfo */, userId, filterCallingUid,
+                        callingPid);
                 aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
 
                 if (aInfo != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 719f72c..b40aa3c 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -23,6 +23,7 @@
 import static android.app.PendingIntent.FLAG_ONE_SHOT;
 import static android.app.admin.DevicePolicyManager.EXTRA_RESTRICTION;
 import static android.app.admin.DevicePolicyManager.POLICY_SUSPEND_PACKAGES;
+import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY;
 import static android.content.Context.KEYGUARD_SERVICE;
 import static android.content.Intent.EXTRA_INTENT;
 import static android.content.Intent.EXTRA_PACKAGE_NAME;
@@ -33,7 +34,9 @@
 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
 
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_SDK_SANDBOX_ORDER_ID;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
 import android.app.KeyguardManager;
@@ -226,16 +229,26 @@
                 getInterceptorInfo(null /* clearOptionsAnimation */);
 
         for (int i = 0; i < callbacks.size(); i++) {
+            final int orderId = callbacks.keyAt(i);
+            if (!shouldInterceptActivityLaunch(orderId, interceptorInfo)) {
+                continue;
+            }
+
             final ActivityInterceptorCallback callback = callbacks.valueAt(i);
-            final ActivityInterceptResult interceptResult = callback.intercept(interceptorInfo);
+            final ActivityInterceptResult interceptResult = callback.onInterceptActivityLaunch(
+                    interceptorInfo);
             if (interceptResult == null) {
                 continue;
             }
-            mIntent = interceptResult.intent;
-            mActivityOptions = interceptResult.activityOptions;
+            mIntent = interceptResult.getIntent();
+            mActivityOptions = interceptResult.getActivityOptions();
             mCallingPid = mRealCallingPid;
             mCallingUid = mRealCallingUid;
-            mRInfo = mSupervisor.resolveIntent(mIntent, null, mUserId, 0, mRealCallingUid);
+            if (interceptResult.isActivityResolved()) {
+                return true;
+            }
+            mRInfo = mSupervisor.resolveIntent(mIntent, null, mUserId, 0,
+                    mRealCallingUid, mRealCallingPid);
             mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags,
                     null /*profilerInfo*/);
             return true;
@@ -277,7 +290,8 @@
         mResolvedType = null;
 
         final UserInfo parent = mUserManager.getProfileParent(mUserId);
-        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid);
+        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0,
+                mRealCallingUid, mRealCallingPid);
         mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
         return true;
     }
@@ -298,10 +312,10 @@
         final UserInfo parent = mUserManager.getProfileParent(mUserId);
         if (parent != null) {
             mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0,
-                    mRealCallingUid);
+                    mRealCallingUid, mRealCallingPid);
         } else {
             mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0,
-                    mRealCallingUid);
+                    mRealCallingUid, mRealCallingPid);
         }
         mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
         return true;
@@ -334,7 +348,8 @@
         mCallingPid = mRealCallingPid;
         mCallingUid = mRealCallingUid;
         mResolvedType = null;
-        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
+        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0,
+                mRealCallingUid, mRealCallingPid);
         mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
         return true;
     }
@@ -353,7 +368,8 @@
         mCallingPid = mRealCallingPid;
         mCallingUid = mRealCallingUid;
         mResolvedType = null;
-        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
+        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0,
+                mRealCallingUid, mRealCallingPid);
         mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
         return true;
     }
@@ -388,7 +404,8 @@
         }
 
         final UserInfo parent = mUserManager.getProfileParent(mUserId);
-        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0, mRealCallingUid);
+        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, parent.id, 0,
+                mRealCallingUid, mRealCallingPid);
         mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
         return true;
     }
@@ -445,7 +462,8 @@
         mCallingUid = mRealCallingUid;
         mResolvedType = null;
 
-        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0, mRealCallingUid);
+        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId, 0,
+                mRealCallingUid, mRealCallingPid);
         mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
         return true;
     }
@@ -459,6 +477,11 @@
         ActivityInterceptorCallback.ActivityInterceptorInfo info = getInterceptorInfo(
                 r::clearOptionsAnimationForSiblings);
         for (int i = 0; i < callbacks.size(); i++) {
+            final int orderId = callbacks.keyAt(i);
+            if (!shouldNotifyOnActivityLaunch(orderId, info)) {
+                continue;
+            }
+
             final ActivityInterceptorCallback callback = callbacks.valueAt(i);
             callback.onActivityLaunched(taskInfo, r.info, info);
         }
@@ -466,9 +489,33 @@
 
     private ActivityInterceptorCallback.ActivityInterceptorInfo getInterceptorInfo(
             @Nullable Runnable clearOptionsAnimation) {
-        return new ActivityInterceptorCallback.ActivityInterceptorInfo(mRealCallingUid,
-                mRealCallingPid, mUserId, mCallingPackage, mCallingFeatureId, mIntent,
-                mRInfo, mAInfo, mResolvedType, mCallingPid, mCallingUid,
-                mActivityOptions, clearOptionsAnimation);
+        return new ActivityInterceptorCallback.ActivityInterceptorInfo.Builder(mCallingUid,
+                mCallingPid, mRealCallingUid, mRealCallingPid, mUserId, mIntent, mRInfo, mAInfo)
+                .setResolvedType(mResolvedType)
+                .setCallingPackage(mCallingPackage)
+                .setCallingFeatureId(mCallingFeatureId)
+                .setCheckedOptions(mActivityOptions)
+                .setClearOptionsAnimationRunnable(clearOptionsAnimation)
+                .build();
+    }
+
+    private boolean shouldInterceptActivityLaunch(
+            @ActivityInterceptorCallback.OrderedId int orderId,
+            @NonNull ActivityInterceptorCallback.ActivityInterceptorInfo info) {
+        if (orderId == MAINLINE_SDK_SANDBOX_ORDER_ID) {
+            return info.getIntent() != null && info.getIntent().getAction() != null
+                    && info.getIntent().getAction().equals(ACTION_START_SANDBOXED_ACTIVITY);
+        }
+        return true;
+    }
+
+    private boolean shouldNotifyOnActivityLaunch(
+            @ActivityInterceptorCallback.OrderedId int orderId,
+            @NonNull ActivityInterceptorCallback.ActivityInterceptorInfo info) {
+        if (orderId == MAINLINE_SDK_SANDBOX_ORDER_ID) {
+            return info.getIntent() != null && info.getIntent().getAction() != null
+                    && info.getIntent().getAction().equals(ACTION_START_SANDBOXED_ACTIVITY);
+        }
+        return true;
     }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index fb1b591..eb3fdca 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -528,7 +528,8 @@
 
             resolveInfo = supervisor.resolveIntent(intent, resolvedType, userId,
                     0 /* matchFlags */,
-                    computeResolveFilterUid(callingUid, realCallingUid, filterCallingUid));
+                    computeResolveFilterUid(callingUid, realCallingUid, filterCallingUid),
+                    realCallingPid);
             if (resolveInfo == null) {
                 final UserInfo userInfo = supervisor.getUserInfo(userId);
                 if (userInfo != null && userInfo.isManagedProfile()) {
@@ -551,7 +552,7 @@
                                 PackageManager.MATCH_DIRECT_BOOT_AWARE
                                         | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                                 computeResolveFilterUid(callingUid, realCallingUid,
-                                        filterCallingUid));
+                                        filterCallingUid), realCallingPid);
                     }
                 }
             }
@@ -806,7 +807,7 @@
         mRequest.resolveInfo = mSupervisor.resolveIntent(mRequest.intent, null /* resolvedType */,
                 mRequest.userId, 0 /* matchFlags */,
                 computeResolveFilterUid(mRequest.callingUid, mRequest.realCallingUid,
-                        mRequest.filterCallingUid));
+                        mRequest.filterCallingUid), mRequest.realCallingPid);
         mRequest.activityInfo =
                 mRequest.resolveInfo != null ? mRequest.resolveInfo.activityInfo : null;
         if (mRequest.activityInfo != null) {
@@ -1143,7 +1144,8 @@
 
                 rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId, 0,
                         computeResolveFilterUid(
-                                callingUid, realCallingUid, request.filterCallingUid));
+                                callingUid, realCallingUid, request.filterCallingUid),
+                        realCallingPid);
                 aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
                         null /*profilerInfo*/);
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index ec48643..c63bd52 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -680,12 +680,23 @@
 
     /**
      * Registers a callback which can intercept activity starts.
-     * @throws IllegalArgumentException if duplicate ids are provided
+     * @throws IllegalArgumentException if duplicate ids are provided or the provided {@code
+     * callback} is null
+     * @see ActivityInterceptorCallbackRegistry
+     * #registerActivityInterceptorCallback(int, ActivityInterceptorCallback)
      */
     public abstract void registerActivityStartInterceptor(
             @ActivityInterceptorCallback.OrderedId int id,
             ActivityInterceptorCallback callback);
 
+    /**
+     * Unregisters an {@link ActivityInterceptorCallback}.
+     * @throws IllegalArgumentException if id is not registered
+     * @see ActivityInterceptorCallbackRegistry#unregisterActivityInterceptorCallback(int)
+     */
+    public abstract void unregisterActivityStartInterceptor(
+            @ActivityInterceptorCallback.OrderedId int id);
+
     /** Get the most recent task excluding the first running task (the one on the front most). */
     public abstract ActivityManager.RecentTaskInfo getMostRecentTaskFromBackground();
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5dd77ea..1180df7 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -94,8 +94,10 @@
 import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE;
 import static com.android.server.am.EventLogTags.writeBootProgressEnableScreen;
 import static com.android.server.am.EventLogTags.writeConfigurationChanged;
-import static com.android.server.wm.ActivityInterceptorCallback.FIRST_ORDERED_ID;
-import static com.android.server.wm.ActivityInterceptorCallback.LAST_ORDERED_ID;
+import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_FIRST_ORDERED_ID;
+import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_LAST_ORDERED_ID;
+import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_FIRST_ORDERED_ID;
+import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_LAST_ORDERED_ID;
 import static com.android.server.wm.ActivityRecord.State.PAUSING;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK;
@@ -1876,7 +1878,7 @@
         try {
             // Collect information about the target of the Intent.
             final ActivityInfo aInfo = resolveActivityInfoForIntent(intent, resolvedType, userId,
-                    callingUid);
+                    callingUid, callingPid);
 
             synchronized (mGlobalLock) {
                 return mTaskSupervisor.canPlaceEntityOnDisplay(displayId, callingPid, callingUid,
@@ -1888,11 +1890,11 @@
     }
 
     ActivityInfo resolveActivityInfoForIntent(Intent intent, String resolvedType,
-            int userId, int callingUid) {
+            int userId, int callingUid, int callingPid) {
         ActivityInfo aInfo = mTaskSupervisor.resolveActivity(intent, resolvedType,
                 0 /* startFlags */, null /* profilerInfo */, userId,
                 ActivityStarter.computeResolveFilterUid(callingUid, callingUid,
-                        UserHandle.USER_NULL));
+                        UserHandle.USER_NULL), callingPid);
         return mAmInternal.getActivityInfoForUser(aInfo, userId);
     }
 
@@ -3628,19 +3630,6 @@
     }
 
     @Override
-    public void setSplitScreenResizing(boolean resizing) {
-        enforceTaskPermission("setSplitScreenResizing()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                mTaskSupervisor.setSplitScreenResizing(resizing);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    @Override
     public IWindowOrganizerController getWindowOrganizerController() {
         return mWindowOrganizerController;
     }
@@ -6812,16 +6801,35 @@
                 if (mActivityInterceptorCallbacks.contains(id)) {
                     throw new IllegalArgumentException("Duplicate id provided: " + id);
                 }
-                if (id > LAST_ORDERED_ID || id < FIRST_ORDERED_ID) {
+                if (callback == null) {
+                    throw new IllegalArgumentException("The passed ActivityInterceptorCallback "
+                            + "can not be null");
+                }
+                if (!ActivityInterceptorCallback.isValidOrderId(id)) {
                     throw new IllegalArgumentException(
-                            "Provided id " + id + " is not in range of valid ids ["
-                                    + FIRST_ORDERED_ID + "," + LAST_ORDERED_ID + "]");
+                            "Provided id " + id + " is not in range of valid ids for system "
+                                    + "services [" + SYSTEM_FIRST_ORDERED_ID + ","
+                                    + SYSTEM_LAST_ORDERED_ID + "] nor in range of valid ids for "
+                                    + "mainline module services [" + MAINLINE_FIRST_ORDERED_ID + ","
+                                    + MAINLINE_LAST_ORDERED_ID + "]");
                 }
                 mActivityInterceptorCallbacks.put(id, callback);
             }
         }
 
         @Override
+        public void unregisterActivityStartInterceptor(
+                @ActivityInterceptorCallback.OrderedId int id) {
+            synchronized (mGlobalLock) {
+                if (!mActivityInterceptorCallbacks.contains(id)) {
+                    throw new IllegalArgumentException(
+                            "ActivityInterceptorCallback with id (" + id + ") is not registered");
+                }
+                mActivityInterceptorCallbacks.remove(id);
+            }
+        }
+
+        @Override
         public ActivityManager.RecentTaskInfo getMostRecentTaskFromBackground() {
             List<ActivityManager.RunningTaskInfo> runningTaskInfoList = getTasks(1);
             ActivityManager.RunningTaskInfo runningTaskInfo;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 33c90a0..42da2a5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -207,9 +207,6 @@
     // Used to indicate that a task is removed it should also be removed from recents.
     static final boolean REMOVE_FROM_RECENTS = true;
 
-    /** True if the docked root task is currently being resized. */
-    private boolean mDockedRootTaskResizing;
-
     // Activity actions an app cannot start if it uses a permission which is not granted.
     private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
             new ArrayMap<>();
@@ -718,7 +715,7 @@
     }
 
     ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
-            int filterCallingUid) {
+            int filterCallingUid, int callingPid) {
         try {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resolveIntent");
             int modifiedFlags = flags
@@ -745,7 +742,7 @@
             try {
                 return mService.getPackageManagerInternalLocked().resolveIntentExported(
                         intent, resolvedType, modifiedFlags, privateResolveFlags, userId, true,
-                        filterCallingUid);
+                        filterCallingUid, callingPid);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -755,8 +752,9 @@
     }
 
     ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
-            ProfilerInfo profilerInfo, int userId, int filterCallingUid) {
-        final ResolveInfo rInfo = resolveIntent(intent, resolvedType, userId, 0, filterCallingUid);
+            ProfilerInfo profilerInfo, int userId, int filterCallingUid, int callingPid) {
+        final ResolveInfo rInfo = resolveIntent(intent, resolvedType, userId, 0,
+                filterCallingUid, callingPid);
         return resolveActivity(intent, rInfo, startFlags, profilerInfo);
     }
 
@@ -1526,15 +1524,6 @@
         return mLaunchParamsController;
     }
 
-    void setSplitScreenResizing(boolean resizing) {
-        if (resizing == mDockedRootTaskResizing) {
-            return;
-        }
-
-        mDockedRootTaskResizing = resizing;
-        mWindowManager.setDockedRootTaskResizing(resizing);
-    }
-
     private void removePinnedRootTaskInSurfaceTransaction(Task rootTask) {
         /**
          * Workaround: Force-stop all the activities in the root pinned task before we reparent them
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4f81ee4..d324df0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -530,7 +530,6 @@
     /** Remove this display when animation on it has completed. */
     private boolean mDeferredRemoval;
 
-    final DockedTaskDividerController mDividerControllerLocked;
     final PinnedTaskController mPinnedTaskController;
 
     final ArrayList<WindowState> mTapExcludedWindows = new ArrayList<>();
@@ -1141,7 +1140,7 @@
         }
 
         mDisplayPolicy = new DisplayPolicy(mWmService, this);
-        mDisplayRotation = new DisplayRotation(mWmService, this);
+        mDisplayRotation = new DisplayRotation(mWmService, this, mDisplayInfo.address);
 
         mDeviceStateController = new DeviceStateController(mWmService.mContext, mWmService.mH,
                 newFoldState -> {
@@ -1163,7 +1162,6 @@
             mDisplayPolicy.systemReady();
         }
         mWindowCornerRadius = mDisplayPolicy.getWindowCornerRadius();
-        mDividerControllerLocked = new DockedTaskDividerController(this);
         mPinnedTaskController = new PinnedTaskController(mWmService, this);
 
         final Transaction pendingTransaction = getPendingTransaction();
@@ -2596,10 +2594,6 @@
         }
     }
 
-    DockedTaskDividerController getDockedDividerController() {
-        return mDividerControllerLocked;
-    }
-
     PinnedTaskController getPinnedTaskController() {
         return mPinnedTaskController;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 300deca..a68d7af 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -59,11 +59,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManagerGlobal.ADD_OKAY;
 import static android.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED;
-import static android.view.WindowManagerPolicyConstants.ALT_BAR_BOTTOM;
-import static android.view.WindowManagerPolicyConstants.ALT_BAR_LEFT;
-import static android.view.WindowManagerPolicyConstants.ALT_BAR_RIGHT;
-import static android.view.WindowManagerPolicyConstants.ALT_BAR_TOP;
-import static android.view.WindowManagerPolicyConstants.ALT_BAR_UNKNOWN;
 import static android.view.WindowManagerPolicyConstants.EXTRA_HDMI_PLUGGED_STATE;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_INVALID;
@@ -139,7 +134,6 @@
 import com.android.internal.widget.PointerLocationView;
 import com.android.server.LocalServices;
 import com.android.server.UiThread;
-import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.policy.WindowManagerPolicy.NavigationBarPosition;
 import com.android.server.policy.WindowManagerPolicy.ScreenOnListener;
 import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
@@ -246,27 +240,6 @@
     @NavigationBarPosition
     private int mNavigationBarPosition = NAV_BAR_BOTTOM;
 
-    // Alternative status bar for when flexible insets mapping is used to place the status bar on
-    // another side of the screen.
-    private WindowState mStatusBarAlt = null;
-    @WindowManagerPolicy.AltBarPosition
-    private int mStatusBarAltPosition = ALT_BAR_UNKNOWN;
-    // Alternative navigation bar for when flexible insets mapping is used to place the navigation
-    // bar elsewhere on the screen.
-    private WindowState mNavigationBarAlt = null;
-    @WindowManagerPolicy.AltBarPosition
-    private int mNavigationBarAltPosition = ALT_BAR_UNKNOWN;
-    // Alternative climate bar for when flexible insets mapping is used to place a climate bar on
-    // the screen.
-    private WindowState mClimateBarAlt = null;
-    @WindowManagerPolicy.AltBarPosition
-    private int mClimateBarAltPosition = ALT_BAR_UNKNOWN;
-    // Alternative extra nav bar for when flexible insets mapping is used to place an extra nav bar
-    // on the screen.
-    private WindowState mExtraNavBarAlt = null;
-    @WindowManagerPolicy.AltBarPosition
-    private int mExtraNavBarAltPosition = ALT_BAR_UNKNOWN;
-
     private final ArraySet<WindowState> mInsetsSourceWindowsExceptIme = new ArraySet<>();
 
     /** Apps which are controlling the appearance of system bars */
@@ -345,6 +318,15 @@
     private boolean mForceConsumeSystemBars;
     private boolean mForceShowSystemBars;
 
+    /**
+     * Windows that provides gesture insets. If multiple windows provide gesture insets at the same
+     * side, the window with the highest z-order wins.
+     */
+    private WindowState mLeftGestureHost;
+    private WindowState mTopGestureHost;
+    private WindowState mRightGestureHost;
+    private WindowState mBottomGestureHost;
+
     private boolean mShowingDream;
     private boolean mLastShowingDream;
     private boolean mDreamingLockscreen;
@@ -362,13 +344,9 @@
     private boolean mShouldAttachNavBarToAppDuringTransition;
 
     // -------- PolicyHandler --------
-    private static final int MSG_REQUEST_TRANSIENT_BARS = 2;
     private static final int MSG_ENABLE_POINTER_LOCATION = 4;
     private static final int MSG_DISABLE_POINTER_LOCATION = 5;
 
-    private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
-    private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
-
     private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
 
     private final WindowManagerInternal.AppTransitionListener mAppTransitionListener;
@@ -385,15 +363,6 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_REQUEST_TRANSIENT_BARS:
-                    synchronized (mLock) {
-                        WindowState targetBar = (msg.arg1 == MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS)
-                                ? getStatusBar() : getNavigationBar();
-                        if (targetBar != null) {
-                            requestTransientBars(targetBar, true /* isGestureOnSystemBar */);
-                        }
-                    }
-                    break;
                 case MSG_ENABLE_POINTER_LOCATION:
                     enablePointerLocation();
                     break;
@@ -438,35 +407,57 @@
         mSystemGestures = new SystemGesturesPointerEventListener(mUiContext, mHandler,
                 new SystemGesturesPointerEventListener.Callbacks() {
 
+                    private static final long MOUSE_GESTURE_DELAY_MS = 500;
+
+                    private Runnable mOnSwipeFromLeft = this::onSwipeFromLeft;
+                    private Runnable mOnSwipeFromTop = this::onSwipeFromTop;
+                    private Runnable mOnSwipeFromRight = this::onSwipeFromRight;
+                    private Runnable mOnSwipeFromBottom = this::onSwipeFromBottom;
+
+                    private Insets getControllableInsets(WindowState win) {
+                        if (win == null) {
+                            return Insets.NONE;
+                        }
+                        final InsetsSourceProvider provider = win.getControllableInsetProvider();
+                        if (provider == null) {
+                            return  Insets.NONE;
+                        }
+                        return provider.getSource().calculateInsets(win.getBounds(),
+                                true /* ignoreVisibility */);
+                    }
+
                     @Override
                     public void onSwipeFromTop() {
                         synchronized (mLock) {
-                            final WindowState bar = mStatusBar != null
-                                    ? mStatusBar
-                                    : findAltBarMatchingPosition(ALT_BAR_TOP);
-                            requestTransientBars(bar, true /* isGestureOnSystemBar */);
+                            requestTransientBars(mTopGestureHost,
+                                    getControllableInsets(mTopGestureHost).top > 0);
                         }
                     }
 
                     @Override
                     public void onSwipeFromBottom() {
                         synchronized (mLock) {
-                            final WindowState bar = mNavigationBar != null
-                                        && mNavigationBarPosition == NAV_BAR_BOTTOM
-                                    ? mNavigationBar
-                                    : findAltBarMatchingPosition(ALT_BAR_BOTTOM);
-                            requestTransientBars(bar, true /* isGestureOnSystemBar */);
+                            requestTransientBars(mBottomGestureHost,
+                                    getControllableInsets(mBottomGestureHost).bottom > 0);
                         }
                     }
 
+                    private boolean allowsSideSwipe(Region excludedRegion) {
+                        return mNavigationBarAlwaysShowOnSideGesture
+                                && !mSystemGestures.currentGestureStartedInRegion(excludedRegion);
+                    }
+
                     @Override
                     public void onSwipeFromRight() {
                         final Region excludedRegion = Region.obtain();
                         synchronized (mLock) {
                             mDisplayContent.calculateSystemGestureExclusion(
                                     excludedRegion, null /* outUnrestricted */);
-                            requestTransientBarsForSideSwipe(excludedRegion, NAV_BAR_RIGHT,
-                                    ALT_BAR_RIGHT);
+                            final boolean hasWindow =
+                                    getControllableInsets(mRightGestureHost).right > 0;
+                            if (hasWindow || allowsSideSwipe(excludedRegion)) {
+                                requestTransientBars(mRightGestureHost, hasWindow);
+                            }
                         }
                         excludedRegion.recycle();
                     }
@@ -477,33 +468,15 @@
                         synchronized (mLock) {
                             mDisplayContent.calculateSystemGestureExclusion(
                                     excludedRegion, null /* outUnrestricted */);
-                            requestTransientBarsForSideSwipe(excludedRegion, NAV_BAR_LEFT,
-                                    ALT_BAR_LEFT);
+                            final boolean hasWindow =
+                                    getControllableInsets(mLeftGestureHost).left > 0;
+                            if (hasWindow || allowsSideSwipe(excludedRegion)) {
+                                requestTransientBars(mLeftGestureHost, hasWindow);
+                            }
                         }
                         excludedRegion.recycle();
                     }
 
-                    private void requestTransientBarsForSideSwipe(Region excludedRegion,
-                            int navBarSide, int altBarSide) {
-                        final WindowState barMatchingSide = mNavigationBar != null
-                                        && mNavigationBarPosition == navBarSide
-                                ? mNavigationBar
-                                : findAltBarMatchingPosition(altBarSide);
-                        final boolean allowSideSwipe = mNavigationBarAlwaysShowOnSideGesture &&
-                                !mSystemGestures.currentGestureStartedInRegion(excludedRegion);
-                        if (barMatchingSide == null && !allowSideSwipe) {
-                            return;
-                        }
-
-                        // Request transient bars on the matching bar, or any bar if we always allow
-                        // side swipes to show the bars
-                        final boolean isGestureOnSystemBar = barMatchingSide != null;
-                        final WindowState bar = barMatchingSide != null
-                                ? barMatchingSide
-                                : findTransientNavOrAltBar();
-                        requestTransientBars(bar, isGestureOnSystemBar);
-                    }
-
                     @Override
                     public void onFling(int duration) {
                         if (mService.mPowerManagerInternal != null) {
@@ -539,24 +512,47 @@
                     }
 
                     @Override
+                    public void onMouseHoverAtLeft() {
+                        mHandler.removeCallbacks(mOnSwipeFromLeft);
+                        mHandler.postDelayed(mOnSwipeFromLeft, MOUSE_GESTURE_DELAY_MS);
+                    }
+
+                    @Override
                     public void onMouseHoverAtTop() {
-                        mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
-                        Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
-                        msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS;
-                        mHandler.sendMessageDelayed(msg, 500 /* delayMillis */);
+                        mHandler.removeCallbacks(mOnSwipeFromTop);
+                        mHandler.postDelayed(mOnSwipeFromTop, MOUSE_GESTURE_DELAY_MS);
+                    }
+
+                    @Override
+                    public void onMouseHoverAtRight() {
+                        mHandler.removeCallbacks(mOnSwipeFromRight);
+                        mHandler.postDelayed(mOnSwipeFromRight, MOUSE_GESTURE_DELAY_MS);
                     }
 
                     @Override
                     public void onMouseHoverAtBottom() {
-                        mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
-                        Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
-                        msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION;
-                        mHandler.sendMessageDelayed(msg, 500 /* delayMillis */);
+                        mHandler.removeCallbacks(mOnSwipeFromBottom);
+                        mHandler.postDelayed(mOnSwipeFromBottom, MOUSE_GESTURE_DELAY_MS);
                     }
 
                     @Override
-                    public void onMouseLeaveFromEdge() {
-                        mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
+                    public void onMouseLeaveFromLeft() {
+                        mHandler.removeCallbacks(mOnSwipeFromLeft);
+                    }
+
+                    @Override
+                    public void onMouseLeaveFromTop() {
+                        mHandler.removeCallbacks(mOnSwipeFromTop);
+                    }
+
+                    @Override
+                    public void onMouseLeaveFromRight() {
+                        mHandler.removeCallbacks(mOnSwipeFromRight);
+                    }
+
+                    @Override
+                    public void onMouseLeaveFromBottom() {
+                        mHandler.removeCallbacks(mOnSwipeFromBottom);
                     }
                 });
         displayContent.registerPointerEventListener(mSystemGestures);
@@ -666,41 +662,6 @@
         }
     }
 
-    /**
-     * Returns the first non-null alt bar window matching the given position.
-     */
-    private WindowState findAltBarMatchingPosition(@WindowManagerPolicy.AltBarPosition int pos) {
-        if (mStatusBarAlt != null && mStatusBarAltPosition == pos) {
-            return mStatusBarAlt;
-        }
-        if (mNavigationBarAlt != null && mNavigationBarAltPosition == pos) {
-            return mNavigationBarAlt;
-        }
-        if (mClimateBarAlt != null && mClimateBarAltPosition == pos) {
-            return mClimateBarAlt;
-        }
-        if (mExtraNavBarAlt != null && mExtraNavBarAltPosition == pos) {
-            return mExtraNavBarAlt;
-        }
-        return null;
-    }
-
-    /**
-     * Finds the first non-null nav bar to request transient for.
-     */
-    private WindowState findTransientNavOrAltBar() {
-        if (mNavigationBar != null) {
-            return mNavigationBar;
-        }
-        if (mNavigationBarAlt != null) {
-            return mNavigationBarAlt;
-        }
-        if (mExtraNavBarAlt != null) {
-            return mExtraNavBarAlt;
-        }
-        return null;
-    }
-
     void systemReady() {
         mSystemGestures.systemReady();
         if (mService.mPointerLocationEnabled) {
@@ -970,20 +931,6 @@
             attrs.privateFlags &= ~PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION;
         }
 
-        // Check if alternate bars positions were updated.
-        if (mStatusBarAlt == win) {
-            mStatusBarAltPosition = getAltBarPosition(attrs);
-        }
-        if (mNavigationBarAlt == win) {
-            mNavigationBarAltPosition = getAltBarPosition(attrs);
-        }
-        if (mClimateBarAlt == win) {
-            mClimateBarAltPosition = getAltBarPosition(attrs);
-        }
-        if (mExtraNavBarAlt == win) {
-            mExtraNavBarAltPosition = getAltBarPosition(attrs);
-        }
-
         final InsetsSourceProvider provider = win.getControllableInsetProvider();
         if (provider != null && provider.getSource().getInsetsRoundedCornerFrame()
                 != attrs.insetsRoundedCornerFrame) {
@@ -1035,8 +982,7 @@
                 mContext.enforcePermission(
                         android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
                         "DisplayPolicy");
-                if ((mStatusBar != null && mStatusBar.isAlive())
-                        || (mStatusBarAlt != null && mStatusBarAlt.isAlive())) {
+                if (mStatusBar != null && mStatusBar.isAlive()) {
                     return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
                 }
                 break;
@@ -1054,8 +1000,7 @@
                 mContext.enforcePermission(
                         android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
                         "DisplayPolicy");
-                if ((mNavigationBar != null && mNavigationBar.isAlive())
-                        || (mNavigationBarAlt != null && mNavigationBarAlt.isAlive())) {
+                if (mNavigationBar != null && mNavigationBar.isAlive()) {
                     return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
                 }
                 break;
@@ -1086,34 +1031,6 @@
                         "DisplayPolicy");
             }
             enforceSingleInsetsTypeCorrespondingToWindowType(attrs.providedInsets);
-
-            for (InsetsFrameProvider provider : attrs.providedInsets) {
-                @InternalInsetsType int insetsType = provider.type;
-                switch (insetsType) {
-                    case ITYPE_STATUS_BAR:
-                        if ((mStatusBar != null && mStatusBar.isAlive())
-                                || (mStatusBarAlt != null && mStatusBarAlt.isAlive())) {
-                            return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
-                        }
-                        break;
-                    case ITYPE_NAVIGATION_BAR:
-                        if ((mNavigationBar != null && mNavigationBar.isAlive())
-                                || (mNavigationBarAlt != null && mNavigationBarAlt.isAlive())) {
-                            return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
-                        }
-                        break;
-                    case ITYPE_CLIMATE_BAR:
-                        if (mClimateBarAlt != null && mClimateBarAlt.isAlive()) {
-                            return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
-                        }
-                        break;
-                    case ITYPE_EXTRA_NAVIGATION_BAR:
-                        if (mExtraNavBarAlt != null && mExtraNavBarAlt.isAlive()) {
-                            return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
-                        }
-                        break;
-                }
-            }
         }
         return ADD_OKAY;
     }
@@ -1139,28 +1056,6 @@
         if (attrs.providedInsets != null) {
             for (int i = attrs.providedInsets.length - 1; i >= 0; i--) {
                 final InsetsFrameProvider provider = attrs.providedInsets[i];
-                switch (provider.type) {
-                    case ITYPE_STATUS_BAR:
-                        if (attrs.type != TYPE_STATUS_BAR) {
-                            mStatusBarAlt = win;
-                            mStatusBarAltPosition = getAltBarPosition(attrs);
-                        }
-                        break;
-                    case ITYPE_NAVIGATION_BAR:
-                        if (attrs.type != TYPE_NAVIGATION_BAR) {
-                            mNavigationBarAlt = win;
-                            mNavigationBarAltPosition = getAltBarPosition(attrs);
-                        }
-                        break;
-                    case ITYPE_CLIMATE_BAR:
-                        mClimateBarAlt = win;
-                        mClimateBarAltPosition = getAltBarPosition(attrs);
-                        break;
-                    case ITYPE_EXTRA_NAVIGATION_BAR:
-                        mExtraNavBarAlt = win;
-                        mExtraNavBarAltPosition = getAltBarPosition(attrs);
-                        break;
-                }
                 // The index of the provider and corresponding insets types cannot change at
                 // runtime as ensured in WMS. Make use of the index in the provider directly
                 // to access the latest provided size at runtime.
@@ -1217,22 +1112,6 @@
         };
     }
 
-    @WindowManagerPolicy.AltBarPosition
-    private int getAltBarPosition(WindowManager.LayoutParams params) {
-        switch (params.gravity) {
-            case Gravity.LEFT:
-                return ALT_BAR_LEFT;
-            case Gravity.RIGHT:
-                return ALT_BAR_RIGHT;
-            case Gravity.BOTTOM:
-                return ALT_BAR_BOTTOM;
-            case Gravity.TOP:
-                return ALT_BAR_TOP;
-            default:
-                return ALT_BAR_UNKNOWN;
-        }
-    }
-
     TriConsumer<DisplayFrames, WindowContainer, Rect> getImeSourceFrameProvider() {
         return (displayFrames, windowContainer, inOutFrame) -> {
             WindowState windowState = windowContainer.asWindowState();
@@ -1280,32 +1159,27 @@
      * @param win The window being removed.
      */
     void removeWindowLw(WindowState win) {
-        if (mStatusBar == win || mStatusBarAlt == win) {
+        if (mStatusBar == win) {
             mStatusBar = null;
-            mStatusBarAlt = null;
-            mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, null, null);
-        } else if (mNavigationBar == win || mNavigationBarAlt == win) {
+        } else if (mNavigationBar == win) {
             mNavigationBar = null;
-            mNavigationBarAlt = null;
-            mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, null, null);
         } else if (mNotificationShade == win) {
             mNotificationShade = null;
-        } else if (mClimateBarAlt == win) {
-            mClimateBarAlt = null;
-            mDisplayContent.setInsetProvider(ITYPE_CLIMATE_BAR, null, null);
-        } else if (mExtraNavBarAlt == win) {
-            mExtraNavBarAlt = null;
-            mDisplayContent.setInsetProvider(ITYPE_EXTRA_NAVIGATION_BAR, null, null);
         }
         if (mLastFocusedWindow == win) {
             mLastFocusedWindow = null;
         }
 
+        final SparseArray<InsetsSource> sources = win.getProvidedInsetsSources();
+        for (int index = sources.size() - 1; index >= 0; index--) {
+            final @InternalInsetsType int type = sources.keyAt(index);
+            mDisplayContent.setInsetProvider(type, null /* win */, null /* frameProvider */);
+        }
         mInsetsSourceWindowsExceptIme.remove(win);
     }
 
     WindowState getStatusBar() {
-        return mStatusBar != null ? mStatusBar : mStatusBarAlt;
+        return mStatusBar;
     }
 
     WindowState getNotificationShade() {
@@ -1313,7 +1187,7 @@
     }
 
     WindowState getNavigationBar() {
-        return mNavigationBar != null ? mNavigationBar : mNavigationBarAlt;
+        return mNavigationBar;
     }
 
     /**
@@ -1439,6 +1313,10 @@
      * Called following layout of all windows before each window has policy applied.
      */
     public void beginPostLayoutPolicyLw() {
+        mLeftGestureHost = null;
+        mTopGestureHost = null;
+        mRightGestureHost = null;
+        mBottomGestureHost = null;
         mTopFullscreenOpaqueWindowState = null;
         mNavBarColorWindowCandidate = null;
         mNavBarBackgroundWindow = null;
@@ -1480,6 +1358,33 @@
             mIsFreeformWindowOverlappingWithNavBar = true;
         }
 
+        final SparseArray<InsetsSource> sources = win.getProvidedInsetsSources();
+        final Rect bounds = win.getBounds();
+        for (int index = sources.size() - 1; index >= 0; index--) {
+            final InsetsSource source = sources.valueAt(index);
+            if ((source.getType()
+                    & (Type.systemGestures() | Type.mandatorySystemGestures())) == 0) {
+                continue;
+            }
+            if (mLeftGestureHost != null && mTopGestureHost != null
+                    && mRightGestureHost != null && mBottomGestureHost != null) {
+                continue;
+            }
+            final Insets insets = source.calculateInsets(bounds, false /* ignoreVisibility */);
+            if (mLeftGestureHost == null && insets.left > 0) {
+                mLeftGestureHost = win;
+            }
+            if (mTopGestureHost == null && insets.top > 0) {
+                mTopGestureHost = win;
+            }
+            if (mRightGestureHost == null && insets.right > 0) {
+                mRightGestureHost = win;
+            }
+            if (mBottomGestureHost == null && insets.bottom > 0) {
+                mBottomGestureHost = win;
+            }
+        }
+
         if (!affectsSystemUi) {
             return;
         }
@@ -2588,11 +2493,6 @@
         if (mStatusBar != null) {
             pw.print(prefix); pw.print("mStatusBar="); pw.println(mStatusBar);
         }
-        if (mStatusBarAlt != null) {
-            pw.print(prefix); pw.print("mStatusBarAlt="); pw.println(mStatusBarAlt);
-            pw.print(prefix); pw.print("mStatusBarAltPosition=");
-            pw.println(mStatusBarAltPosition);
-        }
         if (mNotificationShade != null) {
             pw.print(prefix); pw.print("mExpandedPanel="); pw.println(mNotificationShade);
         }
@@ -2604,20 +2504,17 @@
             pw.print(prefix); pw.print("mNavigationBarPosition=");
             pw.println(mNavigationBarPosition);
         }
-        if (mNavigationBarAlt != null) {
-            pw.print(prefix); pw.print("mNavigationBarAlt="); pw.println(mNavigationBarAlt);
-            pw.print(prefix); pw.print("mNavigationBarAltPosition=");
-            pw.println(mNavigationBarAltPosition);
+        if (mLeftGestureHost != null) {
+            pw.print(prefix); pw.print("mLeftGestureHost="); pw.println(mLeftGestureHost);
         }
-        if (mClimateBarAlt != null) {
-            pw.print(prefix); pw.print("mClimateBarAlt="); pw.println(mClimateBarAlt);
-            pw.print(prefix); pw.print("mClimateBarAltPosition=");
-            pw.println(mClimateBarAltPosition);
+        if (mTopGestureHost != null) {
+            pw.print(prefix); pw.print("mTopGestureHost="); pw.println(mTopGestureHost);
         }
-        if (mExtraNavBarAlt != null) {
-            pw.print(prefix); pw.print("mExtraNavBarAlt="); pw.println(mExtraNavBarAlt);
-            pw.print(prefix); pw.print("mExtraNavBarAltPosition=");
-            pw.println(mExtraNavBarAltPosition);
+        if (mRightGestureHost != null) {
+            pw.print(prefix); pw.print("mRightGestureHost="); pw.println(mRightGestureHost);
+        }
+        if (mBottomGestureHost != null) {
+            pw.print(prefix); pw.print("mBottomGestureHost="); pw.println(mBottomGestureHost);
         }
         if (mFocusedWindow != null) {
             pw.print(prefix); pw.print("mFocusedWindow="); pw.println(mFocusedWindow);
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 185e06e..4fb137b 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -58,6 +58,7 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
+import android.view.DisplayAddress;
 import android.view.IWindowManager;
 import android.view.Surface;
 import android.window.TransitionRequestInfo;
@@ -211,15 +212,16 @@
     private boolean mDemoHdmiRotationLock;
     private boolean mDemoRotationLock;
 
-    DisplayRotation(WindowManagerService service, DisplayContent displayContent) {
-        this(service, displayContent, displayContent.getDisplayPolicy(),
+    DisplayRotation(WindowManagerService service, DisplayContent displayContent,
+            DisplayAddress displayAddress) {
+        this(service, displayContent, displayAddress, displayContent.getDisplayPolicy(),
                 service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock());
     }
 
     @VisibleForTesting
     DisplayRotation(WindowManagerService service, DisplayContent displayContent,
-            DisplayPolicy displayPolicy, DisplayWindowSettings displayWindowSettings,
-            Context context, Object lock) {
+            DisplayAddress displayAddress, DisplayPolicy displayPolicy,
+            DisplayWindowSettings displayWindowSettings, Context context, Object lock) {
         mService = service;
         mDisplayContent = displayContent;
         mDisplayPolicy = displayPolicy;
@@ -235,6 +237,8 @@
         mDeskDockRotation = readRotation(R.integer.config_deskDockRotation);
         mUndockedHdmiRotation = readRotation(R.integer.config_undockedHdmiRotation);
 
+        mRotation = readDefaultDisplayRotation(displayAddress);
+
         if (isDefaultDisplay) {
             final Handler uiHandler = UiThread.getHandler();
             mOrientationListener = new OrientationListener(mContext, uiHandler);
@@ -248,6 +252,33 @@
         }
     }
 
+    // Change the default value to the value specified in the sysprop
+    // ro.bootanim.set_orientation_<display_id>. Four values are supported: ORIENTATION_0,
+    // ORIENTATION_90, ORIENTATION_180 and ORIENTATION_270.
+    // If the value isn't specified or is ORIENTATION_0, nothing will be changed.
+    // This is needed to support having default orientation different from the natural
+    // device orientation. For example, on tablets that may want to keep natural orientation
+    // portrait for applications compatibility but have landscape orientation as a default choice
+    // from the UX perspective.
+    @Surface.Rotation
+    private int readDefaultDisplayRotation(DisplayAddress displayAddress) {
+        if (!(displayAddress instanceof DisplayAddress.Physical)) {
+            return Surface.ROTATION_0;
+        }
+        final DisplayAddress.Physical physicalAddress = (DisplayAddress.Physical) displayAddress;
+        String syspropValue = SystemProperties.get(
+                "ro.bootanim.set_orientation_" + physicalAddress.getPhysicalDisplayId(),
+                "ORIENTATION_0");
+        if (syspropValue.equals("ORIENTATION_90")) {
+            return Surface.ROTATION_90;
+        } else if (syspropValue.equals("ORIENTATION_180")) {
+            return Surface.ROTATION_180;
+        } else if (syspropValue.equals("ORIENTATION_270")) {
+            return Surface.ROTATION_270;
+        }
+        return Surface.ROTATION_0;
+    }
+
     private int readRotation(int resID) {
         try {
             final int rotation = mContext.getResources().getInteger(resID);
diff --git a/services/core/java/com/android/server/wm/DockedTaskDividerController.java b/services/core/java/com/android/server/wm/DockedTaskDividerController.java
deleted file mode 100644
index 925a6d8..0000000
--- a/services/core/java/com/android/server/wm/DockedTaskDividerController.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.graphics.Rect;
-
-/**
- * Keeps information about the docked task divider.
- */
-public class DockedTaskDividerController {
-
-    private final DisplayContent mDisplayContent;
-    private boolean mResizing;
-
-    private final Rect mTouchRegion = new Rect();
-
-    DockedTaskDividerController(DisplayContent displayContent) {
-        mDisplayContent = displayContent;
-    }
-
-    boolean isResizing() {
-        return mResizing;
-    }
-
-    void setResizing(boolean resizing) {
-        if (mResizing != resizing) {
-            mResizing = resizing;
-            resetDragResizingChangeReported();
-        }
-    }
-
-    void setTouchRegion(Rect touchRegion) {
-        mTouchRegion.set(touchRegion);
-        // We need to report touchable region changes to accessibility.
-        if (mDisplayContent.mWmService.mAccessibilityController.hasCallbacks()) {
-            mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(
-                    mDisplayContent.getDisplayId());
-        }
-    }
-
-    void getTouchRegion(Rect outRegion) {
-        outRegion.set(mTouchRegion);
-    }
-
-    private void resetDragResizingChangeReported() {
-        mDisplayContent.forAllWindows(WindowState::resetDragResizingChangeReported,
-                true /* traverseTopToBottom */);
-    }
-}
diff --git a/services/core/java/com/android/server/wm/DragResizeMode.java b/services/core/java/com/android/server/wm/DragResizeMode.java
deleted file mode 100644
index 684cf06..0000000
--- a/services/core/java/com/android/server/wm/DragResizeMode.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2016 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.wm;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
-/**
- * Describes the mode in which a window is drag resizing.
- */
-class DragResizeMode {
-
-    /**
-     * Freeform mode: Client surface is fullscreen, and client is responsible to draw window at
-     * the correct position.
-     */
-    static final int DRAG_RESIZE_MODE_FREEFORM = 0;
-
-    /**
-     * Mode for resizing the docked (and adjacent) root task: Client surface is fullscreen, but
-     * window is drawn at (0, 0), window manager is responsible for positioning the surface when
-     * dragging.
-     */
-    static final int DRAG_RESIZE_MODE_DOCKED_DIVIDER = 1;
-
-    static boolean isModeAllowedForRootTask(Task rootTask, int mode) {
-        switch (mode) {
-            case DRAG_RESIZE_MODE_FREEFORM:
-                return rootTask.getWindowingMode() == WINDOWING_MODE_FREEFORM;
-            default:
-                return false;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ca9b9b3..13da96f 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1478,7 +1478,7 @@
                 final String resolvedType =
                         homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
                 final ResolveInfo info = mTaskSupervisor.resolveIntent(homeIntent, resolvedType,
-                        userId, flags, Binder.getCallingUid());
+                        userId, flags, Binder.getCallingUid(), Binder.getCallingPid());
                 if (info != null) {
                     aInfo = info.activityInfo;
                 }
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index bf3edcf..17b463f 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -403,7 +403,7 @@
                             pi.getIntentSender().getTarget());
                     final ActivityInfo info = mService.mAtmService.resolveActivityInfoForIntent(
                             launchIntent, null /* resolvedType */, user.getIdentifier(),
-                            callingUid);
+                            callingUid, callingPid);
                     item.setActivityInfo(info);
                 }
             } finally {
@@ -437,7 +437,7 @@
                 }
                 final ActivityInfo info = mService.mAtmService.resolveActivityInfoForIntent(
                         shortcutIntents[0], null /* resolvedType */, user.getIdentifier(),
-                        callingUid);
+                        callingUid, callingPid);
                 item.setActivityInfo(info);
             }
         } else if (hasTask) {
@@ -461,7 +461,8 @@
                 } else {
                     // Resolve the activity info manually if the task was restored after reboot
                     final ActivityInfo info = mService.mAtmService.resolveActivityInfoForIntent(
-                            task.intent, null /* resolvedType */, task.mUserId, callingUid);
+                            task.intent, null /* resolvedType */, task.mUserId, callingUid,
+                            callingPid);
                     item.setActivityInfo(info);
                 }
             }
diff --git a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
index 658f4ef..878b33f 100644
--- a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
@@ -78,7 +78,10 @@
     private int mDownPointers;
     private boolean mSwipeFireable;
     private boolean mDebugFireable;
-    private boolean mMouseHoveringAtEdge;
+    private boolean mMouseHoveringAtLeft;
+    private boolean mMouseHoveringAtTop;
+    private boolean mMouseHoveringAtRight;
+    private boolean mMouseHoveringAtBottom;
     private long mLastFlingTime;
 
     SystemGesturesPointerEventListener(Context context, Handler handler, Callbacks callbacks) {
@@ -174,9 +177,21 @@
                 mDebugFireable = true;
                 mDownPointers = 0;
                 captureDown(event, 0);
-                if (mMouseHoveringAtEdge) {
-                    mMouseHoveringAtEdge = false;
-                    mCallbacks.onMouseLeaveFromEdge();
+                if (mMouseHoveringAtLeft) {
+                    mMouseHoveringAtLeft = false;
+                    mCallbacks.onMouseLeaveFromLeft();
+                }
+                if (mMouseHoveringAtTop) {
+                    mMouseHoveringAtTop = false;
+                    mCallbacks.onMouseLeaveFromTop();
+                }
+                if (mMouseHoveringAtRight) {
+                    mMouseHoveringAtRight = false;
+                    mCallbacks.onMouseLeaveFromRight();
+                }
+                if (mMouseHoveringAtBottom) {
+                    mMouseHoveringAtBottom = false;
+                    mCallbacks.onMouseLeaveFromBottom();
                 }
                 mCallbacks.onDown();
                 break;
@@ -211,16 +226,35 @@
                 break;
             case MotionEvent.ACTION_HOVER_MOVE:
                 if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
-                    if (!mMouseHoveringAtEdge && event.getY() == 0) {
+                    final float eventX = event.getX();
+                    final float eventY = event.getY();
+                    if (!mMouseHoveringAtLeft && eventX == 0) {
+                        mCallbacks.onMouseHoverAtLeft();
+                        mMouseHoveringAtLeft = true;
+                    } else if (mMouseHoveringAtLeft && eventX > 0) {
+                        mCallbacks.onMouseLeaveFromLeft();
+                        mMouseHoveringAtLeft = false;
+                    }
+                    if (!mMouseHoveringAtTop && eventY == 0) {
                         mCallbacks.onMouseHoverAtTop();
-                        mMouseHoveringAtEdge = true;
-                    } else if (!mMouseHoveringAtEdge && event.getY() >= screenHeight - 1) {
+                        mMouseHoveringAtTop = true;
+                    } else if (mMouseHoveringAtTop && eventY > 0) {
+                        mCallbacks.onMouseLeaveFromTop();
+                        mMouseHoveringAtTop = false;
+                    }
+                    if (!mMouseHoveringAtRight && eventX >= screenWidth - 1) {
+                        mCallbacks.onMouseHoverAtRight();
+                        mMouseHoveringAtRight = true;
+                    } else if (mMouseHoveringAtRight && eventX < screenWidth - 1) {
+                        mCallbacks.onMouseLeaveFromRight();
+                        mMouseHoveringAtRight = false;
+                    }
+                    if (!mMouseHoveringAtBottom && eventY >= screenHeight - 1) {
                         mCallbacks.onMouseHoverAtBottom();
-                        mMouseHoveringAtEdge = true;
-                    } else if (mMouseHoveringAtEdge
-                            && (event.getY() > 0 && event.getY() < screenHeight - 1)) {
-                        mCallbacks.onMouseLeaveFromEdge();
-                        mMouseHoveringAtEdge = false;
+                        mMouseHoveringAtBottom = true;
+                    } else if (mMouseHoveringAtBottom && eventY < screenHeight - 1) {
+                        mCallbacks.onMouseLeaveFromBottom();
+                        mMouseHoveringAtBottom = false;
                     }
                 }
                 break;
@@ -373,9 +407,14 @@
         void onFling(int durationMs);
         void onDown();
         void onUpOrCancel();
+        void onMouseHoverAtLeft();
         void onMouseHoverAtTop();
+        void onMouseHoverAtRight();
         void onMouseHoverAtBottom();
-        void onMouseLeaveFromEdge();
+        void onMouseLeaveFromLeft();
+        void onMouseLeaveFromTop();
+        void onMouseLeaveFromRight();
+        void onMouseLeaveFromBottom();
         void onDebug();
     }
 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index fdb5f1f..8609e10 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -470,7 +470,6 @@
 
     // Whether the task is currently being drag-resized
     private boolean mDragResizing;
-    private int mDragResizeMode;
 
     // This represents the last resolved activity values for this task
     // NOTE: This value needs to be persisted with each task
@@ -2818,11 +2817,6 @@
         }
 
         final Task rootTask = getRootTask();
-        final DisplayContent displayContent = rootTask.getDisplayContent();
-        // It doesn't matter if we in particular are part of the resize, since we couldn't have
-        // a DimLayer anyway if we weren't visible.
-        final boolean dockedResizing = displayContent != null
-                && displayContent.mDividerControllerLocked.isResizing();
         if (inFreeformWindowingMode()) {
             boolean[] foundTop = { false };
             forAllActivities(a -> { getMaxVisibleBounds(a, out, foundTop); });
@@ -2833,18 +2827,10 @@
 
         if (!matchParentBounds()) {
             // When minimizing the root docked task when going home, we don't adjust the task bounds
-            // so we need to intersect the task bounds with the root task bounds here.
-            //
-            // If we are Docked Resizing with snap points, the task bounds could be smaller than the
-            // root task bounds and so we don't even want to use them. Even if the app should not be
-            // resized the Dim should keep up with the divider.
-            if (dockedResizing) {
-                rootTask.getBounds(out);
-            } else {
-                rootTask.getBounds(mTmpRect);
-                mTmpRect.intersect(getBounds());
-                out.set(mTmpRect);
-            }
+            // so we need to intersect the task bounds with the root task bounds here..
+            rootTask.getBounds(mTmpRect);
+            mTmpRect.intersect(getBounds());
+            out.set(mTmpRect);
         } else {
             out.set(getBounds());
         }
@@ -2875,16 +2861,15 @@
         }
     }
 
-    void setDragResizing(boolean dragResizing, int dragResizeMode) {
+    void setDragResizing(boolean dragResizing) {
         if (mDragResizing != dragResizing) {
-            // No need to check if the mode is allowed if it's leaving dragResize
+            // No need to check if allowed if it's leaving dragResize
             if (dragResizing
-                    && !DragResizeMode.isModeAllowedForRootTask(getRootTask(), dragResizeMode)) {
-                throw new IllegalArgumentException("Drag resize mode not allow for root task id="
-                        + getRootTaskId() + " dragResizeMode=" + dragResizeMode);
+                    && !(getRootTask().getWindowingMode() == WINDOWING_MODE_FREEFORM)) {
+                throw new IllegalArgumentException("Drag resize not allow for root task id="
+                        + getRootTaskId());
             }
             mDragResizing = dragResizing;
-            mDragResizeMode = dragResizeMode;
             resetDragResizingChangeReported();
         }
     }
@@ -2893,10 +2878,6 @@
         return mDragResizing;
     }
 
-    int getDragResizeMode() {
-        return mDragResizeMode;
-    }
-
     void adjustBoundsForDisplayChangeIfNeeded(final DisplayContent displayContent) {
         if (displayContent == null) {
             return;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 5b32149..9b3fb6b 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -27,7 +27,6 @@
 import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
 import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -368,7 +367,7 @@
 
     private void endDragLocked() {
         mResizing = false;
-        mTask.setDragResizing(false, DRAG_RESIZE_MODE_FREEFORM);
+        mTask.setDragResizing(false);
     }
 
     /** Returns true if the move operation should be ended. */
@@ -380,7 +379,7 @@
 
         if (mCtrlType != CTRL_NONE) {
             resizeDrag(x, y);
-            mTask.setDragResizing(true, DRAG_RESIZE_MODE_FREEFORM);
+            mTask.setDragResizing(true);
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3242c2c..70bedc7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7140,20 +7140,6 @@
         return 0;
     }
 
-    void setDockedRootTaskResizing(boolean resizing) {
-        getDefaultDisplayContentLocked().getDockedDividerController().setResizing(resizing);
-        requestTraversal();
-    }
-
-    @Override
-    public void setDockedTaskDividerTouchRegion(Rect touchRegion) {
-        synchronized (mGlobalLock) {
-            final DisplayContent dc = getDefaultDisplayContentLocked();
-            dc.getDockedDividerController().setTouchRegion(touchRegion);
-            dc.updateTouchExcludeRegion();
-        }
-    }
-
     void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) {
         synchronized (mGlobalLock) {
             mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays;
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 4ec1153..b624e80 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -51,7 +51,6 @@
 import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
 import static com.android.server.wm.ActivityTaskManagerService.enforceTaskPermission;
 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK;
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
 import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
@@ -722,7 +721,7 @@
         }
 
         if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING) != 0) {
-            tr.setDragResizing(c.getDragResizing(), DRAG_RESIZE_MODE_FREEFORM);
+            tr.setDragResizing(c.getDragResizing());
         }
 
         final int childWindowingMode = c.getActivityWindowingMode();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3ca254d..746f983 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -34,9 +34,6 @@
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
 import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
-import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
-import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
-import static android.view.WindowCallbacks.RESIZE_MODE_INVALID;
 import static android.view.WindowInsets.Type.navigationBars;
 import static android.view.WindowInsets.Type.systemBars;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
@@ -122,8 +119,6 @@
 import static com.android.server.wm.AnimationSpecProto.MOVE;
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
 import static com.android.server.wm.DisplayContent.logsGestureExclusionRestrictions;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
 import static com.android.server.wm.IdentifierProto.TITLE;
 import static com.android.server.wm.IdentifierProto.USER_ID;
@@ -369,7 +364,6 @@
     boolean mHidden = true;    // Used to determine if to show child windows.
     private boolean mDragResizing;
     private boolean mDragResizingChangeReported = true;
-    private int mResizeMode;
     private boolean mRedrawForSyncReported;
 
     /**
@@ -3942,24 +3936,14 @@
         if (isDragResizeChanged) {
             setDragResizing();
         }
-        int resizeMode = RESIZE_MODE_INVALID;
-        if (isDragResizing()) {
-            switch (getResizeMode()) {
-                case DRAG_RESIZE_MODE_FREEFORM:
-                    resizeMode = RESIZE_MODE_FREEFORM;
-                    break;
-                case DRAG_RESIZE_MODE_DOCKED_DIVIDER:
-                    resizeMode = RESIZE_MODE_DOCKED_DIVIDER;
-                    break;
-            }
-        }
+        final boolean isDragResizing = isDragResizing();
 
         markRedrawForSyncReported();
 
         try {
             mClient.resized(mClientWindowFrames, reportDraw, mLastReportedConfiguration,
                     getCompatInsetsState(), forceRelayout, alwaysConsumeSystemBars, displayId,
-                    syncWithBuffers ? mSyncSeqId : -1, resizeMode);
+                    syncWithBuffers ? mSyncSeqId : -1, isDragResizing);
             if (drawPending && prevRotation >= 0 && prevRotation != mLastReportedConfiguration
                     .getMergedConfiguration().windowConfiguration.getRotation()) {
                 mOrientationChangeRedrawRequestTime = SystemClock.elapsedRealtime();
@@ -4202,10 +4186,6 @@
         super.resetDragResizingChangeReported();
     }
 
-    int getResizeMode() {
-        return mResizeMode;
-    }
-
     private boolean computeDragResizing() {
         final Task task = getTask();
         if (task == null) {
@@ -4228,8 +4208,7 @@
             return true;
         }
 
-        return getDisplayContent().mDividerControllerLocked.isResizing()
-                && !task.inFreeformWindowingMode() && !isGoneForLayout();
+        return false;
     }
 
     void setDragResizing() {
@@ -4238,25 +4217,12 @@
             return;
         }
         mDragResizing = resizing;
-        final Task task = getTask();
-        if (task != null && task.isDragResizing()) {
-            mResizeMode = task.getDragResizeMode();
-        } else {
-            mResizeMode = mDragResizing && getDisplayContent().mDividerControllerLocked.isResizing()
-                    ? DRAG_RESIZE_MODE_DOCKED_DIVIDER
-                    : DRAG_RESIZE_MODE_FREEFORM;
-        }
     }
 
     boolean isDragResizing() {
         return mDragResizing;
     }
 
-    boolean isDockedResizing() {
-        return (mDragResizing && getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER)
-                || (isChildWindow() && getParentWindow().isDockedResizing());
-    }
-
     @CallSuper
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
@@ -5932,10 +5898,6 @@
             //    level. Because the animation runs before display is rotated, task bounds should
             //    represent the frames in display space coordinates.
             outFrame.set(getTask().getBounds());
-        } else if (isDockedResizing()) {
-            // If we are animating while docked resizing, then use the root task bounds as the
-            // animation target (which will be different than the task bounds)
-            outFrame.set(getTask().getParent().getBounds());
         } else {
             outFrame.set(getParentFrame());
         }
diff --git a/services/core/java/com/android/server/wm/package-info.java b/services/core/java/com/android/server/wm/package-info.java
new file mode 100644
index 0000000..6ab5835
--- /dev/null
+++ b/services/core/java/com/android/server/wm/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @hide
+ * TODO(b/146466118) remove this javadoc tag
+ */
[email protected]
+package com.android.server.wm;
diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
new file mode 100644
index 0000000..6b254bf
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.credentials;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.credentials.ClearCredentialStateRequest;
+import android.credentials.IClearCredentialStateCallback;
+import android.credentials.ui.ProviderData;
+import android.credentials.ui.RequestInfo;
+import android.os.RemoteException;
+import android.service.credentials.CredentialProviderInfo;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * Central session for a single clearCredentialState request. This class listens to the
+ * responses from providers, and updates the provider(S) state.
+ */
+public final class ClearRequestSession extends RequestSession<ClearCredentialStateRequest,
+        IClearCredentialStateCallback>
+        implements ProviderSession.ProviderInternalCallback<Void> {
+    private static final String TAG = "GetRequestSession";
+
+    public ClearRequestSession(Context context, int userId,
+            IClearCredentialStateCallback callback, ClearCredentialStateRequest request,
+            String callingPackage) {
+        super(context, userId, request, callback, RequestInfo.TYPE_UNDEFINED, callingPackage);
+    }
+
+    /**
+     * Creates a new provider session, and adds it list of providers that are contributing to
+     * this session.
+     * @return the provider session created within this request session, for the given provider
+     * info.
+     */
+    @Override
+    @Nullable
+    public ProviderSession initiateProviderSession(CredentialProviderInfo providerInfo,
+            RemoteCredentialService remoteCredentialService) {
+        ProviderClearSession providerClearSession = ProviderClearSession
+                .createNewSession(mContext, mUserId, providerInfo,
+                        this, remoteCredentialService);
+        if (providerClearSession != null) {
+            Log.i(TAG, "In startProviderSession - provider session created and being added");
+            mProviders.put(providerClearSession.getComponentName().flattenToString(),
+                    providerClearSession);
+        }
+        return providerClearSession;
+    }
+
+    @Override // from provider session
+    public void onProviderStatusChanged(ProviderSession.Status status,
+            ComponentName componentName) {
+        Log.i(TAG, "in onStatusChanged with status: " + status);
+        if (ProviderSession.isTerminatingStatus(status)) {
+            Log.i(TAG, "in onStatusChanged terminating status");
+            onProviderTerminated(componentName);
+        } else if (ProviderSession.isCompletionStatus(status)) {
+            Log.i(TAG, "in onStatusChanged isCompletionStatus status");
+            onProviderResponseComplete(componentName);
+        }
+    }
+
+    @Override
+    public void onFinalResponseReceived(ComponentName componentName, Void response) {
+        respondToClientWithResponseAndFinish();
+    }
+
+    @Override
+    protected void onProviderResponseComplete(ComponentName componentName) {
+        if (!isAnyProviderPending()) {
+            onFinalResponseReceived(componentName, null);
+        }
+    }
+
+    @Override
+    protected void onProviderTerminated(ComponentName componentName) {
+        if (!isAnyProviderPending()) {
+            processResponses();
+        }
+    }
+
+    @Override
+    protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) {
+        //Not applicable for clearCredential as UI is not needed
+    }
+
+    @Override
+    public void onFinalErrorReceived(ComponentName componentName, String errorType,
+            String message) {
+        //Not applicable for clearCredential as response is not picked by the user
+    }
+
+    private void respondToClientWithResponseAndFinish() {
+        Log.i(TAG, "respondToClientWithResponseAndFinish");
+        try {
+            mClientCallback.onSuccess();
+        } catch (RemoteException e) {
+            Log.i(TAG, "Issue while propagating the response to the client");
+        }
+        finishSession();
+    }
+
+    private void respondToClientWithErrorAndFinish(String errorType, String errorMsg) {
+        Log.i(TAG, "respondToClientWithErrorAndFinish");
+        try {
+            mClientCallback.onError(errorType, errorMsg);
+        } catch (RemoteException e) {
+            e.printStackTrace();
+        }
+        finishSession();
+    }
+    private void processResponses() {
+        for (ProviderSession session: mProviders.values()) {
+            if (session.isProviderResponseSet()) {
+                // If even one provider responded successfully, send back the response
+                // TODO: Aggregate other exceptions
+                respondToClientWithResponseAndFinish();
+                return;
+            }
+        }
+        // TODO: Replace with properly defined error type
+        respondToClientWithErrorAndFinish("unknown", "All providers failed");
+    }
+}
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 1f74e93..d29f86e 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -314,9 +314,45 @@
         @Override
         public ICancellationSignal clearCredentialState(ClearCredentialStateRequest request,
                 IClearCredentialStateCallback callback, String callingPackage) {
-            // TODO: implement.
-            Log.i(TAG, "clearCredentialSession");
+            Log.i(TAG, "starting clearCredentialState with callingPackage: " + callingPackage);
+            // TODO : Implement cancellation
             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
+
+            // New request session, scoped for this request only.
+            final ClearRequestSession session =
+                    new ClearRequestSession(
+                            getContext(),
+                            UserHandle.getCallingUserId(),
+                            callback,
+                            request,
+                            callingPackage);
+
+            // Initiate all provider sessions
+            // TODO: Determine if provider needs to have clear capability in their manifest
+            List<ProviderSession> providerSessions =
+                    initiateProviderSessions(session, List.of());
+
+            if (providerSessions.isEmpty()) {
+                try {
+                    // TODO("Replace with properly defined error type")
+                    callback.onError("unknown_type",
+                            "No providers available to fulfill request.");
+                } catch (RemoteException e) {
+                    Log.i(TAG, "Issue invoking onError on IClearCredentialStateCallback "
+                            + "callback: " + e.getMessage());
+                }
+            }
+
+            // Iterate over all provider sessions and invoke the request
+            providerSessions.forEach(
+                    providerClearSession -> {
+                        providerClearSession
+                                .getRemoteCredentialService()
+                                .onClearCredentialState(
+                                        (android.service.credentials.ClearCredentialStateRequest)
+                                                providerClearSession.getProviderRequest(),
+                                        /* callback= */ providerClearSession);
+                    });
             return cancelTransport;
         }
     }
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
index 08fdeed..c03d505 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerServiceImpl.java
@@ -76,7 +76,7 @@
     @GuardedBy("mLock")
     public ProviderSession initiateProviderSessionForRequestLocked(
             RequestSession requestSession, List<String> requestOptions) {
-        if (!isServiceCapableLocked(requestOptions)) {
+        if (!requestOptions.isEmpty() && !isServiceCapableLocked(requestOptions)) {
             Log.i(TAG, "Service is not capable");
             return null;
         }
@@ -88,9 +88,7 @@
         }
         final RemoteCredentialService remoteService = new RemoteCredentialService(
                 getContext(), mInfo.getServiceInfo().getComponentName(), mUserId);
-        ProviderSession providerSession =
-                requestSession.initiateProviderSession(mInfo, remoteService);
-        return providerSession;
+        return requestSession.initiateProviderSession(mInfo, remoteService);
     }
 
     /** Return true if at least one capability found. */
diff --git a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
new file mode 100644
index 0000000..020552a
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.credentials;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.credentials.ClearCredentialStateException;
+import android.credentials.ui.ProviderData;
+import android.credentials.ui.ProviderPendingIntentResponse;
+import android.service.credentials.CallingAppInfo;
+import android.service.credentials.ClearCredentialStateRequest;
+import android.service.credentials.CredentialProviderInfo;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+/**
+ * Central provider session that listens for provider callbacks, and maintains provider state.
+ *
+ * @hide
+ */
+public final class ProviderClearSession extends ProviderSession<ClearCredentialStateRequest,
+        Void>
+        implements
+        RemoteCredentialService.ProviderCallbacks<Void> {
+    private static final String TAG = "ProviderClearSession";
+
+    private ClearCredentialStateException mProviderException;
+
+    /** Creates a new provider session to be used by the request session. */
+    @Nullable public static ProviderClearSession createNewSession(
+            Context context,
+            @UserIdInt int userId,
+            CredentialProviderInfo providerInfo,
+            ClearRequestSession clearRequestSession,
+            RemoteCredentialService remoteCredentialService) {
+        ClearCredentialStateRequest providerRequest =
+                createProviderRequest(
+                        clearRequestSession.mClientRequest,
+                        clearRequestSession.mClientCallingPackage);
+        return new ProviderClearSession(context, providerInfo, clearRequestSession, userId,
+                    remoteCredentialService, providerRequest);
+    }
+
+    @Nullable
+    private static ClearCredentialStateRequest createProviderRequest(
+            android.credentials.ClearCredentialStateRequest clientRequest,
+            String clientCallingPackage
+    ) {
+        // TODO: Determine if provider needs to declare clear capability in manifest
+        return new ClearCredentialStateRequest(
+                new CallingAppInfo(clientCallingPackage, new ArraySet<>()),
+                clientRequest.getData());
+    }
+
+    public ProviderClearSession(Context context,
+            CredentialProviderInfo info,
+            ProviderInternalCallback callbacks,
+            int userId, RemoteCredentialService remoteCredentialService,
+            ClearCredentialStateRequest providerRequest) {
+        super(context, info, providerRequest, callbacks, userId, remoteCredentialService);
+        setStatus(Status.PENDING);
+    }
+
+    @Override
+    public void onProviderResponseSuccess(@Nullable Void response) {
+        Log.i(TAG, "in onProviderResponseSuccess");
+        mProviderResponseSet = true;
+        updateStatusAndInvokeCallback(Status.COMPLETE);
+    }
+
+    /** Called when the provider response resulted in a failure. */
+    @Override // Callback from the remote provider
+    public void onProviderResponseFailure(int errorCode, Exception exception) {
+        if (exception instanceof ClearCredentialStateException) {
+            mProviderException = (ClearCredentialStateException) exception;
+        }
+        updateStatusAndInvokeCallback(toStatus(errorCode));
+    }
+
+    /** Called when provider service dies. */
+    @Override // Callback from the remote provider
+    public void onProviderServiceDied(RemoteCredentialService service) {
+        if (service.getComponentName().equals(mProviderInfo.getServiceInfo().getComponentName())) {
+            updateStatusAndInvokeCallback(Status.SERVICE_DEAD);
+        } else {
+            Slog.i(TAG, "Component names different in onProviderServiceDied - "
+                    + "this should not happen");
+        }
+    }
+
+    @Nullable
+    @Override
+    protected ProviderData prepareUiData() {
+        //Not applicable for clearCredential as response is not picked by the user
+        return null;
+    }
+
+    @Override
+    protected void onUiEntrySelected(String entryType, String entryId,
+            ProviderPendingIntentResponse providerPendingIntentResponse) {
+        //Not applicable for clearCredential as response is not picked by the user
+    }
+}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index 7ecae9d..93e816a 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -51,6 +51,7 @@
     @Nullable protected Credential mFinalCredentialResponse;
     @NonNull protected final T mProviderRequest;
     @Nullable protected R mProviderResponse;
+    @NonNull protected Boolean mProviderResponseSet = false;
     @Nullable protected Pair<String, CredentialEntry> mUiRemoteEntry;
 
 
@@ -84,7 +85,8 @@
      */
     public static boolean isCompletionStatus(Status status) {
         return status == Status.CREDENTIAL_RECEIVED_FROM_INTENT
-                || status == Status.CREDENTIAL_RECEIVED_FROM_SELECTION;
+                || status == Status.CREDENTIAL_RECEIVED_FROM_SELECTION
+                || status == Status.COMPLETE;
     }
 
     /**
@@ -130,7 +132,8 @@
         CREDENTIAL_RECEIVED_FROM_INTENT,
         PENDING_INTENT_INVOKED,
         CREDENTIAL_RECEIVED_FROM_SELECTION,
-        SAVE_ENTRIES_RECEIVED, CANCELED
+        SAVE_ENTRIES_RECEIVED, CANCELED,
+        COMPLETE
     }
 
     /** Converts exception to a provider session status. */
@@ -183,6 +186,11 @@
         return mProviderRequest;
     }
 
+    /** Returns whether the provider response is set. */
+    protected Boolean isProviderResponseSet() {
+        return mProviderResponse != null || mProviderResponseSet;
+    }
+
     /** Update the response state stored with the provider session. */
     @Nullable protected R getProviderResponse() {
         return mProviderResponse;
diff --git a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
index 6049fd9..8cad6ac 100644
--- a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
+++ b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
@@ -21,6 +21,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.credentials.ClearCredentialStateException;
 import android.credentials.CreateCredentialException;
 import android.credentials.GetCredentialException;
 import android.os.Handler;
@@ -30,10 +31,12 @@
 import android.service.credentials.BeginCreateCredentialResponse;
 import android.service.credentials.BeginGetCredentialRequest;
 import android.service.credentials.BeginGetCredentialResponse;
+import android.service.credentials.ClearCredentialStateRequest;
 import android.service.credentials.CredentialProviderErrors;
 import android.service.credentials.CredentialProviderService;
 import android.service.credentials.IBeginCreateCredentialCallback;
 import android.service.credentials.IBeginGetCredentialCallback;
+import android.service.credentials.IClearCredentialStateCallback;
 import android.service.credentials.ICredentialProviderService;
 import android.text.format.DateUtils;
 import android.util.Log;
@@ -195,6 +198,53 @@
                 handleExecutionResponse(result, error, cancellationSink, callback)));
     }
 
+    /** Main entry point to be called for executing a clearCredentialState call on the remote
+     * provider service.
+     * @param request the request to be sent to the provider
+     * @param callback the callback to be used to send back the provider response to the
+     *                 {@link ProviderClearSession} class that maintains provider state
+     */
+    public void onClearCredentialState(@NonNull ClearCredentialStateRequest request,
+            ProviderCallbacks<Void> callback) {
+        Log.i(TAG, "In onClearCredentialState in RemoteCredentialService");
+        AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
+        AtomicReference<CompletableFuture<Void>> futureRef = new AtomicReference<>();
+
+        CompletableFuture<Void> connectThenExecute =
+                postAsync(service -> {
+                    CompletableFuture<Void> clearCredentialFuture =
+                            new CompletableFuture<>();
+                    ICancellationSignal cancellationSignal = service.onClearCredentialState(
+                            request, new IClearCredentialStateCallback.Stub() {
+                                @Override
+                                public void onSuccess() {
+                                    Log.i(TAG, "In onSuccess onClearCredentialState "
+                                            + "in RemoteCredentialService");
+                                    clearCredentialFuture.complete(null);
+                                }
+
+                                @Override
+                                public void onFailure(String errorType, CharSequence message) {
+                                    Log.i(TAG, "In onFailure in RemoteCredentialService");
+                                    String errorMsg = message == null ? "" :
+                                            String.valueOf(message);
+                                    clearCredentialFuture.completeExceptionally(
+                                            new ClearCredentialStateException(errorType, errorMsg));
+                                }});
+                    CompletableFuture<Void> future = futureRef.get();
+                    if (future != null && future.isCancelled()) {
+                        dispatchCancellationSignal(cancellationSignal);
+                    } else {
+                        cancellationSink.set(cancellationSignal);
+                    }
+                    return clearCredentialFuture;
+                }).orTimeout(TIMEOUT_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
+
+        futureRef.set(connectThenExecute);
+        connectThenExecute.whenComplete((result, error) -> Handler.getMain().post(() ->
+                handleExecutionResponse(result, error, cancellationSink, callback)));
+    }
+
     private <T> void handleExecutionResponse(T result,
             Throwable error,
             AtomicReference<ICancellationSignal> cancellationSink,
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index 937fac9..6d7cb4c 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -144,7 +144,7 @@
         mProviders.clear();
     }
 
-    private boolean isAnyProviderPending() {
+    boolean isAnyProviderPending() {
         for (ProviderSession session : mProviders.values()) {
             if (ProviderSession.isStatusWaitingForRemoteResponse(session.getStatus())) {
                 return true;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 4634ff5..e458145 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -34,6 +34,7 @@
 import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.FactoryResetProtectionPolicy;
+import android.app.admin.PackagePolicy;
 import android.app.admin.PasswordPolicy;
 import android.app.admin.PreferentialNetworkServiceConfig;
 import android.app.admin.WifiSsidPolicy;
@@ -157,6 +158,9 @@
     private static final String TAG_SSID_ALLOWLIST = "ssid-allowlist";
     private static final String TAG_SSID_DENYLIST = "ssid-denylist";
     private static final String TAG_SSID = "ssid";
+    private static final String TAG_CROSS_PROFILE_CALLER_ID_POLICY = "caller-id-policy";
+    private static final String TAG_CROSS_PROFILE_CONTACTS_SEARCH_POLICY = "contacts-policy";
+    private static final String TAG_PACKAGE_POLICY_PACKAGE_NAMES = "package-policy-packages";
     private static final String TAG_PREFERENTIAL_NETWORK_SERVICE_CONFIGS =
             "preferential_network_service_configs";
     private static final String TAG_PREFERENTIAL_NETWORK_SERVICE_CONFIG =
@@ -167,6 +171,8 @@
     private static final String ATTR_VALUE = "value";
     private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
     private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
+    private static final String ATTR_PACKAGE_POLICY_MODE = "package-policy-type";
+
 
     DeviceAdminInfo info;
 
@@ -315,6 +321,12 @@
     // Time by which the profile should be turned on according to System.currentTimeMillis().
     long mProfileOffDeadline = 0;
 
+    // The package policy for Cross Profile Contacts Search
+    PackagePolicy mManagedProfileCallerIdAccess = null;
+
+    // The package policy for Cross Profile Contacts Search
+    PackagePolicy mManagedProfileContactsAccess = null;
+
     public String mAlwaysOnVpnPackage;
     public boolean mAlwaysOnVpnLockdown;
     boolean mCommonCriteriaMode;
@@ -626,6 +638,22 @@
         if (mtePolicy != DevicePolicyManager.MTE_NOT_CONTROLLED_BY_POLICY) {
             writeAttributeValueToXml(out, TAG_MTE_POLICY, mtePolicy);
         }
+        writePackagePolicy(out, TAG_CROSS_PROFILE_CALLER_ID_POLICY,
+                mManagedProfileCallerIdAccess);
+        writePackagePolicy(out, TAG_CROSS_PROFILE_CONTACTS_SEARCH_POLICY,
+                mManagedProfileContactsAccess);
+    }
+
+    private void writePackagePolicy(TypedXmlSerializer out, String tag,
+            PackagePolicy packagePolicy) throws IOException {
+        if (packagePolicy == null) {
+            return;
+        }
+        out.startTag(null, tag);
+        out.attributeInt(null, ATTR_PACKAGE_POLICY_MODE, packagePolicy.getPolicyType());
+        writePackageListToXml(out, TAG_PACKAGE_POLICY_PACKAGE_NAMES,
+                new ArrayList<>(packagePolicy.getPackageNames()));
+        out.endTag(null, tag);
     }
 
     private List<String> ssidsToStrings(Set<WifiSsid> ssids) {
@@ -914,6 +942,10 @@
                 }
             } else if (TAG_MTE_POLICY.equals(tag)) {
                 mtePolicy = parser.getAttributeInt(null, ATTR_VALUE);
+            } else if (TAG_CROSS_PROFILE_CALLER_ID_POLICY.equals(tag)) {
+                mManagedProfileCallerIdAccess = readPackagePolicy(parser);
+            } else if (TAG_CROSS_PROFILE_CONTACTS_SEARCH_POLICY.equals(tag)) {
+                mManagedProfileContactsAccess = readPackagePolicy(parser);
             } else {
                 Slogf.w(LOG_TAG, "Unknown admin tag: %s", tag);
                 XmlUtils.skipCurrentTag(parser);
@@ -921,6 +953,14 @@
         }
     }
 
+    private PackagePolicy readPackagePolicy(TypedXmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        int policy = parser.getAttributeInt(null, ATTR_PACKAGE_POLICY_MODE);
+        Set<String> packageNames = new ArraySet<>(
+                readPackageList(parser, TAG_PACKAGE_POLICY_PACKAGE_NAMES));
+        return new PackagePolicy(policy, packageNames);
+    }
+
     private List<WifiSsid> readWifiSsids(TypedXmlPullParser parser, String tag)
             throws XmlPullParserException, IOException {
         List<String> ssidStrings = new ArrayList<>();
@@ -1106,6 +1146,21 @@
                 key -> UserRestrictionsUtils.isGlobal(adminType, key));
     }
 
+    void dumpPackagePolicy(IndentingPrintWriter pw, String name, PackagePolicy policy) {
+        pw.print(name);
+        pw.println(":");
+        if (policy != null) {
+            pw.increaseIndent();
+            pw.print("policyType=");
+            pw.println(policy.getPolicyType());
+            pw.println("packageNames:");
+            pw.increaseIndent();
+            policy.getPackageNames().forEach(item -> pw.println(item));
+            pw.decreaseIndent();
+            pw.decreaseIndent();
+        }
+    }
+
     void dump(IndentingPrintWriter pw) {
         pw.print("uid=");
         pw.println(getUid());
@@ -1258,6 +1313,13 @@
         pw.print("defaultEnabledRestrictionsAlreadySet=");
         pw.println(defaultEnabledRestrictionsAlreadySet);
 
+
+        dumpPackagePolicy(pw, "managedProfileCallerIdPolicy",
+                mManagedProfileCallerIdAccess);
+
+        dumpPackagePolicy(pw, "managedProfileContactsPolicy",
+                mManagedProfileContactsAccess);
+
         pw.print("isParent=");
         pw.println(isParent);
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 667f41f..710002004 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -212,6 +212,7 @@
 import android.app.admin.FullyManagedDeviceProvisioningParams;
 import android.app.admin.ManagedProfileProvisioningParams;
 import android.app.admin.NetworkEvent;
+import android.app.admin.PackagePolicy;
 import android.app.admin.ParcelableGranteeMap;
 import android.app.admin.ParcelableResource;
 import android.app.admin.PasswordMetrics;
@@ -759,6 +760,12 @@
     private EnterpriseSpecificIdCalculator mEsidCalculator;
 
     /**
+     * Contains the list of OEM Default Role Holders for Contact-related roles
+     * (DIALER, SMS, SYSTEM_CONTACTS)
+     */
+    private final Set<String> mContactSystemRoleHolders;
+
+    /**
      * Contains (package-user) pairs to remove. An entry (p, u) implies that removal of package p
      * is requested for user u.
      */
@@ -1907,6 +1914,7 @@
         if (!mHasFeature) {
             // Skip the rest of the initialization
             mSetupContentObserver = null;
+            mContactSystemRoleHolders = Collections.emptySet();
             return;
         }
 
@@ -1952,10 +1960,50 @@
             mDevicePolicyEngine.load();
         }
 
+        mContactSystemRoleHolders = fetchOemSystemHolders(/* roleResIds...= */
+                com.android.internal.R.string.config_defaultSms,
+                com.android.internal.R.string.config_defaultDialer,
+                com.android.internal.R.string.config_systemContacts
+        );
+
         // The binder caches are not enabled until the first invalidation.
         invalidateBinderCaches();
     }
 
+    /**
+     * Fetch the OEM System Holders for the supplied roleNames
+     *
+     * @param roleResIds the list of resource ids whose role holders are needed
+     * @return the set of packageNames that handle the requested roles
+     */
+    private @NonNull Set<String> fetchOemSystemHolders(int... roleResIds) {
+        Set<String> packageNames = new ArraySet<>();
+
+        for (int roleResId : roleResIds) {
+            String packageName = getDefaultRoleHolderPackageName(roleResId);
+            if (packageName != null) {
+                packageNames.add(packageName);
+            }
+        }
+
+        return Collections.unmodifiableSet(packageNames);
+    }
+
+
+    private @Nullable String getDefaultRoleHolderPackageName(int resId) {
+        String packageNameAndSignature = mContext.getString(resId);
+
+        if (TextUtils.isEmpty(packageNameAndSignature)) {
+            return null;
+        }
+
+        if (packageNameAndSignature.contains(":")) {
+            return packageNameAndSignature.split(":")[0];
+        }
+
+        return packageNameAndSignature;
+    }
+
     private Owners makeOwners(Injector injector, PolicyPathProvider pathProvider) {
         return new Owners(
                 injector.getUserManager(), injector.getUserManagerInternal(),
@@ -12103,10 +12151,14 @@
 
         synchronized (getLockObject()) {
             ActiveAdmin admin = getProfileOwnerLocked(caller.getUserId());
-            if (admin.disableCallerId != disabled) {
-                admin.disableCallerId = disabled;
-                saveSettingsLocked(caller.getUserId());
+            if (disabled) {
+                admin.mManagedProfileCallerIdAccess =
+                        new PackagePolicy(PackagePolicy.PACKAGE_POLICY_ALLOWLIST);
+            } else {
+                admin.mManagedProfileCallerIdAccess =
+                        new PackagePolicy(PackagePolicy.PACKAGE_POLICY_BLOCKLIST);
             }
+            saveSettingsLocked(caller.getUserId());
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_CROSS_PROFILE_CALLER_ID_DISABLED)
@@ -12126,7 +12178,21 @@
 
         synchronized (getLockObject()) {
             ActiveAdmin admin = getProfileOwnerLocked(caller.getUserId());
-            return admin.disableCallerId;
+            if (admin == null) {
+                return false;
+            }
+
+            if (admin.mManagedProfileCallerIdAccess == null) {
+                return admin.disableCallerId;
+            }
+
+            if (admin.mManagedProfileCallerIdAccess.getPolicyType()
+                    == PackagePolicy.PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM) {
+                Slogf.w(LOG_TAG, "Denying callerId due to PACKAGE_POLICY_SYSTEM policyType");
+            }
+
+            return admin.mManagedProfileCallerIdAccess.getPolicyType()
+                    != PackagePolicy.PACKAGE_POLICY_BLOCKLIST;
         }
     }
 
@@ -12139,11 +12205,124 @@
 
         synchronized (getLockObject()) {
             ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
-            return (admin != null) ? admin.disableCallerId : false;
+            if (admin == null) {
+                return false;
+            }
+
+            if (admin.mManagedProfileCallerIdAccess == null) {
+                return admin.disableCallerId;
+            }
+
+            return admin.mManagedProfileCallerIdAccess.getPolicyType()
+                    == PackagePolicy.PACKAGE_POLICY_ALLOWLIST;
         }
     }
 
     @Override
+    public void setManagedProfileCallerIdAccessPolicy(PackagePolicy policy) {
+        if (!mHasFeature) {
+            return;
+        }
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization((isProfileOwner(caller)
+                && isManagedProfile(caller.getUserId())));
+        synchronized (getLockObject()) {
+            ActiveAdmin admin = getProfileOwnerLocked(caller.getUserId());
+            admin.disableCallerId = false;
+            admin.mManagedProfileCallerIdAccess = policy;
+            saveSettingsLocked(caller.getUserId());
+        }
+    }
+
+    @Override
+    public PackagePolicy getManagedProfileCallerIdAccessPolicy() {
+        if (!mHasFeature) {
+            return null;
+        }
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization((isProfileOwner(caller)
+                && isManagedProfile(caller.getUserId())));
+        synchronized (getLockObject()) {
+            ActiveAdmin admin = getProfileOwnerLocked(caller.getUserId());
+            return (admin != null) ? admin.mManagedProfileCallerIdAccess : null;
+        }
+    }
+
+    @Override
+    public boolean hasManagedProfileCallerIdAccess(int userId, String packageName) {
+        Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
+
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasCrossUsersPermission(caller, userId));
+
+        synchronized (getLockObject()) {
+            ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
+            if (admin != null) {
+                if (admin.mManagedProfileCallerIdAccess == null) {
+                    return !admin.disableCallerId;
+                }
+                return admin.mManagedProfileCallerIdAccess.isPackageAllowed(packageName,
+                        mContactSystemRoleHolders);
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void setManagedProfileContactsAccessPolicy(PackagePolicy policy) {
+        if (!mHasFeature) {
+            return;
+        }
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization((isProfileOwner(caller)
+                && isManagedProfile(caller.getUserId())));
+
+        synchronized (getLockObject()) {
+            ActiveAdmin admin = getProfileOwnerLocked(caller.getUserId());
+            admin.disableContactsSearch = false;
+            admin.mManagedProfileContactsAccess = policy;
+            saveSettingsLocked(caller.getUserId());
+        }
+    }
+
+    @Override
+    public PackagePolicy getManagedProfileContactsAccessPolicy() {
+        if (!mHasFeature) {
+            return null;
+        }
+
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization((isProfileOwner(caller)
+                && isManagedProfile(caller.getUserId())));
+
+        synchronized (getLockObject()) {
+            ActiveAdmin admin = getProfileOwnerLocked(caller.getUserId());
+            return (admin != null) ? admin.mManagedProfileContactsAccess : null;
+        }
+    }
+
+    @Override
+    public boolean hasManagedProfileContactsAccess(int userId, String packageName) {
+        Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
+
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasCrossUsersPermission(caller, userId));
+
+        synchronized (getLockObject()) {
+            ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
+            if (admin != null) {
+                if (admin.mManagedProfileContactsAccess == null) {
+                    return !admin.disableContactsSearch;
+                }
+
+                return admin.mManagedProfileContactsAccess.isPackageAllowed(packageName,
+                        mContactSystemRoleHolders);
+            }
+        }
+        return true;
+    }
+
+    @Override
     public void setCrossProfileContactsSearchDisabled(ComponentName who, boolean disabled) {
         if (!mHasFeature) {
             return;
@@ -12154,10 +12333,14 @@
 
         synchronized (getLockObject()) {
             ActiveAdmin admin = getProfileOwnerLocked(caller.getUserId());
-            if (admin.disableContactsSearch != disabled) {
-                admin.disableContactsSearch = disabled;
-                saveSettingsLocked(caller.getUserId());
+            if (disabled) {
+                admin.mManagedProfileContactsAccess =
+                        new PackagePolicy(PackagePolicy.PACKAGE_POLICY_ALLOWLIST);
+            } else {
+                admin.mManagedProfileContactsAccess =
+                        new PackagePolicy(PackagePolicy.PACKAGE_POLICY_BLOCKLIST);
             }
+            saveSettingsLocked(caller.getUserId());
         }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_CROSS_PROFILE_CONTACTS_SEARCH_DISABLED)
@@ -12177,7 +12360,14 @@
 
         synchronized (getLockObject()) {
             ActiveAdmin admin = getProfileOwnerLocked(caller.getUserId());
-            return admin.disableContactsSearch;
+            if (admin == null) {
+                return false;
+            }
+            if (admin.mManagedProfileContactsAccess == null) {
+                return admin.disableContactsSearch;
+            }
+            return admin.mManagedProfileContactsAccess.getPolicyType()
+                    != PackagePolicy.PACKAGE_POLICY_BLOCKLIST;
         }
     }
 
@@ -12190,7 +12380,19 @@
 
         synchronized (getLockObject()) {
             ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
-            return (admin != null) ? admin.disableContactsSearch : false;
+            if (admin == null) {
+                return false;
+            }
+
+            if (admin.mManagedProfileContactsAccess == null) {
+                return admin.disableContactsSearch;
+            }
+            if (admin.mManagedProfileContactsAccess.getPolicyType()
+                    == PackagePolicy.PACKAGE_POLICY_ALLOWLIST_AND_SYSTEM) {
+                Slogf.w(LOG_TAG, "Denying contacts due to PACKAGE_POLICY_SYSTEM policyType");
+            }
+            return admin.mManagedProfileContactsAccess.getPolicyType()
+                    != PackagePolicy.PACKAGE_POLICY_BLOCKLIST;
         }
     }
 
diff --git a/services/permission/java/com/android/server/permission/access/collection/IndexedList.kt b/services/permission/java/com/android/server/permission/access/collection/IndexedList.kt
index c4d07fe..f4ecceb 100644
--- a/services/permission/java/com/android/server/permission/access/collection/IndexedList.kt
+++ b/services/permission/java/com/android/server/permission/access/collection/IndexedList.kt
@@ -99,3 +99,10 @@
     }
     return isChanged
 }
+
+inline fun <T, R> IndexedList<T>.mapNotNullIndexed(transform: (T) -> R?): IndexedList<R> =
+    IndexedList<R>().also { destination ->
+        forEachIndexed { _, element ->
+            transform(element)?.let { destination += it }
+        }
+    }
diff --git a/services/permission/java/com/android/server/permission/access/permission/Permission.kt b/services/permission/java/com/android/server/permission/access/permission/Permission.kt
index 88989c4..35f00a7 100644
--- a/services/permission/java/com/android/server/permission/access/permission/Permission.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/Permission.kt
@@ -46,83 +46,86 @@
         @Suppress("DEPRECATION")
         get() = permissionInfo.protectionLevel
 
+    inline val protection: Int
+        get() = permissionInfo.protection
+
     inline val isInternal: Boolean
-        get() = permissionInfo.protection == PermissionInfo.PROTECTION_INTERNAL
+        get() = protection == PermissionInfo.PROTECTION_INTERNAL
 
     inline val isNormal: Boolean
-        get() = permissionInfo.protection == PermissionInfo.PROTECTION_NORMAL
+        get() = protection == PermissionInfo.PROTECTION_NORMAL
 
     inline val isRuntime: Boolean
-        get() = permissionInfo.protection == PermissionInfo.PROTECTION_DANGEROUS
+        get() = protection == PermissionInfo.PROTECTION_DANGEROUS
 
     inline val isSignature: Boolean
-        get() = permissionInfo.protection == PermissionInfo.PROTECTION_SIGNATURE
+        get() = protection == PermissionInfo.PROTECTION_SIGNATURE
+
+    inline val protectionFlags: Int
+        get() = permissionInfo.protectionFlags
 
     inline val isAppOp: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_APPOP)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_APPOP)
 
     inline val isAppPredictor: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR)
 
     inline val isCompanion: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_COMPANION)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_COMPANION)
 
     inline val isConfigurator: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_CONFIGURATOR)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_CONFIGURATOR)
 
     inline val isDevelopment: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_DEVELOPMENT)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_DEVELOPMENT)
 
     inline val isIncidentReportApprover: Boolean
-        get() = permissionInfo.protectionFlags
-            .hasBits(PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER)
 
     inline val isInstaller: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_INSTALLER)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_INSTALLER)
 
     inline val isInstant: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_INSTANT)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_INSTANT)
 
     inline val isKnownSigner: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_KNOWN_SIGNER)
 
     inline val isOem: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_OEM)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_OEM)
 
     inline val isPre23: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_PRE23)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_PRE23)
 
     inline val isPreInstalled: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_PREINSTALLED)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_PREINSTALLED)
 
     inline val isPrivileged: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_PRIVILEGED)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_PRIVILEGED)
 
     inline val isRecents: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_RECENTS)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_RECENTS)
 
     inline val isRetailDemo: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO)
 
     inline val isRole: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_ROLE)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_ROLE)
 
     inline val isRuntimeOnly: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY)
 
     inline val isSetup: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_SETUP)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_SETUP)
 
     inline val isSystemTextClassifier: Boolean
-        get() = permissionInfo.protectionFlags
-            .hasBits(PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER)
 
     inline val isVendorPrivileged: Boolean
-        get() = permissionInfo.protectionFlags
-            .hasBits(PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED)
 
     inline val isVerifier: Boolean
-        get() = permissionInfo.protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_VERIFIER)
+        get() = protectionFlags.hasBits(PermissionInfo.PROTECTION_FLAG_VERIFIER)
 
     inline val isHardRestricted: Boolean
         get() = permissionInfo.flags.hasBits(PermissionInfo.FLAG_HARD_RESTRICTED)
@@ -133,12 +136,23 @@
     inline val isSoftRestricted: Boolean
         get() = permissionInfo.flags.hasBits(PermissionInfo.FLAG_SOFT_RESTRICTED)
 
+    inline val isHardOrSoftRestricted: Boolean
+        get() = permissionInfo.flags.hasBits(
+            PermissionInfo.FLAG_HARD_RESTRICTED or PermissionInfo.FLAG_SOFT_RESTRICTED
+        )
+
+    inline val isImmutablyRestricted: Boolean
+        get() = permissionInfo.flags.hasBits(PermissionInfo.FLAG_IMMUTABLY_RESTRICTED)
+
     inline val knownCerts: Set<String>
         get() = permissionInfo.knownCerts
 
     inline val hasGids: Boolean
         get() = gids.isNotEmpty()
 
+    inline val footprint: Int
+        get() = name.length + permissionInfo.calculateFootprint()
+
     fun getGidsForUser(userId: Int): IntArray =
         if (areGidsPerUser) {
             IntArray(gids.size) { i -> UserHandle.getUid(userId, gids[i]) }
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 7117501..e2c2c49 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -40,6 +40,7 @@
 import android.os.UserHandle
 import android.os.UserManager
 import android.permission.IOnPermissionsChangeListener
+import android.permission.PermissionControllerManager
 import android.permission.PermissionManager
 import android.provider.Settings
 import android.util.DebugUtils
@@ -48,10 +49,12 @@
 import com.android.internal.compat.IPlatformCompat
 import com.android.internal.logging.MetricsLogger
 import com.android.internal.logging.nano.MetricsProto
+import com.android.internal.util.DumpUtils
 import com.android.internal.util.Preconditions
 import com.android.server.FgThread
 import com.android.server.LocalManagerRegistry
 import com.android.server.LocalServices
+import com.android.server.PermissionThread
 import com.android.server.ServiceThread
 import com.android.server.SystemConfig
 import com.android.server.permission.access.AccessCheckingService
@@ -80,6 +83,10 @@
 import libcore.util.EmptyArray
 import java.io.FileDescriptor
 import java.io.PrintWriter
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.TimeoutException
 
 /**
  * Modern implementation of [PermissionManagerServiceInterface].
@@ -106,6 +113,17 @@
 
     private val mountedStorageVolumes = IndexedSet<String?>()
 
+    private lateinit var permissionControllerManager: PermissionControllerManager
+
+    /**
+     * A permission backup might contain apps that are not installed. In this case we delay the
+     * restoration until the app is installed.
+     *
+     * This array (`userId -> noDelayedBackupLeft`) is `true` for all the users where
+     * there is **no more** delayed backup left.
+     */
+    private val isDelayedPermissionBackupFinished = IntBooleanMap()
+
     fun initialize() {
         metricsLogger = MetricsLogger()
         packageManagerInternal = LocalServices.getService(PackageManagerInternal::class.java)
@@ -161,9 +179,7 @@
                 with(policy) { getPermissionGroups()[permissionGroupName] }
             } ?: return null
 
-            val isPermissionGroupVisible =
-                snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)
-            if (!isPermissionGroupVisible) {
+            if (!snapshot.isPackageVisibleToUid(permissionGroup.packageName, callingUid)) {
                 return null
             }
         }
@@ -199,9 +215,7 @@
                 with(policy) { getPermissions()[permissionName] }
             } ?: return null
 
-            val isPermissionVisible =
-                snapshot.isPackageVisibleToUid(permission.packageName, callingUid)
-            if (!isPermissionVisible) {
+            if (!snapshot.isPackageVisibleToUid(permission.packageName, callingUid)) {
                 return null
             }
 
@@ -274,12 +288,30 @@
         }
     }
 
-    override fun getAllPermissionsWithProtection(protection: Int): List<PermissionInfo> {
-        TODO("Not yet implemented")
-    }
+    override fun getAllPermissionsWithProtection(protection: Int): List<PermissionInfo> =
+        getPermissionsWithProtectionOrProtectionFlags { permission ->
+            permission.protection == protection
+        }
 
-    override fun getAllPermissionsWithProtectionFlags(protectionFlags: Int): List<PermissionInfo> {
-        TODO("Not yet implemented")
+    override fun getAllPermissionsWithProtectionFlags(protectionFlags: Int): List<PermissionInfo> =
+        getPermissionsWithProtectionOrProtectionFlags { permission ->
+            permission.protectionFlags.hasBits(protectionFlags)
+        }
+
+    private inline fun getPermissionsWithProtectionOrProtectionFlags(
+        predicate: (Permission) -> Boolean
+    ): List<PermissionInfo> {
+        service.getState {
+            with(policy) {
+                return getPermissions().mapNotNullIndexed { _, _, permission ->
+                    if (predicate(permission)) {
+                        permission.generatePermissionInfo(0)
+                    } else {
+                        null
+                    }
+                }
+            }
+        }
     }
 
     override fun getPermissionGids(permissionName: String, userId: Int): IntArray {
@@ -290,11 +322,100 @@
     }
 
     override fun addPermission(permissionInfo: PermissionInfo, async: Boolean): Boolean {
-        TODO("Not yet implemented")
+        val permissionName = permissionInfo.name
+        requireNotNull(permissionName) { "permissionName cannot be null" }
+        val callingUid = Binder.getCallingUid()
+        if (packageManagerLocal.withUnfilteredSnapshot().use { it.isUidInstantApp(callingUid) }) {
+            throw SecurityException("Instant apps cannot add permissions")
+        }
+        if (permissionInfo.labelRes == 0 && permissionInfo.nonLocalizedLabel == null) {
+            throw SecurityException("Label must be specified in permission")
+        }
+        val oldPermission: Permission?
+
+        service.mutateState {
+            val permissionTree = getAndEnforcePermissionTree(permissionName)
+            enforcePermissionTreeSize(permissionInfo, permissionTree)
+
+            oldPermission = with(policy) { getPermissions()[permissionName] }
+            if (oldPermission != null && !oldPermission.isDynamic) {
+                throw SecurityException(
+                    "Not allowed to modify non-dynamic permission $permissionName"
+                )
+            }
+
+            permissionInfo.packageName = permissionTree.permissionInfo.packageName
+            @Suppress("DEPRECATION")
+            permissionInfo.protectionLevel =
+                PermissionInfo.fixProtectionLevel(permissionInfo.protectionLevel)
+
+            val newPermission = Permission(
+                permissionInfo, true, Permission.TYPE_DYNAMIC, permissionTree.appId
+            )
+
+            with(policy) { addPermission(newPermission, !async) }
+        }
+
+        return oldPermission == null
     }
 
     override fun removePermission(permissionName: String) {
-        TODO("Not yet implemented")
+        val callingUid = Binder.getCallingUid()
+        if (packageManagerLocal.withUnfilteredSnapshot().use { it.isUidInstantApp(callingUid) }) {
+            throw SecurityException("Instant applications don't have access to this method")
+        }
+        service.mutateState {
+            getAndEnforcePermissionTree(permissionName)
+            val permission = with(policy) { getPermissions()[permissionName] } ?: return@mutateState
+
+            if (!permission.isDynamic) {
+                // TODO(b/67371907): switch to logging if it fails
+                throw SecurityException(
+                    "Not allowed to modify non-dynamic permission $permissionName"
+                )
+            }
+
+            with(policy) { removePermission(permission) }
+        }
+    }
+    private fun GetStateScope.getAndEnforcePermissionTree(permissionName: String): Permission {
+        val callingUid = Binder.getCallingUid()
+        val permissionTree = with(policy) { findPermissionTree(permissionName) }
+        if (permissionTree != null && permissionTree.appId == UserHandle.getAppId(callingUid)) {
+                return permissionTree
+        }
+
+        throw SecurityException(
+            "Calling UID $callingUid is not allowed to add to or remove from the permission tree"
+        )
+    }
+
+    private fun GetStateScope.enforcePermissionTreeSize(
+        permissionInfo: PermissionInfo,
+        permissionTree: Permission
+    ) {
+        // We calculate the max size of permissions defined by this uid and throw
+        // if that plus the size of 'info' would exceed our stated maximum.
+        if (permissionTree.appId != Process.SYSTEM_UID) {
+            val permissionTreeFootprint = calculatePermissionTreeFootprint(permissionTree)
+            if (permissionTreeFootprint + permissionInfo.calculateFootprint() >
+                MAX_PERMISSION_TREE_FOOTPRINT
+            ) {
+                throw SecurityException("Permission tree size cap exceeded")
+            }
+        }
+    }
+
+    private fun GetStateScope.calculatePermissionTreeFootprint(permissionTree: Permission): Int {
+        var size = 0
+        with(policy) {
+            getPermissions().forEachValueIndexed { _, permission ->
+                if (permissionTree.appId == permission.appId) {
+                    size += permission.footprint
+                }
+            }
+        }
+        return size
     }
 
     override fun checkUidPermission(uid: Int, permissionName: String): Int {
@@ -1099,30 +1220,300 @@
         with(policy) { setPermissionFlags(appId, userId, permissionName, newFlags) }
     }
 
+    override fun getAllowlistedRestrictedPermissions(
+        packageName: String,
+        allowlistedFlags: Int,
+        userId: Int
+    ): IndexedList<String>? {
+        requireNotNull(packageName) { "packageName cannot be null" }
+        Preconditions.checkFlagsArgument(allowlistedFlags, PERMISSION_ALLOWLIST_MASK)
+        Preconditions.checkArgumentNonnegative(userId, "userId cannot be null")
+
+        enforceCallingOrSelfCrossUserPermission(
+            userId, enforceFullPermission = false, enforceShellRestriction = false,
+            "getAllowlistedRestrictedPermissions"
+        )
+
+        if (!userManagerInternal.exists(userId)) {
+            Log.w(LOG_TAG, "AllowlistedRestrictedPermission api: Unknown user $userId")
+            return null
+        }
+
+        val callingUid = Binder.getCallingUid()
+        val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId)
+            .use { it.getPackageState(packageName) } ?: return null
+        val androidPackage = packageState.androidPackage ?: return null
+
+        val isCallerPrivileged = context.checkCallingOrSelfPermission(
+            Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+        ) == PackageManager.PERMISSION_GRANTED
+
+        if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) &&
+            !isCallerPrivileged) {
+            throw SecurityException(
+                "Querying system allowlist requires " +
+                    Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+            )
+        }
+
+        val isCallerInstallerOnRecord =
+            packageManagerInternal.isCallerInstallerOfRecord(androidPackage, callingUid)
+
+        if (allowlistedFlags.hasAnyBit(PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE or
+                PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER)) {
+            if (!isCallerPrivileged && !isCallerInstallerOnRecord) {
+                throw SecurityException(
+                    "Querying upgrade or installer allowlist requires being installer on record" +
+                        " or ${Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS}"
+                )
+            }
+        }
+
+        return getAllowlistedRestrictedPermissionsUnchecked(
+            packageState.appId, allowlistedFlags, userId
+        )
+    }
+
+    /**
+     * This method does not enforce checks on the caller, should only be called after
+     * required checks.
+     */
+    private fun getAllowlistedRestrictedPermissionsUnchecked(
+        appId: Int,
+        allowlistedFlags: Int,
+        userId: Int
+    ): IndexedList<String>? {
+        val permissionFlags = service.getState {
+            with(policy) { getUidPermissionFlags(appId, userId) }
+        } ?: return null
+
+        var queryFlags = 0
+        if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM)) {
+            queryFlags = queryFlags or PermissionFlags.SYSTEM_EXEMPT
+        }
+        if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE)) {
+            queryFlags = queryFlags or PermissionFlags.UPGRADE_EXEMPT
+        }
+        if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER)) {
+            queryFlags = queryFlags or PermissionFlags.INSTALLER_EXEMPT
+        }
+
+        return permissionFlags.mapNotNullIndexed { _, permissionName, flags ->
+            if (flags.hasAnyBit(queryFlags)) permissionName else null
+        }
+    }
+
     override fun addAllowlistedRestrictedPermission(
         packageName: String,
         permissionName: String,
-        flags: Int,
+        allowlistedFlags: Int,
         userId: Int
     ): Boolean {
-        TODO("Not yet implemented")
-    }
+        requireNotNull(permissionName) { "permissionName cannot be null" }
+        if (!enforceRestrictedPermission(permissionName)) {
+            return false
+        }
 
-    override fun getAllowlistedRestrictedPermissions(
-        packageName: String,
-        flags: Int,
-        userId: Int
-    ): MutableList<String> {
-        TODO("Not yet implemented")
+        val permissionNames = getAllowlistedRestrictedPermissions(
+            packageName, allowlistedFlags, userId
+        ) ?: IndexedList(1)
+
+        if (permissionName !in permissionNames) {
+            permissionNames += permissionName
+            return setAllowlistedRestrictedPermissions(
+                packageName, permissionNames, allowlistedFlags, userId, isAddingPermission = true
+            )
+        }
+        return false
     }
 
     override fun removeAllowlistedRestrictedPermission(
         packageName: String,
         permissionName: String,
-        flags: Int,
+        allowlistedFlags: Int,
         userId: Int
     ): Boolean {
-        TODO("Not yet implemented")
+        requireNotNull(permissionName) { "permissionName cannot be null" }
+        if (!enforceRestrictedPermission(permissionName)) {
+            return false
+        }
+
+        val permissions = getAllowlistedRestrictedPermissions(
+            packageName, allowlistedFlags, userId
+        ) ?: return false
+
+        if (permissions.remove(permissionName)) {
+            return setAllowlistedRestrictedPermissions(
+                packageName, permissions, allowlistedFlags, userId, isAddingPermission = false
+            )
+        }
+
+        return false
+    }
+
+    private fun enforceRestrictedPermission(permissionName: String): Boolean {
+        val permission = service.getState { with(policy) { getPermissions()[permissionName] } }
+        if (permission == null) {
+            Log.w(LOG_TAG, "permission definition for $permissionName does not exist")
+            return false
+        }
+
+        if (packageManagerLocal.withFilteredSnapshot()
+                .use { it.getPackageState(permission.packageName) } == null) {
+            return false
+        }
+
+        val isImmutablyRestrictedPermission =
+            permission.isHardOrSoftRestricted && permission.isImmutablyRestricted
+        if (isImmutablyRestrictedPermission && context.checkCallingOrSelfPermission(
+                Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+            ) != PackageManager.PERMISSION_GRANTED) {
+            throw SecurityException(
+                "Cannot modify allowlist of an immutably restricted permission: ${permission.name}"
+            )
+        }
+
+        return true
+    }
+
+    private fun setAllowlistedRestrictedPermissions(
+        packageName: String,
+        allowlistedPermissions: List<String>,
+        allowlistedFlags: Int,
+        userId: Int,
+        isAddingPermission: Boolean
+    ): Boolean {
+        Preconditions.checkArgument(allowlistedFlags.countOneBits() == 1)
+
+        val isCallerPrivileged = context.checkCallingOrSelfPermission(
+            Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+        ) == PackageManager.PERMISSION_GRANTED
+
+        val callingUid = Binder.getCallingUid()
+        val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId)
+            .use { snapshot -> snapshot.packageStates[packageName] ?: return false }
+        val androidPackage = packageState.androidPackage ?: return false
+
+        val isCallerInstallerOnRecord =
+            packageManagerInternal.isCallerInstallerOfRecord(androidPackage, callingUid)
+
+        if (allowlistedFlags.hasBits(PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE)) {
+            if (!isCallerPrivileged && !isCallerInstallerOnRecord) {
+                throw SecurityException(
+                    "Modifying upgrade allowlist requires being installer on record or " +
+                        Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+                )
+            }
+            if (isAddingPermission && !isCallerPrivileged) {
+                throw SecurityException(
+                    "Adding to upgrade allowlist requires" +
+                        Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS
+                )
+            }
+        }
+
+        setAllowlistedRestrictedPermissionsUnchecked(
+            androidPackage, packageState.appId, allowlistedPermissions, allowlistedFlags, userId
+        )
+
+        return true
+    }
+
+    /**
+     * This method does not enforce checks on the caller, should only be called after
+     * required checks.
+     */
+    private fun setAllowlistedRestrictedPermissionsUnchecked(
+        androidPackage: AndroidPackage,
+        appId: Int,
+        allowlistedPermissions: List<String>,
+        allowlistedFlags: Int,
+        userId: Int
+    ) {
+        service.mutateState {
+            with(policy) {
+                val permissionsFlags =
+                    getUidPermissionFlags(appId, userId) ?: return@mutateState
+
+                val permissions = getPermissions()
+                androidPackage.requestedPermissions.forEachIndexed { _, requestedPermission ->
+                    val permission = permissions[requestedPermission]
+                    if (permission == null || !permission.isHardOrSoftRestricted) {
+                        return@forEachIndexed
+                    }
+
+                    val oldFlags = permissionsFlags[requestedPermission] ?: 0
+                    val wasGranted = PermissionFlags.isPermissionGranted(oldFlags)
+
+                    var newFlags = oldFlags
+                    var mask = 0
+                    var allowlistFlagsCopy = allowlistedFlags
+                    while (allowlistFlagsCopy != 0) {
+                        val flag = 1 shl allowlistFlagsCopy.countTrailingZeroBits()
+                        allowlistFlagsCopy = allowlistFlagsCopy and flag.inv()
+                        when (flag) {
+                            PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM -> {
+                                mask = mask or PermissionFlags.SYSTEM_EXEMPT
+                                newFlags =
+                                    if (allowlistedPermissions.contains(requestedPermission)) {
+                                        newFlags or PermissionFlags.SYSTEM_EXEMPT
+                                    } else {
+                                        newFlags andInv PermissionFlags.SYSTEM_EXEMPT
+                                    }
+                            }
+                            PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE -> {
+                                mask = mask or PermissionFlags.UPGRADE_EXEMPT
+                                newFlags =
+                                    if (allowlistedPermissions.contains(requestedPermission)) {
+                                        newFlags or PermissionFlags.UPGRADE_EXEMPT
+                                    } else {
+                                        newFlags andInv PermissionFlags.UPGRADE_EXEMPT
+                                    }
+                            }
+                            PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER -> {
+                                mask = mask or PermissionFlags.INSTALLER_EXEMPT
+                                newFlags =
+                                    if (allowlistedPermissions.contains(requestedPermission)) {
+                                        newFlags or PermissionFlags.INSTALLER_EXEMPT
+                                    } else {
+                                        newFlags andInv PermissionFlags.INSTALLER_EXEMPT
+                                    }
+                            }
+                        }
+                    }
+
+                    if (oldFlags == newFlags) {
+                        return@forEachIndexed
+                    }
+
+                    val wasAllowlisted = oldFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)
+                    val isAllowlisted = newFlags.hasAnyBit(PermissionFlags.MASK_EXEMPT)
+
+                    // If the permission is policy fixed as granted but it is no longer
+                    // on any of the allowlists we need to clear the policy fixed flag
+                    // as allowlisting trumps policy i.e. policy cannot grant a non
+                    // grantable permission.
+                    if (oldFlags.hasBits(PermissionFlags.POLICY_FIXED)) {
+                        if (!isAllowlisted && wasGranted) {
+                            mask = mask or PermissionFlags.POLICY_FIXED
+                            newFlags = newFlags andInv PermissionFlags.POLICY_FIXED
+                        }
+                    }
+
+                    // If we are allowlisting an app that does not support runtime permissions
+                    // we need to make sure it goes through the permission review UI at launch.
+                    if (androidPackage.targetSdkVersion < Build.VERSION_CODES.M &&
+                        !wasAllowlisted && isAllowlisted) {
+                        mask = mask or PermissionFlags.IMPLICIT
+                        newFlags = newFlags or PermissionFlags.IMPLICIT
+                    }
+
+                    updatePermissionFlags(
+                        appId, userId, requestedPermission, mask, newFlags
+                    )
+                }
+            }
+        }
     }
 
     override fun resetRuntimePermissions(androidPackage: AndroidPackage, userId: Int) {
@@ -1159,8 +1550,29 @@
         )
     }
 
+
+
     override fun getAppOpPermissionPackages(permissionName: String): Array<String> {
-        TODO("Not yet implemented")
+        requireNotNull(permissionName) { "permissionName cannot be null" }
+        val packageNames = IndexedSet<String>()
+
+        val permission = service.getState {
+            with(policy) { getPermissions()[permissionName] }
+        }
+        if (permission == null || !permission.isAppOp) {
+            packageNames.toTypedArray()
+        }
+
+        packageManagerLocal.withUnfilteredSnapshot().use { snapshot ->
+            snapshot.packageStates.forEach packageStates@{ (_, packageState) ->
+                val androidPackage = packageState.androidPackage ?: return@packageStates
+                if (permissionName in androidPackage.requestedPermissions) {
+                    packageNames += androidPackage.packageName
+                }
+            }
+        }
+
+        return packageNames.toTypedArray()
     }
 
     override fun getAllAppOpPermissionPackages(): Map<String, Set<String>> {
@@ -1183,19 +1595,63 @@
     }
 
     override fun backupRuntimePermissions(userId: Int): ByteArray? {
-        TODO("Not yet implemented")
+        Preconditions.checkArgumentNonnegative(userId, "userId cannot be null")
+        val backup = CompletableFuture<ByteArray>()
+        permissionControllerManager.getRuntimePermissionBackup(
+            UserHandle.of(userId), PermissionThread.getExecutor(), backup::complete
+        )
+
+        return try {
+            backup.get(BACKUP_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+        } catch (e: Exception) {
+            when (e) {
+                is TimeoutException, is InterruptedException, is ExecutionException -> {
+                    Log.e(LOG_TAG, "Cannot create permission backup for user $userId", e)
+                    null
+                }
+                else -> throw e
+            }
+        }
     }
 
     override fun restoreRuntimePermissions(backup: ByteArray, userId: Int) {
-        TODO("Not yet implemented")
+        requireNotNull(backup) { "backup" }
+        Preconditions.checkArgumentNonnegative(userId, "userId")
+
+        synchronized(isDelayedPermissionBackupFinished) {
+            isDelayedPermissionBackupFinished -= userId
+        }
+        permissionControllerManager.stageAndApplyRuntimePermissionsBackup(
+            backup, UserHandle.of(userId)
+        )
     }
 
     override fun restoreDelayedRuntimePermissions(packageName: String, userId: Int) {
-        TODO("Not yet implemented")
+        requireNotNull(packageName) { "packageName" }
+        Preconditions.checkArgumentNonnegative(userId, "userId")
+
+        synchronized(isDelayedPermissionBackupFinished) {
+            if (isDelayedPermissionBackupFinished.get(userId, false)) {
+                return
+            }
+        }
+        permissionControllerManager.applyStagedRuntimePermissionBackup(
+            packageName, UserHandle.of(userId), PermissionThread.getExecutor()
+        ) { hasMoreBackup ->
+            if (hasMoreBackup) {
+                return@applyStagedRuntimePermissionBackup
+            }
+            synchronized(isDelayedPermissionBackupFinished) {
+                isDelayedPermissionBackupFinished.put(userId, true)
+            }
+        }
     }
 
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>?) {
-        TODO("Not yet implemented")
+        if (!DumpUtils.checkDumpPermission(context, LOG_TAG, pw)) {
+            return
+        }
+        context.getSystemService(PermissionControllerManager::class.java)!!.dump(fd, args)
     }
 
     override fun getPermissionTEMP(
@@ -1231,7 +1687,10 @@
     }
 
     override fun onSystemReady() {
-        TODO("Not yet implemented")
+        // TODO STOPSHIP privappPermissionsViolationsfix check
+        permissionControllerManager = PermissionControllerManager(
+            context, PermissionThread.getHandler()
+        )
     }
 
     override fun onUserCreated(userId: Int) {
@@ -1250,17 +1709,17 @@
     }
 
     override fun onPackageAdded(
-        androidPackage: AndroidPackage,
+        packageState: PackageState,
         isInstantApp: Boolean,
         oldPackage: AndroidPackage?
     ) {
         synchronized(mountedStorageVolumes) {
-            if (androidPackage.volumeUuid !in mountedStorageVolumes) {
+            if (packageState.volumeUuid !in mountedStorageVolumes) {
                 // Wait for the storage volume to be mounted and batch the state mutation there.
                 return
             }
         }
-        service.onPackageAdded(androidPackage.packageName)
+        service.onPackageAdded(packageState.packageName)
     }
 
     override fun onPackageRemoved(androidPackage: AndroidPackage) {
@@ -1301,6 +1760,7 @@
     override fun onPackageUninstalled(
         packageName: String,
         appId: Int,
+        packageState: PackageState,
         androidPackage: AndroidPackage?,
         sharedUserPkgs: List<AndroidPackage>,
         userId: Int
@@ -1348,13 +1808,12 @@
         if (activityManager != null) {
             val appId = UserHandle.getAppId(uid)
             val userId = UserHandle.getUserId(uid)
-            val identity = Binder.clearCallingIdentity()
-            try {
-                activityManager.killUidForPermissionChange(appId, userId, reason)
-            } catch (e: RemoteException) {
-                /* ignore - same process */
-            } finally {
-                Binder.restoreCallingIdentity(identity)
+            Binder::class.withClearedCallingIdentity {
+                try {
+                    activityManager.killUidForPermissionChange(appId, userId, reason)
+                } catch (e: RemoteException) {
+                    /* ignore - same process */
+                }
             }
         }
     }
@@ -1677,5 +2136,15 @@
         private const val UNREQUESTABLE_MASK = PermissionFlags.RESTRICTION_REVOKED or
             PermissionFlags.SYSTEM_FIXED or PermissionFlags.POLICY_FIXED or
             PermissionFlags.USER_FIXED
+
+        private val BACKUP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60)
+
+        /** Cap the size of permission trees that 3rd party apps can define; in characters of text  */
+        private const val MAX_PERMISSION_TREE_FOOTPRINT = 32768
+
+        private const val PERMISSION_ALLOWLIST_MASK =
+            PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE or
+            PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM or
+            PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER
     }
 }
diff --git a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
index 49759c0..4c3ffde 100644
--- a/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/UidPermissionPolicy.kt
@@ -191,6 +191,61 @@
         check(packageName !in newState.systemState.disabledSystemPackageStates) {
             "Package $packageName reported as removed before disabled system package is enabled"
         }
+
+        val changedPermissionNames = IndexedSet<String>()
+        trimPermissions(packageName, changedPermissionNames)
+        trimPermissionStates(appId)
+        changedPermissionNames.forEachIndexed { _, permissionName ->
+            evaluatePermissionStateForAllPackages(permissionName, null)
+        }
+    }
+
+    override fun MutateStateScope.onPackageUninstalled(
+        packageName: String,
+        appId: Int,
+        userId: Int
+    ) {
+        resetRuntimePermissions(packageName, appId, userId)
+    }
+
+    fun MutateStateScope.resetRuntimePermissions(
+        packageName: String,
+        appId: Int,
+        userId: Int
+    ) {
+        val androidPackage = newState.systemState.packageStates[packageName]?.androidPackage
+            ?: return
+        androidPackage.requestedPermissions.forEachIndexed { _, permissionName ->
+            val permission = newState.systemState.permissions[permissionName]
+                ?: return@forEachIndexed
+            if (permission.isRemoved) {
+                return@forEachIndexed
+            }
+            val isRequestedByOtherPackages = anyPackageInAppId(appId) { packageState ->
+                packageState.packageName != packageName &&
+                    permissionName in packageState.androidPackage!!.requestedPermissions
+            }
+            if (isRequestedByOtherPackages) {
+                return@forEachIndexed
+            }
+            val oldFlags = getPermissionFlags(appId, userId, permissionName)
+            if (oldFlags.hasAnyBit(SYSTEM_OR_POLICY_FIXED_MASK)) {
+                return@forEachIndexed
+            }
+            var newFlags = oldFlags
+            newFlags = if (
+                newFlags.hasBits(PermissionFlags.ROLE) || newFlags.hasBits(PermissionFlags.PREGRANT)
+            ) {
+                newFlags or PermissionFlags.RUNTIME_GRANTED
+            } else {
+                newFlags andInv PermissionFlags.RUNTIME_GRANTED
+            }
+            newFlags = newFlags andInv USER_SETTABLE_MASK
+            if (newFlags.hasBits(PermissionFlags.LEGACY_GRANTED)) {
+                newFlags = newFlags or PermissionFlags.IMPLICIT
+            }
+            setPermissionFlags(appId, userId, permissionName, newFlags)
+        }
     }
 
     private fun MutateStateScope.adoptPermissions(
@@ -306,7 +361,7 @@
             // Different from the old implementation, which may add an (incomplete) signature
             // permission inside another package's permission tree, we now consistently ignore such
             // permissions.
-            val permissionTree = getPermissionTree(permissionName)
+            val permissionTree = findPermissionTree(permissionName)
             val newPackageName = newPermissionInfo.packageName
             if (permissionTree != null && newPackageName != permissionTree.packageName) {
                 Log.w(
@@ -427,7 +482,7 @@
         if (!permission.isDynamic) {
             return permission
         }
-        val permissionTree = getPermissionTree(permission.name) ?: return permission
+        val permissionTree = findPermissionTree(permission.name) ?: return permission
         @Suppress("DEPRECATION")
         return permission.copy(
             permissionInfo = PermissionInfo(permission.permissionInfo).apply {
@@ -436,18 +491,6 @@
         )
     }
 
-    private fun MutateStateScope.getPermissionTree(permissionName: String): Permission? =
-        newState.systemState.permissionTrees.firstNotNullOfOrNullIndexed {
-            _, permissionTreeName, permissionTree ->
-            if (permissionName.startsWith(permissionTreeName) &&
-                permissionName.length > permissionTreeName.length &&
-                permissionName[permissionTreeName.length] == '.') {
-                permissionTree
-            } else {
-                null
-            }
-        }
-
     private fun MutateStateScope.trimPermissionStates(appId: Int) {
         val requestedPermissions = IndexedSet<String>()
         forEachPackageInAppId(appId) {
@@ -586,7 +629,9 @@
                 newFlags = newFlags or (oldFlags and PermissionFlags.RUNTIME_GRANTED)
             }
             if (permission.isRole) {
-                newFlags = newFlags or (oldFlags and PermissionFlags.ROLE)
+                newFlags = newFlags or (
+                    oldFlags and (PermissionFlags.ROLE or PermissionFlags.RUNTIME_GRANTED)
+                )
             }
             setPermissionFlags(appId, userId, permissionName, newFlags)
         } else if (permission.isRuntime) {
@@ -646,11 +691,7 @@
                             PermissionFlags.isAppOpGranted(accessBackgroundLocationFlags) &&
                                 !accessBackgroundLocationFlags.hasBits(PermissionFlags.IMPLICIT)
                     }
-                    // These are the permission flags that imply we shouldn't automatically
-                    // modify the permission grant state.
-                    val shouldRetainByMask = newFlags.hasAnyBit(
-                        PermissionFlags.SYSTEM_FIXED or PermissionFlags.POLICY_FIXED
-                    )
+                    val shouldRetainByMask = newFlags.hasAnyBit(SYSTEM_OR_POLICY_FIXED_MASK)
                     if (shouldRetainAsNearbyDevices || shouldRetainByMask) {
                         if (wasGrantedByImplicit) {
                             newFlags = newFlags or PermissionFlags.RUNTIME_GRANTED
@@ -784,14 +825,13 @@
         if (packageState.packageName == PLATFORM_PACKAGE_NAME) {
             return true
         }
-        val androidPackage = packageState.androidPackage!!
-        if (!androidPackage.isPrivileged) {
+        if (!packageState.isPrivileged) {
             return true
         }
         if (permission.packageName !in newState.systemState.privilegedPermissionAllowlistPackages) {
             return true
         }
-        val allowlistState = getPrivilegedPermissionAllowlistState(androidPackage, permission.name)
+        val allowlistState = getPrivilegedPermissionAllowlistState(packageState, permission.name)
         if (allowlistState != null) {
             return allowlistState
         }
@@ -808,23 +848,23 @@
      * allowlist, or `null` if it's not in the allowlist.
      */
     private fun MutateStateScope.getPrivilegedPermissionAllowlistState(
-        androidPackage: AndroidPackage,
+        packageState: PackageState,
         permissionName: String
     ): Boolean? {
         val permissionAllowlist = newState.systemState.permissionAllowlist
         // TODO(b/261913353): STOPSHIP: Add AndroidPackage.apexModuleName. The below is only for
         //  passing compilation but won't actually work.
         // val apexModuleName = androidPackage.apexModuleName
-        val apexModuleName = androidPackage.packageName
-        val packageName = androidPackage.packageName
+        val apexModuleName = packageState.packageName
+        val packageName = packageState.packageName
         return when {
-            androidPackage.isVendor -> permissionAllowlist.getVendorPrivilegedAppAllowlistState(
+            packageState.isVendor -> permissionAllowlist.getVendorPrivilegedAppAllowlistState(
                 packageName, permissionName
             )
-            androidPackage.isProduct -> permissionAllowlist.getProductPrivilegedAppAllowlistState(
+            packageState.isProduct -> permissionAllowlist.getProductPrivilegedAppAllowlistState(
                 packageName, permissionName
             )
-            androidPackage.isSystemExt ->
+            packageState.isSystemExt ->
                 permissionAllowlist.getSystemExtPrivilegedAppAllowlistState(
                     packageName, permissionName
                 )
@@ -898,13 +938,14 @@
             val shouldGrant = if (packageState.isUpdatedSystemApp) {
                 // For updated system applications, a privileged/oem permission
                 // is granted only if it had been defined by the original application.
-                val disabledSystemPackage = newState.systemState
-                    .disabledSystemPackageStates[packageState.packageName]?.androidPackage
+                val disabledSystemPackageState = newState.systemState
+                    .disabledSystemPackageStates[packageState.packageName]
+                val disabledSystemPackage = disabledSystemPackageState?.androidPackage
                 disabledSystemPackage != null &&
                     permission.name in disabledSystemPackage.requestedPermissions &&
-                    shouldGrantPrivilegedOrOemPermission(disabledSystemPackage, permission)
+                    shouldGrantPrivilegedOrOemPermission(disabledSystemPackageState, permission)
             } else {
-                shouldGrantPrivilegedOrOemPermission(androidPackage, permission)
+                shouldGrantPrivilegedOrOemPermission(packageState, permission)
             }
             if (shouldGrant) {
                 return true
@@ -989,18 +1030,18 @@
     }
 
     private fun MutateStateScope.shouldGrantPrivilegedOrOemPermission(
-        androidPackage: AndroidPackage,
+        packageState: PackageState,
         permission: Permission
     ): Boolean {
         val permissionName = permission.name
-        val packageName = androidPackage.packageName
+        val packageName = packageState.packageName
         when {
             permission.isPrivileged -> {
-                if (androidPackage.isPrivileged) {
+                if (packageState.isPrivileged) {
                     // In any case, don't grant a privileged permission to privileged vendor apps,
                     // if the permission's protectionLevel does not have the extra vendorPrivileged
                     // flag.
-                    if (androidPackage.isVendor && !permission.isVendorPrivileged) {
+                    if (packageState.isVendor && !permission.isVendorPrivileged) {
                         Log.w(
                             LOG_TAG, "Permission $permissionName cannot be granted to privileged" +
                             " vendor app $packageName because it isn't a vendorPrivileged" +
@@ -1012,7 +1053,7 @@
                 }
             }
             permission.isOem -> {
-                if (androidPackage.isOem) {
+                if (packageState.isOem) {
                     val allowlistState = newState.systemState.permissionAllowlist
                         .getOemAppAllowlistState(packageName, permissionName)
                     checkNotNull(allowlistState) {
@@ -1050,6 +1091,26 @@
         with(persistence) { [email protected](state, userId) }
     }
 
+    fun GetStateScope.getPermissionTrees(): IndexedMap<String, Permission> =
+        state.systemState.permissionTrees
+
+    fun GetStateScope.findPermissionTree(permissionName: String): Permission? =
+        state.systemState.permissionTrees.firstNotNullOfOrNullIndexed {
+                _, permissionTreeName, permissionTree ->
+            if (permissionName.startsWith(permissionTreeName) &&
+                permissionName.length > permissionTreeName.length &&
+                permissionName[permissionTreeName.length] == '.') {
+                permissionTree
+            } else {
+                null
+            }
+        }
+
+    fun MutateStateScope.addPermissionTree(permission: Permission) {
+        newState.systemState.permissionTrees[permission.name] = permission
+        newState.systemState.requestWrite()
+    }
+
     /**
      * returns all permission group definitions available in the system
      */
@@ -1062,6 +1123,16 @@
     fun GetStateScope.getPermissions(): IndexedMap<String, Permission> =
         state.systemState.permissions
 
+    fun MutateStateScope.addPermission(permission: Permission, sync: Boolean = false) {
+        newState.systemState.permissions[permission.name] = permission
+        newState.systemState.requestWrite(sync)
+    }
+
+    fun MutateStateScope.removePermission(permission: Permission) {
+        newState.systemState.permissions -= permission.name
+        newState.systemState.requestWrite()
+    }
+
     fun GetStateScope.getUidPermissionFlags(appId: Int, userId: Int): IndexedMap<String, Int>? =
         state.userStates[userId]?.uidPermissionFlags?.get(appId)
 
@@ -1159,6 +1230,24 @@
         private val NOTIFICATIONS_PERMISSIONS = indexedSetOf(
             Manifest.permission.POST_NOTIFICATIONS
         )
+
+        /**
+         * Mask for all permission flags that can be set by the user
+         */
+        private const val USER_SETTABLE_MASK =
+            PermissionFlags.USER_SET or
+                PermissionFlags.USER_FIXED or
+                PermissionFlags.APP_OP_REVOKED or
+                PermissionFlags.ONE_TIME or
+                PermissionFlags.HIBERNATION or
+                PermissionFlags.USER_SELECTED
+
+        /**
+         * Mask for all permission flags that imply we shouldn't automatically modify the
+         * permission grant state.
+         */
+        private const val SYSTEM_OR_POLICY_FIXED_MASK =
+            PermissionFlags.SYSTEM_FIXED or PermissionFlags.POLICY_FIXED
     }
 
     /**
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
index ffa2729..c6b355c 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceWindowGainedFocusTest.java
@@ -28,6 +28,8 @@
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -35,6 +37,7 @@
 import static org.mockito.Mockito.when;
 
 import android.os.IBinder;
+import android.os.LocaleList;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.inputmethod.EditorInfo;
@@ -47,11 +50,14 @@
 import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.inputmethod.StartInputFlags;
 import com.android.internal.inputmethod.StartInputReason;
+import com.android.server.LocalServices;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
+import org.mockito.Mock;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -84,6 +90,8 @@
             };
     private static final int DEFAULT_SOFT_INPUT_FLAG =
             StartInputFlags.VIEW_HAS_FOCUS | StartInputFlags.IS_TEXT_EDITOR;
+    @Mock
+    VirtualDeviceManagerInternal mMockVdmInternal;
 
     @Parameterized.Parameters(name = "softInputState={0}, softInputAdjustment={1}")
     public static List<Object[]> softInputModeConfigs() {
@@ -256,6 +264,19 @@
                 mMockImeOnBackInvokedDispatcher /* imeDispatcher */);
     }
 
+    @Test
+    public void startInputOrWindowGainedFocus_localeHintsOverride() throws RemoteException {
+        doReturn(mMockVdmInternal).when(
+                () -> LocalServices.getService(VirtualDeviceManagerInternal.class));
+        LocaleList overrideLocale = LocaleList.forLanguageTags("zh-CN");
+        doReturn(overrideLocale).when(mMockVdmInternal).getPreferredLocaleListForUid(anyInt());
+        mockHasImeFocusAndRestoreImeVisibility(false /* restoreImeVisibility */);
+
+        assertThat(startInputOrWindowGainedFocus(DEFAULT_SOFT_INPUT_FLAG,
+                true /* forwardNavigation */)).isEqualTo(SUCCESS_WAITING_IME_BINDING_RESULT);
+        assertThat(mEditorInfo.hintLocales).isEqualTo(overrideLocale);
+    }
+
     private void mockHasImeFocusAndRestoreImeVisibility(boolean restoreImeVisibility) {
         when(mMockWindowManagerInternal.hasInputMethodClientFocus(
                         any(), anyInt(), anyInt(), anyInt()))
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
index ce0b3a2..b6f1b87 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
@@ -117,14 +117,14 @@
     @Before
     public void setupDefaultAbiBehavior() throws Exception {
         when(mMockPackageAbiHelper.derivePackageAbi(
-                any(AndroidPackage.class), anyBoolean(), nullable(String.class),
+                any(AndroidPackage.class), anyBoolean(), anyBoolean(), nullable(String.class),
                 any(File.class)))
                 .thenReturn(new Pair<>(
                         new PackageAbiHelper.Abis("derivedPrimary", "derivedSecondary"),
                         new PackageAbiHelper.NativeLibraryPaths(
                                 "derivedRootDir", true, "derivedNativeDir", "derivedNativeDir2")));
         when(mMockPackageAbiHelper.deriveNativeLibraryPaths(
-                any(AndroidPackage.class), anyBoolean(), any(File.class)))
+                any(AndroidPackage.class), anyBoolean(), anyBoolean(), any(File.class)))
                 .thenReturn(new PackageAbiHelper.NativeLibraryPaths(
                         "getRootDir", true, "getNativeDir", "getNativeDir2"
                 ));
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java
index d3107b0..f376e73 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidHidlUpdaterTest.java
@@ -54,7 +54,7 @@
                 .hideAsFinal();
 
         // no change, not system
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -73,7 +73,7 @@
                 .setSystem(true)
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, true);
     }
 
     @Test
@@ -90,7 +90,7 @@
                 .hideAsFinal();
 
         // no change, not system
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -112,7 +112,7 @@
                 .setSystem(true)
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, true);
     }
 
     @Test
@@ -129,7 +129,7 @@
                 .hideAsFinal();
 
         // Libraries are removed because they are not available for non-system apps
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -151,7 +151,7 @@
 
         // No change is required because the package explicitly requests the HIDL libraries
         // and is targeted at the current version so does not need backwards compatibility.
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, true);
     }
 
     @Test
@@ -168,7 +168,7 @@
                 .hideAsFinal();
 
         // Libraries are removed because they are not available for apps targeting Q+
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -185,10 +185,11 @@
                 .hideAsFinal();
 
         // Libraries are removed because they are not available for apps targeting Q+
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
-    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
-        checkBackwardsCompatibility(before, after, AndroidHidlUpdater::new);
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after,
+            boolean isSystemApp) {
+        checkBackwardsCompatibility(before, after, isSystemApp, AndroidHidlUpdater::new);
     }
 }
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java
index 36308d2..9248da6 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidNetIpSecIkeUpdaterTest.java
@@ -51,7 +51,7 @@
                 .addUsesOptionalLibrary("optional")
                 .hideAsParsed())
                 .hideAsFinal();
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -66,7 +66,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -81,10 +81,11 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
-    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
-        checkBackwardsCompatibility(before, after, AndroidNetIpSecIkeUpdater::new);
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after,
+            boolean isSystemApp) {
+        checkBackwardsCompatibility(before, after, isSystemApp, AndroidNetIpSecIkeUpdater::new);
     }
 }
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java
index 3782f5d..23a2c20 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestBaseUpdaterTest.java
@@ -54,7 +54,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -73,7 +73,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -91,7 +91,7 @@
 
         // No change is required because although org.apache.http.legacy has been removed from
         // the bootclasspath the package explicitly requests it.
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -109,7 +109,7 @@
 
         // No change is required because although org.apache.http.legacy has been removed from
         // the bootclasspath the package explicitly requests it.
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -127,7 +127,7 @@
 
         // No change is required because the package explicitly requests org.apache.http.legacy
         // and is targeted at the current version so does not need backwards compatibility.
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -145,10 +145,11 @@
 
         // No change is required because the package explicitly requests org.apache.http.legacy
         // and is targeted at the current version so does not need backwards compatibility.
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
-    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
-        checkBackwardsCompatibility(before, after, AndroidTestBaseUpdater::new);
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after,
+            boolean isSystemApp) {
+        checkBackwardsCompatibility(before, after, isSystemApp, AndroidTestBaseUpdater::new);
     }
 }
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
index a739607..2060caa 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/AndroidTestRunnerSplitUpdaterTest.java
@@ -55,7 +55,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -73,10 +73,11 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
-    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
-        checkBackwardsCompatibility(before, after, AndroidTestRunnerSplitUpdater::new);
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after,
+            boolean isSystemApp) {
+        checkBackwardsCompatibility(before, after, isSystemApp, AndroidTestRunnerSplitUpdater::new);
     }
 }
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
index 3977d0d..b3ad861 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
@@ -97,7 +97,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -115,7 +115,7 @@
         // note: target sdk is not what matters in this logic. It's the system SDK
         // should be removed because on 30+ (R+) it is implicit
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -131,7 +131,7 @@
 
         // note: target sdk is not what matters in this logic. It's the system SDK
         // nothing should change because the implicit from is only from a future platform release
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -151,7 +151,7 @@
 
         // note: target sdk is not what matters in this logic. It's the system SDK
         // nothing should change because the implicit from is only from a future platform release
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -166,7 +166,7 @@
                 .hideAsFinal();
 
         // should not be affected because it is still in the BCP in 30 / R
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -182,7 +182,7 @@
                 .hideAsFinal();
 
         // should be present because this was in BCP in 29 / Q
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     /**
@@ -206,7 +206,7 @@
 
         // the library is now in the BOOTCLASSPATH (for the second time) so it doesn't need to be
         // listed
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     /**
@@ -233,7 +233,7 @@
 
         // in this example, we are at the point where the library is not in the BOOTCLASSPATH.
         // Because the app targets Q / 29 (when this library was in the BCP) then we need to add it
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     /**
@@ -258,7 +258,7 @@
         // in this example, we are at the point where the library is not in the BOOTCLASSPATH.
         // Because the app targets R/30 (when this library was removed from the BCP) then we don't
         //need to add it
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     /**
@@ -284,7 +284,7 @@
 
         // in this example, we are at the point where the library is not in the BOOTCLASSPATH.
         // Because the app wants to use the library, it needs to be present
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     /**
@@ -304,7 +304,7 @@
                 .hideAsFinal();
 
         // in this example, we are at the point where the library is still in the BCP
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     /**
@@ -326,7 +326,7 @@
                 .hideAsFinal();
 
         // in this example, we are at the point where the library was removed from the BCP
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -343,7 +343,7 @@
                 .hideAsFinal();
 
         // in this example, we are at the point where the library was removed from the BCP
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -360,11 +360,12 @@
                 .hideAsFinal();
 
         // in this example, we are at the point where the library was removed from the BCP
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
-    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
-        checkBackwardsCompatibility(before, after,
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after,
+            boolean isSystemApp) {
+        checkBackwardsCompatibility(before, after, isSystemApp,
                 () -> new ApexSharedLibraryUpdater(mSharedLibraries));
     }
 }
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java
index a31c781..558c0e8 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/ComGoogleAndroidMapsUpdaterTest.java
@@ -51,7 +51,7 @@
                 .addUsesOptionalLibrary("optional")
                 .hideAsParsed())
                 .hideAsFinal();
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -68,7 +68,7 @@
 
         // No change is required because the package explicitly requests org.apache.http.legacy
         // and is targeted at the current version so does not need backwards compatibility.
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -85,10 +85,11 @@
 
         // No change is required because the package explicitly requests org.apache.http.legacy
         // and is targeted at the current version so does not need backwards compatibility.
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
-    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
-        checkBackwardsCompatibility(before, after, ComGoogleAndroidMapsUpdater::new);
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after,
+            boolean isSystemApp) {
+        checkBackwardsCompatibility(before, after, isSystemApp, ComGoogleAndroidMapsUpdater::new);
     }
 }
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
index f5e3f4e..7a2ac75 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/OrgApacheHttpLegacyUpdaterTest.java
@@ -54,7 +54,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -73,7 +73,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -91,7 +91,7 @@
 
         // No change is required because although org.apache.http.legacy has been removed from
         // the bootclasspath the package explicitly requests it.
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -109,7 +109,7 @@
 
         // No change is required because although org.apache.http.legacy has been removed from
         // the bootclasspath the package explicitly requests it.
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -127,7 +127,7 @@
 
         // No change is required because the package explicitly requests org.apache.http.legacy
         // and is targeted at the current version so does not need backwards compatibility.
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -145,10 +145,11 @@
 
         // No change is required because the package explicitly requests org.apache.http.legacy
         // and is targeted at the current version so does not need backwards compatibility.
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
-    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
-        checkBackwardsCompatibility(before, after, OrgApacheHttpLegacyUpdater::new);
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after,
+            boolean isSystemApp) {
+        checkBackwardsCompatibility(before, after, isSystemApp, OrgApacheHttpLegacyUpdater::new);
     }
 }
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
index b3648b1..c4b8e6f 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
@@ -55,7 +55,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     /**
@@ -93,7 +93,8 @@
         }
         after.addUsesLibrary(ORG_APACHE_HTTP_LEGACY);
 
-        checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal());
+        checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal(),
+                false);
     }
 
     /**
@@ -122,7 +123,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     /**
@@ -147,7 +148,8 @@
         after.addUsesLibrary(ANDROID_TEST_MOCK);
         after.addUsesLibrary(ANDROID_TEST_RUNNER);
 
-        checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal());
+        checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal(),
+                false);
     }
 
     /**
@@ -164,7 +166,8 @@
         ParsingPackage after = PackageImpl.forTesting(PACKAGE_NAME)
                 .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT);
 
-        checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal());
+        checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal(),
+                false);
     }
 
     /**
@@ -181,7 +184,8 @@
         ParsingPackage after = PackageImpl.forTesting(PACKAGE_NAME)
                 .setTargetSdkVersion(Build.VERSION_CODES.CUR_DEVELOPMENT);
 
-        checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal());
+        checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal(),
+                false);
     }
 
     /**
@@ -200,7 +204,9 @@
         assertThat(lastUpdater).isInstanceOf(ApexSharedLibraryUpdater.class);
     }
 
-    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
-        checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance);
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after,
+            boolean isSystemApp) {
+        checkBackwardsCompatibility(before, after, isSystemApp,
+                PackageBackwardCompatibility::getInstance);
     }
 }
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageSharedLibraryUpdaterTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
index 2450a14..33fc261 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/PackageSharedLibraryUpdaterTest.java
@@ -31,8 +31,8 @@
     protected static final String PACKAGE_NAME = "org.package.name";
 
     static void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after,
-            Supplier<PackageSharedLibraryUpdater> updaterSupplier) {
-        updaterSupplier.get().updatePackage(before, false);
+            boolean isSystemApp, Supplier<PackageSharedLibraryUpdater> updaterSupplier) {
+        updaterSupplier.get().updatePackage(before, isSystemApp, false);
         check(before.hideAsFinal(), after);
     }
 
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
index 2825c69..8918233 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryAndroidTestBaseLibraryTest.java
@@ -55,7 +55,7 @@
                 .hideAsFinal();
 
         // No change required.
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -72,7 +72,7 @@
                 .hideAsFinal();
 
         // No change required.
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -89,7 +89,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -106,7 +106,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -121,7 +121,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -138,7 +138,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -156,16 +156,17 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
-    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after,
+            boolean isSystemApp) {
         // TODO(b/72538146) - Cannot use constructor reference here because it is also used in
         // PackageBackwardCompatibility and that seems to create a package-private lambda in
         // android.content.pm which this then tries to reuse but fails because it cannot access
         // package-private classes/members because the test is loaded by a different ClassLoader
         // than the lambda.
-        checkBackwardsCompatibility(before, after,
+        checkBackwardsCompatibility(before, after, isSystemApp,
                 () -> new RemoveUnnecessaryAndroidTestBaseLibrary());
     }
 
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
index c0da8a7..3e9ec0e 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/library/RemoveUnnecessaryOrgApacheHttpLegacyLibraryTest.java
@@ -55,7 +55,7 @@
                 .hideAsFinal();
 
         // No change required.
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -72,7 +72,7 @@
                 .hideAsFinal();
 
         // No change required.
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -89,7 +89,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -106,7 +106,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -123,7 +123,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -140,7 +140,7 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
     @Test
@@ -158,16 +158,17 @@
                 .hideAsParsed())
                 .hideAsFinal();
 
-        checkBackwardsCompatibility(before, after);
+        checkBackwardsCompatibility(before, after, false);
     }
 
-    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+    private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after,
+            boolean isSystemApp) {
         // TODO(b/72538146) - Cannot use constructor reference here because it is also used in
         // PackageBackwardCompatibility and that seems to create a package-private lambda in
         // android.content.pm which this then tries to reuse but fails because it cannot access
         // package-private classes/members because the test is loaded by a different ClassLoader
         // than the lambda.
-        checkBackwardsCompatibility(before, after,
+        checkBackwardsCompatibility(before, after, isSystemApp,
                 () -> new RemoveUnnecessaryOrgApacheHttpLegacyLibrary());
     }
 }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index 7bf9a9e..c439639 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -30,6 +30,7 @@
 import android.util.SparseArray
 import android.util.SparseIntArray
 import com.android.internal.R
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils
 import com.android.server.pm.parsing.pkg.PackageImpl
 import com.android.server.pm.pkg.AndroidPackage
 import com.android.server.pm.pkg.component.ParsedActivityImpl
@@ -126,7 +127,23 @@
         "addUsesStaticLibrary",
         "getUsesStaticLibraries",
         "getUsesStaticLibrariesVersions",
-        "getUsesStaticLibrariesCertDigests"
+        "getUsesStaticLibrariesCertDigests",
+
+        // Tested through getSetByValue via AndroidPackageHidden APIs, to be removed eventually
+        "setOdm",
+        "setOem",
+        "setPrivileged",
+        "setProduct",
+        "setSystem",
+        "setSystemExt",
+        "setVendor",
+        "isOdm",
+        "isOem",
+        "isPrivileged",
+        "isProduct",
+        "isSystem",
+        "isSystemExt",
+        "isVendor",
     )
 
     override val baseParams = listOf(
@@ -221,15 +238,11 @@
         AndroidPackage::isLargeHeap,
         AndroidPackage::isMultiArch,
         AndroidPackage::isNativeLibraryRootRequiresIsa,
-        AndroidPackage::isOdm,
-        AndroidPackage::isOem,
         AndroidPackage::isOnBackInvokedCallbackEnabled,
         AndroidPackage::isOverlay,
         AndroidPackage::isOverlayIsStatic,
         AndroidPackage::isPartiallyDirectBootAware,
         AndroidPackage::isPersistent,
-        AndroidPackage::isPrivileged,
-        AndroidPackage::isProduct,
         AndroidPackage::isProfileableByShell,
         AndroidPackage::isRequestLegacyExternalStorage,
         AndroidPackage::isRequiredForAllUsers,
@@ -240,14 +253,11 @@
         AndroidPackage::isStaticSharedLibrary,
         AndroidPackage::isStub,
         AndroidPackage::isSupportsRtl,
-        AndroidPackage::isSystem,
-        AndroidPackage::isSystemExt,
         AndroidPackage::isTestOnly,
         AndroidPackage::isUse32BitAbi,
         AndroidPackage::isUseEmbeddedDex,
         AndroidPackage::isUsesCleartextTraffic,
         AndroidPackage::isUsesNonSdkApi,
-        AndroidPackage::isVendor,
         AndroidPackage::isVisibleToInstantApps,
         AndroidPackage::isVmSafeMode,
         AndroidPackage::isLeavingSharedUid,
@@ -518,6 +528,38 @@
             }
         ),
         getter(AndroidPackage::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT")),
+        getSetByValue({ AndroidPackageUtils.isOdm(it) }, "isOdm", PackageImpl::setOdm, true),
+        getSetByValue({ AndroidPackageUtils.isOem(it) }, "isOem", PackageImpl::setOem, true),
+        getSetByValue(
+            { AndroidPackageUtils.isPrivileged(it) },
+            "isPrivileged",
+            PackageImpl::setPrivileged,
+            true
+        ),
+        getSetByValue(
+            { AndroidPackageUtils.isProduct(it) },
+            "isProduct",
+            PackageImpl::setProduct,
+            true
+        ),
+        getSetByValue(
+            { AndroidPackageUtils.isVendor(it) },
+            "isVendor",
+            PackageImpl::setVendor,
+            true
+        ),
+        getSetByValue(
+            { AndroidPackageUtils.isSystem(it) },
+            "isSystem",
+            PackageImpl::setSystem,
+            true
+        ),
+        getSetByValue(
+            { AndroidPackageUtils.isSystemExt(it) },
+            "isSystemExt",
+            PackageImpl::setSystemExt,
+            true
+        ),
     )
 
     override fun initialObject() = PackageImpl.forParsing(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableComponentTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableComponentTest.kt
index 37bb935..bbeaa2f 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableComponentTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableComponentTest.kt
@@ -243,6 +243,29 @@
     )
 
     /**
+     * Variant of [getSetByValue] that allows specifying a non-member [getFunction]. Mostly used
+     * for AndroidPackageHidden for APIs which are hidden from the interface.
+     */
+    @Suppress("UNCHECKED_CAST")
+    protected fun <ObjectType, ReturnType, SetType : Any?, CompareType : Any?> getSetByValue(
+        getFunction: (ObjectType) -> ReturnType,
+        getFunctionName: String,
+        setFunction: KFunction2<ObjectType, SetType, Any?>,
+        value: CompareType,
+        transformGet: (ReturnType) -> CompareType = { it as CompareType },
+        transformSet: (CompareType) -> SetType = { it as SetType },
+        compare: (CompareType, CompareType) -> Boolean? = Objects::equals
+    ) = Param(
+        getFunctionName,
+        { transformGet(getFunction(it as ObjectType)) },
+        setFunction.name,
+        { setFunction.call(it.first() as ObjectType, transformSet(it[1] as CompareType)) },
+        { value },
+        { first, second -> compare(first as CompareType, second as CompareType) == true },
+        verifyFunctionName = false
+    )
+
+    /**
      * Variant of [getSetByValue] that allows specifying a [setFunction] with 2 inputs.
      */
     @Suppress("UNCHECKED_CAST")
@@ -407,7 +430,7 @@
                 - excludedMethods)
             .distinct()
 
-        val allTestedFunctions = params.flatMap {
+        val allTestedFunctions = params.filter(Param::verifyFunctionName).flatMap {
             listOfNotNull(it.getFunctionName, it.setFunctionName)
         }
         expect.that(allTestedFunctions).containsExactlyElementsIn(expectedFunctions)
@@ -427,6 +450,7 @@
         val setFunctionName: String?,
         val setFunction: (Array<Any?>) -> Unit,
         val value: () -> Any?,
-        val compare: (Any?, Any?) -> Boolean = Objects::equals
+        val compare: (Any?, Any?) -> Boolean = Objects::equals,
+        val verifyFunctionName: Boolean = true
     )
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index e7c384b..83441bf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -40,9 +40,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
-import android.os.Bundle;
 import android.os.RecoverySystem;
-import android.os.RemoteCallback;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
@@ -69,6 +67,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Test RescueParty.
@@ -111,7 +110,7 @@
     private PackageManager mPackageManager;
 
     @Captor
-    private ArgumentCaptor<RemoteCallback> mMonitorCallbackCaptor;
+    private ArgumentCaptor<DeviceConfig.MonitorCallback> mMonitorCallbackCaptor;
     @Captor
     private ArgumentCaptor<List<String>> mPackageListCaptor;
 
@@ -125,7 +124,6 @@
                         .spyStatic(SystemProperties.class)
                         .spyStatic(Settings.Global.class)
                         .spyStatic(Settings.Secure.class)
-                        .spyStatic(Settings.Config.class)
                         .spyStatic(SettingsToPropertiesMapper.class)
                         .spyStatic(RecoverySystem.class)
                         .spyStatic(RescueParty.class)
@@ -222,7 +220,8 @@
     @Test
     public void testBootLoopDetectionWithExecutionForAllRescueLevels() {
         RescueParty.onSettingsProviderPublished(mMockContext);
-        verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver),
+        verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
+                any(Executor.class),
                 mMonitorCallbackCaptor.capture()));
         HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>();
 
@@ -233,9 +232,9 @@
 
         // Record DeviceConfig accesses
         RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
-        RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue();
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1));
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2));
+        DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue();
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1);
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2);
 
         final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
 
@@ -309,25 +308,27 @@
     @Test
     public void testNonPersistentAppCrashDetectionWithScopedResets() {
         RescueParty.onSettingsProviderPublished(mMockContext);
-        verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver),
+        verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
+                any(Executor.class),
                 mMonitorCallbackCaptor.capture()));
 
         // Record DeviceConfig accesses
         RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
-        RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue();
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1));
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2));
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE2));
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE3));
+        DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue();
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1);
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2);
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE2);
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE3);
+
         // Fake DeviceConfig value changes
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1));
+        monitorCallback.onNamespaceUpdate(NAMESPACE1);
         verify(mMockPackageWatchdog).startObservingHealth(observer,
                 Arrays.asList(CALLING_PACKAGE1), RescueParty.DEFAULT_OBSERVING_DURATION_MS);
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE2));
+        monitorCallback.onNamespaceUpdate(NAMESPACE2);
         verify(mMockPackageWatchdog, times(2)).startObservingHealth(eq(observer),
                 mPackageListCaptor.capture(),
                 eq(RescueParty.DEFAULT_OBSERVING_DURATION_MS));
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE3));
+        monitorCallback.onNamespaceUpdate(NAMESPACE3);
         verify(mMockPackageWatchdog).startObservingHealth(observer,
                 Arrays.asList(CALLING_PACKAGE2), RescueParty.DEFAULT_OBSERVING_DURATION_MS);
         assertTrue(mPackageListCaptor.getValue().containsAll(
@@ -364,20 +365,21 @@
     @Test
     public void testNonDeviceConfigSettingsOnlyResetOncePerLevel() {
         RescueParty.onSettingsProviderPublished(mMockContext);
-        verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver),
+        verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
+                any(Executor.class),
                 mMonitorCallbackCaptor.capture()));
 
         // Record DeviceConfig accesses
         RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
-        RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue();
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1));
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2));
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE2));
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE3));
+        DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue();
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1);
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2);
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE2);
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE3);
         // Fake DeviceConfig value changes
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1));
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE2));
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE3));
+        monitorCallback.onNamespaceUpdate(NAMESPACE1);
+        monitorCallback.onNamespaceUpdate(NAMESPACE2);
+        monitorCallback.onNamespaceUpdate(NAMESPACE3);
         // Perform and verify scoped resets
         final String[] expectedPackage1ResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2};
         final String[] expectedPackage2ResetNamespaces = new String[]{NAMESPACE2, NAMESPACE3};
@@ -549,20 +551,21 @@
     @Test
     public void testResetDeviceConfigForPackagesOnlyRuntimeMap() {
         RescueParty.onSettingsProviderPublished(mMockContext);
-        verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver),
+        verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
+                any(Executor.class),
                 mMonitorCallbackCaptor.capture()));
 
         // Record DeviceConfig accesses
         RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
-        RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue();
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1));
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2));
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE2));
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE3));
+        DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue();
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1);
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2);
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE2);
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE3);
         // Fake DeviceConfig value changes
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1));
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE2));
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE3));
+        monitorCallback.onNamespaceUpdate(NAMESPACE1);
+        monitorCallback.onNamespaceUpdate(NAMESPACE2);
+        monitorCallback.onNamespaceUpdate(NAMESPACE3);
 
         doReturn("").when(() -> DeviceConfig.getString(
                 eq(RescueParty.NAMESPACE_CONFIGURATION),
@@ -578,7 +581,8 @@
     @Test
     public void testResetDeviceConfigForPackagesOnlyPresetMap() {
         RescueParty.onSettingsProviderPublished(mMockContext);
-        verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver),
+        verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
+                any(Executor.class),
                 mMonitorCallbackCaptor.capture()));
 
         String presetMapping = NAMESPACE1 + ":" + CALLING_PACKAGE1 + ","
@@ -598,22 +602,23 @@
     @Test
     public void testResetDeviceConfigForPackagesBothMaps() {
         RescueParty.onSettingsProviderPublished(mMockContext);
-        verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver),
+        verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
+                any(Executor.class),
                 mMonitorCallbackCaptor.capture()));
 
         // Record DeviceConfig accesses
         RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
-        RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue();
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1));
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE2));
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE2));
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE3));
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE3, NAMESPACE4));
+        DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue();
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1);
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE2);
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE2);
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE3);
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE3, NAMESPACE4);
         // Fake DeviceConfig value changes
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1));
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE2));
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE3));
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE4));
+        monitorCallback.onNamespaceUpdate(NAMESPACE1);
+        monitorCallback.onNamespaceUpdate(NAMESPACE2);
+        monitorCallback.onNamespaceUpdate(NAMESPACE3);
+        monitorCallback.onNamespaceUpdate(NAMESPACE4);
 
         String presetMapping = NAMESPACE1 + ":" + CALLING_PACKAGE1 + ","
                 + NAMESPACE2 + ":" + CALLING_PACKAGE2 + ","
@@ -633,20 +638,21 @@
     @Test
     public void testResetDeviceConfigNoExceptionWhenFlagMalformed() {
         RescueParty.onSettingsProviderPublished(mMockContext);
-        verify(() -> Settings.Config.registerMonitorCallback(eq(mMockContentResolver),
+        verify(() -> DeviceConfig.setMonitorCallback(eq(mMockContentResolver),
+                any(Executor.class),
                 mMonitorCallbackCaptor.capture()));
 
         // Record DeviceConfig accesses
         RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext);
-        RemoteCallback monitorCallback = mMonitorCallbackCaptor.getValue();
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE1, NAMESPACE1));
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE2, NAMESPACE3));
-        monitorCallback.sendResult(getConfigAccessBundle(CALLING_PACKAGE3, NAMESPACE4));
+        DeviceConfig.MonitorCallback monitorCallback = mMonitorCallbackCaptor.getValue();
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE1, NAMESPACE1);
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE2, NAMESPACE3);
+        monitorCallback.onDeviceConfigAccess(CALLING_PACKAGE3, NAMESPACE4);
         // Fake DeviceConfig value changes
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE1));
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE2));
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE3));
-        monitorCallback.sendResult(getConfigNamespaceUpdateBundle(NAMESPACE4));
+        monitorCallback.onNamespaceUpdate(NAMESPACE1);
+        monitorCallback.onNamespaceUpdate(NAMESPACE2);
+        monitorCallback.onNamespaceUpdate(NAMESPACE3);
+        monitorCallback.onNamespaceUpdate(NAMESPACE4);
 
         String invalidPresetMapping = NAMESPACE2 + ":" + CALLING_PACKAGE2 + ","
                 + NAMESPACE1 + "." + CALLING_PACKAGE2;
@@ -696,20 +702,4 @@
         RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage(
                 packageName, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount);
     }
-
-    private Bundle getConfigAccessBundle(String callingPackage, String namespace) {
-        Bundle result = new Bundle();
-        result.putString(Settings.EXTRA_MONITOR_CALLBACK_TYPE, Settings.EXTRA_ACCESS_CALLBACK);
-        result.putString(Settings.EXTRA_CALLING_PACKAGE, callingPackage);
-        result.putString(Settings.EXTRA_NAMESPACE, namespace);
-        return result;
-    }
-
-    private Bundle getConfigNamespaceUpdateBundle(String updatedNamespace) {
-        Bundle result = new Bundle();
-        result.putString(Settings.EXTRA_MONITOR_CALLBACK_TYPE,
-                Settings.EXTRA_NAMESPACE_UPDATED_CALLBACK);
-        result.putString(Settings.EXTRA_NAMESPACE, updatedNamespace);
-        return result;
-    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActiveServicesTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActiveServicesTest.java
index b3dc3ed..e2c338a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActiveServicesTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActiveServicesTest.java
@@ -32,6 +32,7 @@
 import static org.mockito.Mockito.mock;
 
 import android.app.compat.CompatChanges;
+import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ServiceInfo;
 import android.os.SystemClock;
@@ -40,6 +41,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -194,6 +196,47 @@
                 rd2 + btwn + extra, rd2 + (btwn + extra) * 2});
     }
 
+    @Test
+    public void testGetProcessNameForService() throws Exception {
+        // Regular service
+        final ServiceInfo regularService = new ServiceInfo();
+        regularService.processName = "com.foo";
+        String processName = ActiveServices.getProcessNameForService(regularService, null, null,
+                null, false, false);
+        assertEquals("com.foo", processName);
+
+        // Isolated service
+        final ServiceInfo isolatedService = new ServiceInfo();
+        isolatedService.processName = "com.foo";
+        isolatedService.flags = ServiceInfo.FLAG_ISOLATED_PROCESS;
+        final ComponentName component = new ComponentName("com.foo", "barService");
+        processName = ActiveServices.getProcessNameForService(isolatedService, component,
+                null, null, false, false);
+        assertEquals("com.foo:barService", processName);
+
+        // Isolated service in shared isolated process
+        final ServiceInfo isolatedServiceShared1 = new ServiceInfo();
+        isolatedServiceShared1.flags = ServiceInfo.FLAG_ISOLATED_PROCESS;
+        final String instanceName = "pool";
+        final String callingPackage = "com.foo";
+        final String sharedIsolatedProcessName1 = ActiveServices.getProcessNameForService(
+                isolatedServiceShared1, null, callingPackage, instanceName, false, true);
+        assertEquals("com.foo:ishared:pool", sharedIsolatedProcessName1);
+
+        // Bind another one in the same isolated process
+        final ServiceInfo isolatedServiceShared2 = new ServiceInfo(isolatedServiceShared1);
+        final String sharedIsolatedProcessName2 = ActiveServices.getProcessNameForService(
+                isolatedServiceShared2, null, callingPackage, instanceName, false, true);
+        assertEquals(sharedIsolatedProcessName1, sharedIsolatedProcessName2);
+
+        // Simulate another app trying to do the bind
+        final ServiceInfo isolatedServiceShared3 = new ServiceInfo(isolatedServiceShared1);
+        final String otherCallingPackage = "com.bar";
+        final String sharedIsolatedProcessName3 = ActiveServices.getProcessNameForService(
+                isolatedServiceShared3, null, otherCallingPackage, instanceName, false, true);
+        Assert.assertNotEquals(sharedIsolatedProcessName2, sharedIsolatedProcessName3);
+    }
+
     private void prepareTestRescheduleServiceRestarts() {
         mService = mock(ActivityManagerService.class);
         mService.mConstants = mock(ActivityManagerConstants.class);
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index c0688d1..52027e7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -57,6 +57,7 @@
 import com.android.server.LocalServices;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.testutils.MockitoUtilsKt;
 
 import org.junit.After;
 import org.junit.Before;
@@ -137,11 +138,12 @@
         // Mock LocalServices.getService(PackageManagerInternal.class).getPackageStateInternal
         // and getPackage dependency needed by AppOpsService
         PackageManagerInternal mockPackageManagerInternal = mock(PackageManagerInternal.class);
-        PackageStateInternal mockMyPSInternal = mock(PackageStateInternal.class);
         AndroidPackage mockMyPkg = mock(AndroidPackage.class);
-        when(mockMyPkg.isPrivileged()).thenReturn(false);
-        when(mockMyPkg.getUid()).thenReturn(mMyUid);
         when(mockMyPkg.getAttributions()).thenReturn(Collections.emptyList());
+        PackageStateInternal mockMyPSInternal = mock(PackageStateInternal.class);
+        when(mockMyPSInternal.isPrivileged()).thenReturn(false);
+        when(mockMyPSInternal.getAppId()).thenReturn(mMyUid);
+        when(mockMyPSInternal.getAndroidPackage()).thenReturn(mockMyPkg);
 
         when(mockPackageManagerInternal.getPackageStateInternal(sMyPackageName))
                 .thenReturn(mockMyPSInternal);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
index aabec22..2f909aa 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -49,6 +49,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 
@@ -380,7 +381,7 @@
                 findFactory(results, "test.apex.rebootless").apexInfo);
         assertThat(factoryPkg.getBaseApkPath()).isEqualTo(activeApexInfo.modulePath);
         assertThat(factoryPkg.getLongVersionCode()).isEqualTo(1);
-        assertThat(factoryPkg.isSystem()).isTrue();
+        assertThat(AndroidPackageUtils.isSystem(factoryPkg)).isTrue();
     }
 
     @Test
@@ -410,7 +411,7 @@
                 findFactory(results, "test.apex.rebootless").apexInfo);
         assertThat(factoryPkg.getBaseApkPath()).isEqualTo(factoryApexInfo.modulePath);
         assertThat(factoryPkg.getLongVersionCode()).isEqualTo(1);
-        assertThat(factoryPkg.isSystem()).isTrue();
+        assertThat(AndroidPackageUtils.isSystem(factoryPkg)).isTrue();
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 6dc45c3..9935a2f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -327,16 +327,16 @@
         whenever(mocks.settings.keySetManagerService).thenReturn(mocks.keySetManagerService)
         whenever(mocks.settings.keySetManagerService).thenReturn(mocks.keySetManagerService)
         whenever(mocks.settings.snapshot()).thenReturn(mocks.settings)
-        whenever(mocks.packageAbiHelper.derivePackageAbi(
-                any(AndroidPackage::class.java), anyBoolean(), nullable(), any(File::class.java))) {
+        whenever(mocks.packageAbiHelper.derivePackageAbi(any(AndroidPackage::class.java),
+            anyBoolean(), anyBoolean(), nullable(), any(File::class.java))) {
             android.util.Pair(PackageAbiHelper.Abis("", ""),
                     PackageAbiHelper.NativeLibraryPaths("", false, "", ""))
         }
         whenever(mocks.userManagerInternal.getUsers(true, false, false)).thenReturn(DEFAULT_USERS)
         whenever(mocks.userManagerService.userIds).thenReturn(intArrayOf(0))
         whenever(mocks.userManagerService.exists(0)).thenReturn(true)
-        whenever(mocks.packageAbiHelper.deriveNativeLibraryPaths(
-                any(AndroidPackage::class.java), anyBoolean(), any(File::class.java))) {
+        whenever(mocks.packageAbiHelper.deriveNativeLibraryPaths(any(AndroidPackage::class.java),
+                anyBoolean(), anyBoolean(), any(File::class.java))) {
             PackageAbiHelper.NativeLibraryPaths("", false, "", "")
         }
         whenever(mocks.injector.bootstrap(any(PackageManagerService::class.java))) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/ThermalManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/ThermalManagerServiceMockingTest.java
index 34b17c7..c85ed26 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/ThermalManagerServiceMockingTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/ThermalManagerServiceMockingTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import android.hardware.thermal.CoolingType;
@@ -84,6 +85,42 @@
     }
 
     @Test
+    public void setCallback_illegalState_aidl() throws Exception {
+        Mockito.doThrow(new IllegalStateException()).when(
+                mAidlHalMock).registerThermalChangedCallback(Mockito.any());
+        verifyWrapperStatusOnCallbackError();
+    }
+
+    @Test
+    public void setCallback_illegalArgument_aidl() throws Exception {
+        Mockito.doThrow(new IllegalStateException()).when(
+                mAidlHalMock).registerThermalChangedCallback(Mockito.any());
+        verifyWrapperStatusOnCallbackError();
+    }
+
+
+    void verifyWrapperStatusOnCallbackError() throws RemoteException {
+        android.hardware.thermal.Temperature halT1 = new android.hardware.thermal.Temperature();
+        halT1.type = TemperatureType.MODEM;
+        halT1.name = "test1";
+        Mockito.when(mAidlHalMock.getTemperaturesWithType(Mockito.anyInt())).thenReturn(
+                new android.hardware.thermal.Temperature[]{
+                        halT1
+                });
+        List<Temperature> ret = mAidlWrapper.getCurrentTemperatures(true, TemperatureType.MODEM);
+        Mockito.verify(mAidlHalMock, Mockito.times(1)).getTemperaturesWithType(
+                TemperatureType.MODEM);
+        assertNotNull(ret);
+        Temperature expectedT1 = new Temperature(halT1.value, halT1.type, halT1.name,
+                halT1.throttlingStatus);
+        List<Temperature> expectedRet = List.of(expectedT1);
+        // test that even if the callback fails to register without hal connection error, the
+        // wrapper should still work
+        assertTrue("Got temperature list as " + ret + " with different values compared to "
+                + expectedRet, expectedRet.containsAll(ret));
+    }
+
+    @Test
     public void getCurrentTemperatures_withFilter_aidl() throws RemoteException {
         android.hardware.thermal.Temperature halT1 = new android.hardware.thermal.Temperature();
         halT1.type = TemperatureType.MODEM;
@@ -135,7 +172,42 @@
         List<Temperature> expectedRet = List.of(
                 new Temperature(halTInvalid.value, halTInvalid.type, halTInvalid.name,
                         ThrottlingSeverity.NONE));
-        assertEquals(expectedRet, ret);
+        assertTrue("Got temperature list as " + ret + " with different values compared to "
+                + expectedRet, expectedRet.containsAll(ret));
+    }
+
+    @Test
+    public void getCurrentTemperatures_illegalArgument_aidl() throws RemoteException {
+        Mockito.when(mAidlHalMock.getTemperatures()).thenThrow(new IllegalArgumentException());
+        List<Temperature> ret = mAidlWrapper.getCurrentTemperatures(false, 0);
+        Mockito.verify(mAidlHalMock, Mockito.times(1)).getTemperatures();
+        assertNotNull(ret);
+        assertEquals(0, ret.size());
+
+        Mockito.when(mAidlHalMock.getTemperaturesWithType(TemperatureType.MODEM)).thenThrow(
+                new IllegalArgumentException());
+        ret = mAidlWrapper.getCurrentTemperatures(true, TemperatureType.MODEM);
+        Mockito.verify(mAidlHalMock, Mockito.times(1)).getTemperaturesWithType(
+                TemperatureType.MODEM);
+        assertNotNull(ret);
+        assertEquals(0, ret.size());
+    }
+
+    @Test
+    public void getCurrentTemperatures_illegalState_aidl() throws RemoteException {
+        Mockito.when(mAidlHalMock.getTemperatures()).thenThrow(new IllegalStateException());
+        List<Temperature> ret = mAidlWrapper.getCurrentTemperatures(false, 0);
+        Mockito.verify(mAidlHalMock, Mockito.times(1)).getTemperatures();
+        assertNotNull(ret);
+        assertEquals(0, ret.size());
+
+        Mockito.when(mAidlHalMock.getTemperaturesWithType(TemperatureType.MODEM)).thenThrow(
+                new IllegalStateException());
+        ret = mAidlWrapper.getCurrentTemperatures(true, TemperatureType.MODEM);
+        Mockito.verify(mAidlHalMock, Mockito.times(1)).getTemperaturesWithType(
+                TemperatureType.MODEM);
+        assertNotNull(ret);
+        assertEquals(0, ret.size());
     }
 
     @Test
@@ -187,6 +259,40 @@
     }
 
     @Test
+    public void getCurrentCoolingDevices_illegalArgument_aidl() throws RemoteException {
+        Mockito.when(mAidlHalMock.getCoolingDevices()).thenThrow(new IllegalArgumentException());
+        List<CoolingDevice> ret = mAidlWrapper.getCurrentCoolingDevices(false, 0);
+        Mockito.verify(mAidlHalMock, Mockito.times(1)).getCoolingDevices();
+        assertNotNull(ret);
+        assertEquals(0, ret.size());
+
+        Mockito.when(mAidlHalMock.getCoolingDevicesWithType(Mockito.anyInt())).thenThrow(
+                new IllegalArgumentException());
+        ret = mAidlWrapper.getCurrentCoolingDevices(true, CoolingType.SPEAKER);
+        Mockito.verify(mAidlHalMock, Mockito.times(1)).getCoolingDevicesWithType(
+                CoolingType.SPEAKER);
+        assertNotNull(ret);
+        assertEquals(0, ret.size());
+    }
+
+    @Test
+    public void getCurrentCoolingDevices_illegalState_aidl() throws RemoteException {
+        Mockito.when(mAidlHalMock.getCoolingDevices()).thenThrow(new IllegalStateException());
+        List<CoolingDevice> ret = mAidlWrapper.getCurrentCoolingDevices(false, 0);
+        Mockito.verify(mAidlHalMock, Mockito.times(1)).getCoolingDevices();
+        assertNotNull(ret);
+        assertEquals(0, ret.size());
+
+        Mockito.when(mAidlHalMock.getCoolingDevicesWithType(Mockito.anyInt())).thenThrow(
+                new IllegalStateException());
+        ret = mAidlWrapper.getCurrentCoolingDevices(true, CoolingType.SPEAKER);
+        Mockito.verify(mAidlHalMock, Mockito.times(1)).getCoolingDevicesWithType(
+                CoolingType.SPEAKER);
+        assertNotNull(ret);
+        assertEquals(0, ret.size());
+    }
+
+    @Test
     public void getTemperatureThresholds_withFilter_aidl() throws RemoteException {
         TemperatureThreshold halT1 = new TemperatureThreshold();
         halT1.name = "test1";
@@ -215,4 +321,44 @@
         assertArrayEquals(halT1.hotThrottlingThresholds, threshold.hotThrottlingThresholds, 0.1f);
         assertArrayEquals(halT1.coldThrottlingThresholds, threshold.coldThrottlingThresholds, 0.1f);
     }
+
+    @Test
+    public void getTemperatureThresholds_illegalArgument_aidl() throws RemoteException {
+        Mockito.when(mAidlHalMock.getTemperatureThresholdsWithType(Mockito.anyInt())).thenThrow(
+                new IllegalArgumentException());
+        List<TemperatureThreshold> ret = mAidlWrapper.getTemperatureThresholds(true,
+                Temperature.TYPE_SOC);
+        Mockito.verify(mAidlHalMock, Mockito.times(1)).getTemperatureThresholdsWithType(
+                Temperature.TYPE_SOC);
+        assertNotNull(ret);
+        assertEquals(0, ret.size());
+
+        Mockito.when(mAidlHalMock.getTemperatureThresholds()).thenThrow(
+                new IllegalArgumentException());
+        ret = mAidlWrapper.getTemperatureThresholds(false,
+                Temperature.TYPE_SOC);
+        Mockito.verify(mAidlHalMock, Mockito.times(1)).getTemperatureThresholds();
+        assertNotNull(ret);
+        assertEquals(0, ret.size());
+    }
+
+    @Test
+    public void getTemperatureThresholds_illegalState_aidl() throws RemoteException {
+        Mockito.when(mAidlHalMock.getTemperatureThresholdsWithType(Mockito.anyInt())).thenThrow(
+                new IllegalStateException());
+        List<TemperatureThreshold> ret = mAidlWrapper.getTemperatureThresholds(true,
+                Temperature.TYPE_SOC);
+        Mockito.verify(mAidlHalMock, Mockito.times(1)).getTemperatureThresholdsWithType(
+                Temperature.TYPE_SOC);
+        assertNotNull(ret);
+        assertEquals(0, ret.size());
+
+        Mockito.when(mAidlHalMock.getTemperatureThresholds()).thenThrow(
+                new IllegalStateException());
+        ret = mAidlWrapper.getTemperatureThresholds(false,
+                Temperature.TYPE_SOC);
+        Mockito.verify(mAidlHalMock, Mockito.times(1)).getTemperatureThresholds();
+        assertNotNull(ret);
+        assertEquals(0, ret.size());
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 57f5777..e168596 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -207,7 +207,7 @@
         addA11yWindowInfo(mA11yWindowInfos, PIP_WINDOWID, true, Display.DEFAULT_DISPLAY);
         addA11yWindowInfo(mA11yWindowInfosOnSecondDisplay, WINDOWID_ONSECONDDISPLAY, false,
                 SECONDARY_DISPLAY_ID);
-        when(mMockA11yWindowManager.getDisplayListLocked()).thenReturn(mDisplayList);
+        when(mMockA11yWindowManager.getDisplayListLocked(anyInt())).thenReturn(mDisplayList);
         when(mMockA11yWindowManager.getWindowListLocked(Display.DEFAULT_DISPLAY))
                 .thenReturn(mA11yWindowInfos);
         when(mMockA11yWindowManager.findA11yWindowInfoByIdLocked(WINDOWID))
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index acbcad5..e66a1d4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.accessibility;
 
+import static com.android.server.accessibility.AbstractAccessibilityServiceConnection.DISPLAY_TYPE_DEFAULT;
 import static com.android.server.accessibility.AccessibilityWindowManagerTest.DisplayIdMatcher.displayId;
 import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowChangesMatcher.a11yWindowChanges;
 import static com.android.server.accessibility.AccessibilityWindowManagerTest.WindowIdMatcher.a11yWindowId;
@@ -823,7 +824,8 @@
         // Starts tracking window of second display.
         startTrackingPerDisplay(SECONDARY_DISPLAY_ID);
 
-        final ArrayList<Integer> displayList = mA11yWindowManager.getDisplayListLocked();
+        final ArrayList<Integer> displayList = mA11yWindowManager.getDisplayListLocked(
+                DISPLAY_TYPE_DEFAULT);
         assertTrue(displayList.equals(mExpectedDisplayList));
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 0a4ae6f..ac880ce 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -83,6 +83,7 @@
 import android.os.IBinder;
 import android.os.IPowerManager;
 import android.os.IThermalService;
+import android.os.LocaleList;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -132,7 +133,7 @@
     private static final String GOOGLE_DIALER_PACKAGE_NAME = "com.google.android.dialer";
     private static final String GOOGLE_MAPS_PACKAGE_NAME = "com.google.android.apps.maps";
     private static final String DEVICE_NAME = "device name";
-    private static final int DISPLAY_ID = 2;
+    private static final int DISPLAY_ID_1 = 2;
     private static final int DISPLAY_ID_2 = 3;
     private static final int DEVICE_OWNER_UID_1 = 50;
     private static final int DEVICE_OWNER_UID_2 = 51;
@@ -144,26 +145,27 @@
     private static final int VENDOR_ID = 5;
     private static final String UNIQUE_ID = "uniqueid";
     private static final String PHYS = "phys";
-    private static final int DEVICE_ID = 53;
+    private static final int INPUT_DEVICE_ID = 53;
     private static final int HEIGHT = 1800;
     private static final int WIDTH = 900;
     private static final int SENSOR_HANDLE = 64;
     private static final Binder BINDER = new Binder("binder");
     private static final int FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES = 0x00000;
-    private static final int VIRTUAL_DEVICE_ID = 42;
+    private static final int VIRTUAL_DEVICE_ID_1 = 42;
+    private static final int VIRTUAL_DEVICE_ID_2 = 43;
     private static final VirtualDpadConfig DPAD_CONFIG =
             new VirtualDpadConfig.Builder()
                     .setVendorId(VENDOR_ID)
                     .setProductId(PRODUCT_ID)
                     .setInputDeviceName(DEVICE_NAME)
-                    .setAssociatedDisplayId(DISPLAY_ID)
+                    .setAssociatedDisplayId(DISPLAY_ID_1)
                     .build();
     private static final VirtualKeyboardConfig KEYBOARD_CONFIG =
             new VirtualKeyboardConfig.Builder()
                     .setVendorId(VENDOR_ID)
                     .setProductId(PRODUCT_ID)
                     .setInputDeviceName(DEVICE_NAME)
-                    .setAssociatedDisplayId(DISPLAY_ID)
+                    .setAssociatedDisplayId(DISPLAY_ID_1)
                     .setLanguageTag(VirtualKeyboardConfig.DEFAULT_LANGUAGE_TAG)
                     .setLayoutType(VirtualKeyboardConfig.DEFAULT_LAYOUT_TYPE)
                     .build();
@@ -172,14 +174,14 @@
                     .setVendorId(VENDOR_ID)
                     .setProductId(PRODUCT_ID)
                     .setInputDeviceName(DEVICE_NAME)
-                    .setAssociatedDisplayId(DISPLAY_ID)
+                    .setAssociatedDisplayId(DISPLAY_ID_1)
                     .build();
     private static final VirtualTouchscreenConfig TOUCHSCREEN_CONFIG =
             new VirtualTouchscreenConfig.Builder()
                     .setVendorId(VENDOR_ID)
                     .setProductId(PRODUCT_ID)
                     .setInputDeviceName(DEVICE_NAME)
-                    .setAssociatedDisplayId(DISPLAY_ID)
+                    .setAssociatedDisplayId(DISPLAY_ID_1)
                     .setWidthInPixels(WIDTH)
                     .setHeightInPixels(HEIGHT)
                     .build();
@@ -189,7 +191,7 @@
                     .setVendorId(VENDOR_ID)
                     .setProductId(PRODUCT_ID)
                     .setInputDeviceName(DEVICE_NAME)
-                    .setAssociatedDisplayId(DISPLAY_ID)
+                    .setAssociatedDisplayId(DISPLAY_ID_1)
                     .build();
     private static final String TEST_SITE = "http://test";
 
@@ -250,9 +252,9 @@
     private Intent createRestrictedActivityBlockedIntent(List displayCategories,
             String targetDisplayCategory) {
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(displayCategories), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(displayCategories), DISPLAY_ID_1);
         GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
-                DISPLAY_ID);
+                DISPLAY_ID_1);
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
 
         ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
@@ -320,7 +322,7 @@
         mInputController = new InputController(new Object(), mNativeWrapperMock,
                 new Handler(TestableLooper.get(this).getLooper()),
                 mContext.getSystemService(WindowManager.class), threadVerifier);
-        mSensorController = new SensorController(new Object(), VIRTUAL_DEVICE_ID);
+        mSensorController = new SensorController(new Object(), VIRTUAL_DEVICE_ID_1);
 
         mAssociationInfo = new AssociationInfo(/* associationId= */ 1, 0, null,
                 MacAddress.BROADCAST_ADDRESS, "", null, null, true, false, false, 0, 0);
@@ -328,7 +330,7 @@
         mVdms = new VirtualDeviceManagerService(mContext);
         mLocalService = mVdms.getLocalServiceInstance();
         mVdm = mVdms.new VirtualDeviceManagerImpl();
-        mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID, DEVICE_OWNER_UID_1);
+        mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1);
     }
 
     @Test
@@ -345,17 +347,17 @@
 
     @Test
     public void getDeviceIdForDisplayId_nonExistentDisplayId_returnsDefault() {
-        mDeviceImpl.mVirtualDisplayIds.remove(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.remove(DISPLAY_ID_1);
 
-        assertThat(mVdm.getDeviceIdForDisplayId(DISPLAY_ID))
+        assertThat(mVdm.getDeviceIdForDisplayId(DISPLAY_ID_1))
                 .isEqualTo(DEVICE_ID_DEFAULT);
     }
 
     @Test
     public void getDeviceIdForDisplayId_withValidVirtualDisplayId_returnsDeviceId() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
 
-        assertThat(mVdm.getDeviceIdForDisplayId(DISPLAY_ID))
+        assertThat(mVdm.getDeviceIdForDisplayId(DISPLAY_ID_1))
                 .isEqualTo(mDeviceImpl.getDeviceId());
     }
 
@@ -391,7 +393,7 @@
                 .setDevicePolicy(POLICY_TYPE_SENSORS, DEVICE_POLICY_CUSTOM)
                 .build();
         mDeviceImpl = new VirtualDeviceImpl(mContext,
-                mAssociationInfo, new Binder(), /* ownerUid */ 0, VIRTUAL_DEVICE_ID,
+                mAssociationInfo, new Binder(), /* ownerUid */ 0, VIRTUAL_DEVICE_ID_1,
                 mInputController, mSensorController,
                 /* onDeviceCloseListener= */ (int deviceId) -> {},
                 mPendingTrampolineCallback, mActivityListener, mRunningAppsChangedCallback, params);
@@ -409,15 +411,12 @@
 
     @Test
     public void getDeviceOwnerUid_twoDevices_returnsCorrectId() {
-        int firstDeviceId = mDeviceImpl.getDeviceId();
-        int secondDeviceId = VIRTUAL_DEVICE_ID + 1;
+        createVirtualDevice(VIRTUAL_DEVICE_ID_2, DEVICE_OWNER_UID_2);
 
-        createVirtualDevice(secondDeviceId, DEVICE_OWNER_UID_2);
-
-        int secondDeviceOwner = mLocalService.getDeviceOwnerUid(secondDeviceId);
+        int secondDeviceOwner = mLocalService.getDeviceOwnerUid(VIRTUAL_DEVICE_ID_2);
         assertThat(secondDeviceOwner).isEqualTo(DEVICE_OWNER_UID_2);
 
-        int firstDeviceOwner = mLocalService.getDeviceOwnerUid(firstDeviceId);
+        int firstDeviceOwner = mLocalService.getDeviceOwnerUid(VIRTUAL_DEVICE_ID_1);
         assertThat(firstDeviceOwner).isEqualTo(DEVICE_OWNER_UID_1);
     }
 
@@ -438,7 +437,7 @@
     public void getDeviceIdsForUid_differentUidOnDevice_returnsNull() {
         GenericWindowPolicyController gwpc =
                 mDeviceImpl.createWindowPolicyController(new ArrayList<>());
-        mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID);
+        mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID_1);
         gwpc.onRunningAppsChanged(Sets.newArraySet(UID_2));
 
         Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
@@ -449,7 +448,7 @@
     public void getDeviceIdsForUid_oneUidOnDevice_returnsCorrectId() {
         GenericWindowPolicyController gwpc =
                 mDeviceImpl.createWindowPolicyController(new ArrayList<>());
-        mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID);
+        mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID_1);
         gwpc.onRunningAppsChanged(Sets.newArraySet(UID_1));
 
         Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
@@ -460,7 +459,7 @@
     public void getDeviceIdsForUid_twoUidsOnDevice_returnsCorrectId() {
         GenericWindowPolicyController gwpc =
                 mDeviceImpl.createWindowPolicyController(new ArrayList<>());
-        mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID);
+        mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID_1);
         gwpc.onRunningAppsChanged(Sets.newArraySet(UID_1, UID_2));
 
         Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
@@ -469,9 +468,8 @@
 
     @Test
     public void getDeviceIdsForUid_twoDevicesUidOnOne_returnsCorrectId() {
-        int secondDeviceId = VIRTUAL_DEVICE_ID + 1;
-
-        VirtualDeviceImpl secondDevice = createVirtualDevice(secondDeviceId, DEVICE_OWNER_UID_2);
+        VirtualDeviceImpl secondDevice = createVirtualDevice(VIRTUAL_DEVICE_ID_2,
+                DEVICE_OWNER_UID_2);
 
         GenericWindowPolicyController gwpc =
                 secondDevice.createWindowPolicyController(new ArrayList<>());
@@ -484,14 +482,13 @@
 
     @Test
     public void getDeviceIdsForUid_twoDevicesUidOnBoth_returnsCorrectId() {
-        int secondDeviceId = VIRTUAL_DEVICE_ID + 1;
-
-        VirtualDeviceImpl secondDevice = createVirtualDevice(secondDeviceId, DEVICE_OWNER_UID_2);
+        VirtualDeviceImpl secondDevice = createVirtualDevice(VIRTUAL_DEVICE_ID_2,
+                DEVICE_OWNER_UID_2);
         GenericWindowPolicyController gwpc1 =
                 mDeviceImpl.createWindowPolicyController(new ArrayList<>());
         GenericWindowPolicyController gwpc2 =
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>());
-        mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc1, DISPLAY_ID);
+                secondDevice.createWindowPolicyController(new ArrayList<>());
+        mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc1, DISPLAY_ID_1);
         secondDevice.onVirtualDisplayCreatedLocked(gwpc2, DISPLAY_ID_2);
         gwpc1.onRunningAppsChanged(Sets.newArraySet(UID_1));
         gwpc2.onRunningAppsChanged(Sets.newArraySet(UID_1, UID_2));
@@ -502,33 +499,90 @@
     }
 
     @Test
+    public void getPreferredLocaleListForApp_keyboardAttached_returnLocaleHints() {
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+
+        mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER);
+
+        mVdms.notifyRunningAppsChanged(mDeviceImpl.getDeviceId(), Sets.newArraySet(UID_1));
+
+        LocaleList localeList = mLocalService.getPreferredLocaleListForUid(UID_1);
+        assertThat(localeList).isEqualTo(
+                LocaleList.forLanguageTags(KEYBOARD_CONFIG.getLanguageTag()));
+    }
+
+    @Test
+    public void getPreferredLocaleListForApp_noKeyboardAttached_nullLocaleHints() {
+        mVdms.notifyRunningAppsChanged(mDeviceImpl.getDeviceId(), Sets.newArraySet(UID_1));
+
+        // no preceding call to createVirtualKeyboard()
+        assertThat(mLocalService.getPreferredLocaleListForUid(UID_1)).isNull();
+    }
+
+    @Test
+    public void getPreferredLocaleListForApp_appOnMultipleVD_localeOnFirstVDReturned() {
+        VirtualDeviceImpl secondDevice = createVirtualDevice(VIRTUAL_DEVICE_ID_2,
+                DEVICE_OWNER_UID_2);
+        Binder secondBinder = new Binder("secondBinder");
+        VirtualKeyboardConfig firstKeyboardConfig =
+                new VirtualKeyboardConfig.Builder()
+                        .setVendorId(VENDOR_ID)
+                        .setProductId(PRODUCT_ID)
+                        .setInputDeviceName(DEVICE_NAME)
+                        .setAssociatedDisplayId(DISPLAY_ID_1)
+                        .setLanguageTag("zh-CN")
+                        .build();
+        VirtualKeyboardConfig secondKeyboardConfig =
+                new VirtualKeyboardConfig.Builder()
+                        .setVendorId(VENDOR_ID)
+                        .setProductId(PRODUCT_ID)
+                        .setInputDeviceName(DEVICE_NAME)
+                        .setAssociatedDisplayId(DISPLAY_ID_2)
+                        .setLanguageTag("fr-FR")
+                        .build();
+
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        secondDevice.mVirtualDisplayIds.add(DISPLAY_ID_2);
+
+        mDeviceImpl.createVirtualKeyboard(firstKeyboardConfig, BINDER);
+        secondDevice.createVirtualKeyboard(secondKeyboardConfig, secondBinder);
+
+        mVdms.notifyRunningAppsChanged(mDeviceImpl.getDeviceId(), Sets.newArraySet(UID_1));
+        mVdms.notifyRunningAppsChanged(secondDevice.getDeviceId(), Sets.newArraySet(UID_1));
+
+        LocaleList localeList = mLocalService.getPreferredLocaleListForUid(UID_1);
+        assertThat(localeList).isEqualTo(
+                LocaleList.forLanguageTags(firstKeyboardConfig.getLanguageTag()));
+    }
+
+    @Test
     public void onVirtualDisplayRemovedLocked_doesNotThrowException() {
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
         // This call should not throw any exceptions.
-        mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID);
+        mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID_1);
     }
 
     @Test
     public void onVirtualDisplayCreatedLocked_listenersNotified() {
         mLocalService.registerVirtualDisplayListener(mDisplayListener);
 
-        mLocalService.onVirtualDisplayCreated(DISPLAY_ID);
+        mLocalService.onVirtualDisplayCreated(DISPLAY_ID_1);
         TestableLooper.get(this).processAllMessages();
 
-        verify(mDisplayListener).onVirtualDisplayCreated(DISPLAY_ID);
+        verify(mDisplayListener).onVirtualDisplayCreated(DISPLAY_ID_1);
     }
 
     @Test
     public void onVirtualDisplayRemovedLocked_listenersNotified() {
         mLocalService.registerVirtualDisplayListener(mDisplayListener);
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
 
-        mLocalService.onVirtualDisplayRemoved(mDeviceImpl, DISPLAY_ID);
+        mLocalService.onVirtualDisplayRemoved(mDeviceImpl, DISPLAY_ID_1);
         TestableLooper.get(this).processAllMessages();
 
-        verify(mDisplayListener).onVirtualDisplayRemoved(DISPLAY_ID);
+        verify(mDisplayListener).onVirtualDisplayRemoved(DISPLAY_ID_1);
     }
 
     @Test
@@ -585,10 +639,10 @@
                 nullable(String.class), nullable(String.class), nullable(WorkSource.class),
                 nullable(String.class), anyInt(), eq(null));
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
         verify(mIPowerManagerMock).acquireWakeLock(any(Binder.class), anyInt(),
                 nullable(String.class), nullable(String.class), nullable(WorkSource.class),
-                nullable(String.class), eq(DISPLAY_ID), eq(null));
+                nullable(String.class), eq(DISPLAY_ID_1), eq(null));
     }
 
     @Test
@@ -597,13 +651,13 @@
         GenericWindowPolicyController gwpc = mDeviceImpl.createWindowPolicyController(
                 new ArrayList<>());
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
         assertThrows(IllegalStateException.class,
-                () -> mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID));
+                () -> mDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, DISPLAY_ID_1));
         TestableLooper.get(this).processAllMessages();
         verify(mIPowerManagerMock).acquireWakeLock(any(Binder.class), anyInt(),
                 nullable(String.class), nullable(String.class), nullable(WorkSource.class),
-                nullable(String.class), eq(DISPLAY_ID), eq(null));
+                nullable(String.class), eq(DISPLAY_ID_1), eq(null));
     }
 
     @Test
@@ -616,29 +670,29 @@
     @Test
     public void onVirtualDisplayRemovedLocked_wakeLockIsReleased() throws RemoteException {
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
         ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class);
         TestableLooper.get(this).processAllMessages();
         verify(mIPowerManagerMock).acquireWakeLock(wakeLockCaptor.capture(),
                 anyInt(),
                 nullable(String.class), nullable(String.class), nullable(WorkSource.class),
-                nullable(String.class), eq(DISPLAY_ID), eq(null));
+                nullable(String.class), eq(DISPLAY_ID_1), eq(null));
 
         IBinder wakeLock = wakeLockCaptor.getValue();
-        mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID);
+        mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID_1);
         verify(mIPowerManagerMock).releaseWakeLock(eq(wakeLock), anyInt());
     }
 
     @Test
     public void addVirtualDisplay_displayNotReleased_wakeLockIsReleased() throws RemoteException {
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
         ArgumentCaptor<IBinder> wakeLockCaptor = ArgumentCaptor.forClass(IBinder.class);
         TestableLooper.get(this).processAllMessages();
         verify(mIPowerManagerMock).acquireWakeLock(wakeLockCaptor.capture(),
                 anyInt(),
                 nullable(String.class), nullable(String.class), nullable(WorkSource.class),
-                nullable(String.class), eq(DISPLAY_ID), eq(null));
+                nullable(String.class), eq(DISPLAY_ID_1), eq(null));
         IBinder wakeLock = wakeLockCaptor.getValue();
 
         // Close the VirtualDevice without first notifying it of the VirtualDisplay removal.
@@ -672,13 +726,13 @@
 
     @Test
     public void createVirtualTouchscreen_zeroDisplayDimension_failsIllegalArgumentException() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         final VirtualTouchscreenConfig zeroConfig =
                 new VirtualTouchscreenConfig.Builder()
                         .setVendorId(VENDOR_ID)
                         .setProductId(PRODUCT_ID)
                         .setInputDeviceName(DEVICE_NAME)
-                        .setAssociatedDisplayId(DISPLAY_ID)
+                        .setAssociatedDisplayId(DISPLAY_ID_1)
                         .setWidthInPixels(0)
                         .setHeightInPixels(0)
                         .build();
@@ -688,13 +742,13 @@
 
     @Test
     public void createVirtualTouchscreen_negativeDisplayDimension_failsIllegalArgumentException() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         final VirtualTouchscreenConfig negativeConfig =
                 new VirtualTouchscreenConfig.Builder()
                         .setVendorId(VENDOR_ID)
                         .setProductId(PRODUCT_ID)
                         .setInputDeviceName(DEVICE_NAME)
-                        .setAssociatedDisplayId(DISPLAY_ID)
+                        .setAssociatedDisplayId(DISPLAY_ID_1)
                         .setWidthInPixels(-100)
                         .setHeightInPixels(-100)
                         .build();
@@ -705,20 +759,20 @@
 
     @Test
     public void createVirtualTouchscreen_positiveDisplayDimension_successful() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         VirtualTouchscreenConfig positiveConfig =
                 new VirtualTouchscreenConfig.Builder()
                         .setVendorId(VENDOR_ID)
                         .setProductId(PRODUCT_ID)
                         .setInputDeviceName(DEVICE_NAME)
-                        .setAssociatedDisplayId(DISPLAY_ID)
+                        .setAssociatedDisplayId(DISPLAY_ID_1)
                         .setWidthInPixels(600)
                         .setHeightInPixels(800)
                         .build();
         mDeviceImpl.createVirtualTouchscreen(positiveConfig, BINDER);
         assertWithMessage(
-            "Virtual touchscreen should create input device descriptor on successful creation"
-                + ".").that(mInputController.getInputDeviceDescriptors()).isNotEmpty();
+                "Virtual touchscreen should create input device descriptor on successful creation"
+                        + ".").that(mInputController.getInputDeviceDescriptors()).isNotEmpty();
     }
 
     @Test
@@ -730,66 +784,66 @@
 
     @Test
     public void createVirtualNavigationTouchpad_zeroDisplayDimension_failsWithException() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         assertThrows(IllegalArgumentException.class,
                 () -> {
-                final VirtualNavigationTouchpadConfig zeroConfig =
-                        new VirtualNavigationTouchpadConfig.Builder(
-                                /* touchpadHeight= */ 0, /* touchpadWidth= */ 0)
-                                .setVendorId(VENDOR_ID)
-                                .setProductId(PRODUCT_ID)
-                                .setInputDeviceName(DEVICE_NAME)
-                                .setAssociatedDisplayId(DISPLAY_ID)
-                                .build();
-                mDeviceImpl.createVirtualNavigationTouchpad(zeroConfig, BINDER);
-            });
+                    final VirtualNavigationTouchpadConfig zeroConfig =
+                            new VirtualNavigationTouchpadConfig.Builder(
+                                    /* touchpadHeight= */ 0, /* touchpadWidth= */ 0)
+                                    .setVendorId(VENDOR_ID)
+                                    .setProductId(PRODUCT_ID)
+                                    .setInputDeviceName(DEVICE_NAME)
+                                    .setAssociatedDisplayId(DISPLAY_ID_1)
+                                    .build();
+                    mDeviceImpl.createVirtualNavigationTouchpad(zeroConfig, BINDER);
+                });
     }
 
     @Test
     public void createVirtualNavigationTouchpad_negativeDisplayDimension_failsWithException() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         assertThrows(IllegalArgumentException.class,
                 () -> {
-                    final VirtualNavigationTouchpadConfig zeroConfig =
+                    final VirtualNavigationTouchpadConfig negativeConfig =
                             new VirtualNavigationTouchpadConfig.Builder(
                                     /* touchpadHeight= */ -50, /* touchpadWidth= */ 50)
                                     .setVendorId(VENDOR_ID)
                                     .setProductId(PRODUCT_ID)
                                     .setInputDeviceName(DEVICE_NAME)
-                                    .setAssociatedDisplayId(DISPLAY_ID)
+                                    .setAssociatedDisplayId(DISPLAY_ID_1)
                                     .build();
-                mDeviceImpl.createVirtualNavigationTouchpad(zeroConfig, BINDER);
-            });
+                    mDeviceImpl.createVirtualNavigationTouchpad(negativeConfig, BINDER);
+                });
     }
 
     @Test
     public void createVirtualNavigationTouchpad_positiveDisplayDimension_successful() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         VirtualNavigationTouchpadConfig positiveConfig =
                 new VirtualNavigationTouchpadConfig.Builder(
-                            /* touchpadHeight= */ 50, /* touchpadWidth= */ 50)
+                        /* touchpadHeight= */ 50, /* touchpadWidth= */ 50)
                         .setVendorId(VENDOR_ID)
                         .setProductId(PRODUCT_ID)
                         .setInputDeviceName(DEVICE_NAME)
-                        .setAssociatedDisplayId(DISPLAY_ID)
+                        .setAssociatedDisplayId(DISPLAY_ID_1)
                         .build();
         mDeviceImpl.createVirtualNavigationTouchpad(positiveConfig, BINDER);
         assertWithMessage(
-            "Virtual navigation touchpad should create input device descriptor on successful "
-            + "creation"
-                + ".").that(mInputController.getInputDeviceDescriptors()).isNotEmpty();
+                "Virtual navigation touchpad should create input device descriptor on successful "
+                        + "creation"
+                        + ".").that(mInputController.getInputDeviceDescriptors()).isNotEmpty();
     }
 
     @Test
     public void onAudioSessionStarting_noDisplay_failsSecurityException() {
         assertThrows(SecurityException.class,
                 () -> mDeviceImpl.onAudioSessionStarting(
-                        DISPLAY_ID, mRoutingCallback, mConfigChangedCallback));
+                        DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback));
     }
 
     @Test
     public void createVirtualDpad_noPermission_failsSecurityException() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         doCallRealMethod().when(mContext).enforceCallingOrSelfPermission(
                 eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
         assertThrows(SecurityException.class,
@@ -798,7 +852,7 @@
 
     @Test
     public void createVirtualKeyboard_noPermission_failsSecurityException() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         doCallRealMethod().when(mContext).enforceCallingOrSelfPermission(
                 eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
         assertThrows(SecurityException.class,
@@ -807,7 +861,7 @@
 
     @Test
     public void createVirtualMouse_noPermission_failsSecurityException() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         doCallRealMethod().when(mContext).enforceCallingOrSelfPermission(
                 eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
         assertThrows(SecurityException.class,
@@ -816,7 +870,7 @@
 
     @Test
     public void createVirtualTouchscreen_noPermission_failsSecurityException() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         doCallRealMethod().when(mContext).enforceCallingOrSelfPermission(
                 eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
         assertThrows(SecurityException.class,
@@ -825,7 +879,7 @@
 
     @Test
     public void createVirtualNavigationTouchpad_noPermission_failsSecurityException() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         doCallRealMethod().when(mContext).enforceCallingOrSelfPermission(
                 eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
         assertThrows(SecurityException.class,
@@ -847,12 +901,12 @@
 
     @Test
     public void onAudioSessionStarting_noPermission_failsSecurityException() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         doCallRealMethod().when(mContext).enforceCallingOrSelfPermission(
                 eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
         assertThrows(SecurityException.class,
                 () -> mDeviceImpl.onAudioSessionStarting(
-                        DISPLAY_ID, mRoutingCallback, mConfigChangedCallback));
+                        DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback));
     }
 
     @Test
@@ -864,7 +918,7 @@
 
     @Test
     public void createVirtualDpad_hasDisplay_obtainFileDescriptor() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         mDeviceImpl.createVirtualDpad(DPAD_CONFIG, BINDER);
         assertWithMessage("Virtual dpad should register fd when the display matches").that(
                 mInputController.getInputDeviceDescriptors()).isNotEmpty();
@@ -874,7 +928,7 @@
 
     @Test
     public void createVirtualKeyboard_hasDisplay_obtainFileDescriptor() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER);
         assertWithMessage("Virtual keyboard should register fd when the display matches").that(
                 mInputController.getInputDeviceDescriptors()).isNotEmpty();
@@ -883,8 +937,50 @@
     }
 
     @Test
+    public void createVirtualKeyboard_keyboardCreated_localeUpdated() {
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        mDeviceImpl.createVirtualKeyboard(KEYBOARD_CONFIG, BINDER);
+        assertWithMessage("Virtual keyboard should register fd when the display matches")
+                .that(mInputController.getInputDeviceDescriptors())
+                .isNotEmpty();
+        verify(mNativeWrapperMock).openUinputKeyboard(eq(DEVICE_NAME), eq(VENDOR_ID),
+                eq(PRODUCT_ID), anyString());
+        assertThat(mDeviceImpl.getDeviceLocaleList()).isEqualTo(
+                LocaleList.forLanguageTags(KEYBOARD_CONFIG.getLanguageTag()));
+    }
+
+    @Test
+    public void createVirtualKeyboard_keyboardWithoutExplicitLayoutInfo_localeUpdatedWithDefault() {
+        VirtualKeyboardConfig configWithoutExplicitLayoutInfo =
+                new VirtualKeyboardConfig.Builder()
+                        .setVendorId(VENDOR_ID)
+                        .setProductId(PRODUCT_ID)
+                        .setInputDeviceName(DEVICE_NAME)
+                        .setAssociatedDisplayId(DISPLAY_ID_1)
+                        .build();
+
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+        mDeviceImpl.createVirtualKeyboard(configWithoutExplicitLayoutInfo, BINDER);
+        assertWithMessage("Virtual keyboard should register fd when the display matches")
+                .that(mInputController.getInputDeviceDescriptors())
+                .isNotEmpty();
+        verify(mNativeWrapperMock).openUinputKeyboard(eq(DEVICE_NAME), eq(VENDOR_ID),
+                eq(PRODUCT_ID), anyString());
+        assertThat(mDeviceImpl.getDeviceLocaleList()).isEqualTo(
+                LocaleList.forLanguageTags(VirtualKeyboardConfig.DEFAULT_LANGUAGE_TAG));
+    }
+
+    @Test
+    public void virtualDeviceWithoutKeyboard_noLocaleUpdate() {
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
+
+        // no preceding call to createVirtualKeyboard()
+        assertThat(mDeviceImpl.getDeviceLocaleList()).isNull();
+    }
+
+    @Test
     public void createVirtualMouse_hasDisplay_obtainFileDescriptor() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         mDeviceImpl.createVirtualMouse(MOUSE_CONFIG, BINDER);
         assertWithMessage("Virtual mouse should register fd when the display matches").that(
                 mInputController.getInputDeviceDescriptors()).isNotEmpty();
@@ -894,21 +990,21 @@
 
     @Test
     public void createVirtualTouchscreen_hasDisplay_obtainFileDescriptor() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         mDeviceImpl.createVirtualTouchscreen(TOUCHSCREEN_CONFIG, BINDER);
         assertWithMessage("Virtual touchscreen should register fd when the display matches").that(
-            mInputController.getInputDeviceDescriptors()).isNotEmpty();
+                mInputController.getInputDeviceDescriptors()).isNotEmpty();
         verify(mNativeWrapperMock).openUinputTouchscreen(eq(DEVICE_NAME), eq(VENDOR_ID),
                 eq(PRODUCT_ID), anyString(), eq(HEIGHT), eq(WIDTH));
     }
 
     @Test
     public void createVirtualNavigationTouchpad_hasDisplay_obtainFileDescriptor() {
-        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+        mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         mDeviceImpl.createVirtualNavigationTouchpad(NAVIGATION_TOUCHPAD_CONFIG, BINDER);
         assertWithMessage("Virtual navigation touchpad should register fd when the display matches")
-            .that(
-            mInputController.getInputDeviceDescriptors()).isNotEmpty();
+                .that(
+                        mInputController.getInputDeviceDescriptors()).isNotEmpty();
         verify(mNativeWrapperMock).openUinputTouchscreen(eq(DEVICE_NAME), eq(VENDOR_ID),
                 eq(PRODUCT_ID), anyString(), eq(HEIGHT), eq(WIDTH));
     }
@@ -917,18 +1013,18 @@
     public void createVirtualKeyboard_inputDeviceId_obtainFromInputController() {
         final int fd = 1;
         mInputController.addDeviceForTesting(BINDER, fd, /* type= */ 1, /* displayId= */ 1, PHYS,
-                DEVICE_ID);
+                INPUT_DEVICE_ID);
         assertWithMessage(
                 "InputController should return device id from InputDeviceDescriptor").that(
-                mInputController.getInputDeviceId(BINDER)).isEqualTo(DEVICE_ID);
+                mInputController.getInputDeviceId(BINDER)).isEqualTo(INPUT_DEVICE_ID);
     }
 
     @Test
     public void onAudioSessionStarting_hasVirtualAudioController() {
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
 
-        mDeviceImpl.onAudioSessionStarting(DISPLAY_ID, mRoutingCallback, mConfigChangedCallback);
+        mDeviceImpl.onAudioSessionStarting(DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback);
 
         assertThat(mDeviceImpl.getVirtualAudioControllerForTesting()).isNotNull();
     }
@@ -936,8 +1032,8 @@
     @Test
     public void onAudioSessionEnded_noVirtualAudioController() {
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
-        mDeviceImpl.onAudioSessionStarting(DISPLAY_ID, mRoutingCallback, mConfigChangedCallback);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+        mDeviceImpl.onAudioSessionStarting(DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback);
 
         mDeviceImpl.onAudioSessionEnded();
 
@@ -947,8 +1043,8 @@
     @Test
     public void close_cleanVirtualAudioController() {
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
-        mDeviceImpl.onAudioSessionStarting(DISPLAY_ID, mRoutingCallback, mConfigChangedCallback);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
+        mDeviceImpl.onAudioSessionStarting(DISPLAY_ID_1, mRoutingCallback, mConfigChangedCallback);
 
         mDeviceImpl.close();
 
@@ -982,10 +1078,12 @@
         final int keyCode = KeyEvent.KEYCODE_A;
         final int action = VirtualKeyEvent.ACTION_UP;
         mInputController.addDeviceForTesting(BINDER, fd, /* type= */1, /* displayId= */ 1, PHYS,
-                DEVICE_ID);
+                INPUT_DEVICE_ID);
 
-        mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder().setKeyCode(keyCode)
-                .setAction(action).build());
+        mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder()
+                .setKeyCode(keyCode)
+                .setAction(action)
+                .build());
         verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action);
     }
 
@@ -1007,7 +1105,7 @@
         final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK;
         final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
         mInputController.addDeviceForTesting(BINDER, fd, /* type= */2, /* displayId= */ 1, PHYS,
-                DEVICE_ID);
+                INPUT_DEVICE_ID);
         doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
         mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
                 .setButtonCode(buttonCode)
@@ -1021,7 +1119,7 @@
         final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK;
         final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
         mInputController.addDeviceForTesting(BINDER, fd, /* type= */2, /* displayId= */ 1, PHYS,
-                DEVICE_ID);
+                INPUT_DEVICE_ID);
         assertThrows(
                 IllegalStateException.class,
                 () ->
@@ -1046,7 +1144,7 @@
         final float x = -0.2f;
         final float y = 0.7f;
         mInputController.addDeviceForTesting(BINDER, fd, /* type= */2, /* displayId= */ 1, PHYS,
-                DEVICE_ID);
+                INPUT_DEVICE_ID);
         doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
         mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder()
                 .setRelativeX(x).setRelativeY(y).build());
@@ -1059,7 +1157,7 @@
         final float x = -0.2f;
         final float y = 0.7f;
         mInputController.addDeviceForTesting(BINDER, fd, /* type= */2, /* displayId= */ 1, PHYS,
-                DEVICE_ID);
+                INPUT_DEVICE_ID);
         assertThrows(
                 IllegalStateException.class,
                 () ->
@@ -1085,7 +1183,7 @@
         final float x = 0.5f;
         final float y = 1f;
         mInputController.addDeviceForTesting(BINDER, fd, /* type= */2, /* displayId= */ 1, PHYS,
-                DEVICE_ID);
+                INPUT_DEVICE_ID);
         doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
         mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
                 .setXAxisMovement(x)
@@ -1099,7 +1197,7 @@
         final float x = 0.5f;
         final float y = 1f;
         mInputController.addDeviceForTesting(BINDER, fd, /* type= */2, /* displayId= */ 1, PHYS,
-                DEVICE_ID);
+                INPUT_DEVICE_ID);
         assertThrows(
                 IllegalStateException.class,
                 () ->
@@ -1131,9 +1229,14 @@
         final float y = 200.5f;
         final int action = VirtualTouchEvent.ACTION_UP;
         mInputController.addDeviceForTesting(BINDER, fd, /* type= */3, /* displayId= */ 1, PHYS,
-                DEVICE_ID);
-        mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x)
-                .setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType).build());
+                INPUT_DEVICE_ID);
+        mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder()
+                .setX(x)
+                .setY(y)
+                .setAction(action)
+                .setPointerId(pointerId)
+                .setToolType(toolType)
+                .build());
         verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, Float.NaN,
                 Float.NaN);
     }
@@ -1149,10 +1252,16 @@
         final float pressure = 1.0f;
         final float majorAxisSize = 10.0f;
         mInputController.addDeviceForTesting(BINDER, fd, /* type= */3, /* displayId= */ 1, PHYS,
-                DEVICE_ID);
-        mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x)
-                .setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType)
-                .setPressure(pressure).setMajorAxisSize(majorAxisSize).build());
+                INPUT_DEVICE_ID);
+        mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder()
+                .setX(x)
+                .setY(y)
+                .setAction(action)
+                .setPointerId(pointerId)
+                .setToolType(toolType)
+                .setPressure(pressure)
+                .setMajorAxisSize(majorAxisSize)
+                .build());
         verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, pressure,
                 majorAxisSize);
     }
@@ -1194,9 +1303,9 @@
     @Test
     public void openNonBlockedAppOnVirtualDisplay_doesNotStartBlockedAlertActivity() {
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
         GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
-                DISPLAY_ID);
+                DISPLAY_ID_1);
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
 
         ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
@@ -1215,9 +1324,9 @@
     @Test
     public void openPermissionControllerOnVirtualDisplay_startBlockedAlertActivity() {
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
         GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
-                DISPLAY_ID);
+                DISPLAY_ID_1);
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
 
         ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
@@ -1236,9 +1345,9 @@
     @Test
     public void openSettingsOnVirtualDisplay_startBlockedAlertActivity() {
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
         GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
-                DISPLAY_ID);
+                DISPLAY_ID_1);
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
 
         ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
@@ -1257,9 +1366,9 @@
     @Test
     public void openVendingOnVirtualDisplay_startBlockedAlertActivity() {
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
         GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
-                DISPLAY_ID);
+                DISPLAY_ID_1);
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
 
         ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
@@ -1278,9 +1387,9 @@
     @Test
     public void openGoogleDialerOnVirtualDisplay_startBlockedAlertActivity() {
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
         GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
-                DISPLAY_ID);
+                DISPLAY_ID_1);
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
 
         ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
@@ -1299,9 +1408,9 @@
     @Test
     public void openGoogleMapsOnVirtualDisplay_startBlockedAlertActivity() {
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
         GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
-                DISPLAY_ID);
+                DISPLAY_ID_1);
         doNothing().when(mContext).startActivityAsUser(any(), any(), any());
 
         ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
@@ -1321,9 +1430,9 @@
     public void registerRunningAppsChangedListener_onRunningAppsChanged_listenersNotified() {
         ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2));
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
         GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
-                DISPLAY_ID);
+                DISPLAY_ID_1);
 
         gwpc.onRunningAppsChanged(uids);
         mDeviceImpl.onRunningAppsChanged(uids);
@@ -1336,10 +1445,10 @@
     public void noRunningAppsChangedListener_onRunningAppsChanged_doesNotThrowException() {
         ArraySet<Integer> uids = new ArraySet<>(Arrays.asList(UID_1, UID_2));
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
         GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
-                DISPLAY_ID);
-        mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID);
+                DISPLAY_ID_1);
+        mDeviceImpl.onVirtualDisplayRemovedLocked(DISPLAY_ID_1);
 
         // This call should not throw any exceptions.
         gwpc.onRunningAppsChanged(uids);
@@ -1351,17 +1460,17 @@
     public void canActivityBeLaunched_activityCanLaunch() {
         Intent intent = new Intent(ACTION_VIEW, Uri.parse(TEST_SITE));
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
         GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
-                            DISPLAY_ID);
+                DISPLAY_ID_1);
         ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
                 NONBLOCKED_APP_PACKAGE_NAME,
                 NONBLOCKED_APP_PACKAGE_NAME,
-            /* displayOnRemoveDevices */ true,
-            /* targetDisplayCategory */ null);
+                /* displayOnRemoveDevices */ true,
+                /* targetDisplayCategory */ null);
         assertThat(gwpc.canActivityBeLaunched(activityInfos.get(0), intent,
-            WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
-            .isTrue();
+                WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false))
+                .isTrue();
     }
 
     @Test
@@ -1376,14 +1485,14 @@
         doReturn(interceptor).when(interceptor).queryLocalInterface(anyString());
 
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
         GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
-                            DISPLAY_ID);
+                DISPLAY_ID_1);
         ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
                 NONBLOCKED_APP_PACKAGE_NAME,
                 NONBLOCKED_APP_PACKAGE_NAME,
-            /* displayOnRemoveDevices */ true,
-            /* targetDisplayCategory */ null);
+                /* displayOnRemoveDevices */ true,
+                /* targetDisplayCategory */ null);
 
         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_VIEW);
         intentFilter.addDataScheme(IntentFilter.SCHEME_HTTP);
@@ -1392,8 +1501,8 @@
         // register interceptor and intercept intent
         mDeviceImpl.registerIntentInterceptor(interceptor, intentFilter);
         assertThat(gwpc.canActivityBeLaunched(activityInfos.get(0), intent,
-            WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
-            .isFalse();
+                WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false))
+                .isFalse();
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
         verify(interceptor).onIntentIntercepted(intentCaptor.capture());
         Intent cIntent = intentCaptor.getValue();
@@ -1404,8 +1513,8 @@
         // unregister interceptor and launch activity
         mDeviceImpl.unregisterIntentInterceptor(interceptor);
         assertThat(gwpc.canActivityBeLaunched(activityInfos.get(0), intent,
-            WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
-            .isTrue();
+                WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false))
+                .isTrue();
     }
 
     @Test
@@ -1420,14 +1529,14 @@
         doReturn(interceptor).when(interceptor).queryLocalInterface(anyString());
 
         mDeviceImpl.onVirtualDisplayCreatedLocked(
-                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
+                mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID_1);
         GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
-                            DISPLAY_ID);
+                DISPLAY_ID_1);
         ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
                 NONBLOCKED_APP_PACKAGE_NAME,
                 NONBLOCKED_APP_PACKAGE_NAME,
-            /* displayOnRemoveDevices */ true,
-            /* targetDisplayCategory */ null);
+                /* displayOnRemoveDevices */ true,
+                /* targetDisplayCategory */ null);
 
         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_VIEW);
         intentFilter.addDataScheme("mailto");
@@ -1436,8 +1545,8 @@
         mDeviceImpl.registerIntentInterceptor(interceptor, intentFilter);
 
         assertThat(gwpc.canActivityBeLaunched(activityInfos.get(0), intent,
-            WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
-            .isTrue();
+                WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID_1, /*isNewTask=*/false))
+                .isTrue();
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
index 8f6dd5d..10f27ca 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
@@ -18,12 +18,11 @@
 
 import android.content.om.OverlayInfo
 import android.content.om.OverlayableInfo
-import android.content.pm.ApplicationInfo
-import android.content.pm.PackageInfo
 import android.os.Process
 import android.util.ArrayMap
 import com.android.server.om.OverlayActorEnforcer.ActorState
 import com.android.server.pm.pkg.AndroidPackage
+import com.android.server.pm.pkg.PackageState
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.whenever
 import com.google.common.truth.Truth.assertThat
@@ -31,7 +30,6 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
-import org.mockito.Mockito
 import org.mockito.Mockito.spy
 import java.io.IOException
 
@@ -126,13 +124,11 @@
          */
         private val CASES = listOf(
                 ActorState.TARGET_NOT_FOUND withCases {
-                    failure("nullPkgInfo") { targetPkgInfo = null }
+                    failure("nullPkgInfo") { targetPkgState = null }
                     allowed("debuggable") {
-                        targetPkgInfo = androidPackage(TARGET_PKG).apply {
-                            whenever(this.isDebuggable).thenReturn(true)
-                        }
+                        targetPkgState = packageState(TARGET_PKG, debuggable = true)
                     }
-                    skip { targetPkgInfo = androidPackage(TARGET_PKG) }
+                    skip { targetPkgState = packageState(TARGET_PKG) }
                 },
                 ActorState.NO_PACKAGES_FOR_UID withCases {
                     failure("empty") { callingUid = EMPTY_UID }
@@ -240,21 +236,15 @@
                     }
                 },
                 ActorState.ACTOR_NOT_FOUND withCases {
-                    failure("nullActorPkgInfo") { actorPkgInfo = null }
+                    failure("nullActorPkgInfo") { actorPkgState = null }
                     failure("nullActorAppInfo") {
-                        actorPkgInfo = null
+                        actorPkgState = null
                     }
-                    skip { actorPkgInfo = androidPackage(VALID_ACTOR_PKG) }
+                    skip { actorPkgState = packageState(VALID_ACTOR_PKG) }
                 },
                 ActorState.ACTOR_NOT_PREINSTALLED withCases {
-                    failure("notSystem") {
-                        actorPkgInfo = androidPackage(VALID_ACTOR_PKG)
-                    }
-                    skip {
-                        actorPkgInfo = androidPackage(VALID_ACTOR_PKG).apply {
-                            whenever(this.isSystem).thenReturn(true)
-                        }
-                    }
+                    failure("notSystem") { actorPkgState = packageState(VALID_ACTOR_PKG) }
+                    skip { actorPkgState = packageState(VALID_ACTOR_PKG, isSystem = true) }
                 },
                 ActorState.INVALID_ACTOR withCases {
                     failure("invalidUid") { callingUid = INVALID_ACTOR_UID }
@@ -285,10 +275,18 @@
         private infix fun ActorState.withCases(block: TestCase.() -> Unit) =
                 TestCase(this).apply(block)
 
-        private fun androidPackage(pkgName: String): AndroidPackage = mockThrowOnUnmocked {
+        private fun packageState(
+            pkgName: String,
+            debuggable: Boolean = false,
+            isSystem: Boolean = false
+        ) = mockThrowOnUnmocked<PackageState> {
             whenever(this.packageName).thenReturn(pkgName)
-            whenever(this.isDebuggable).thenReturn(false)
-            whenever(this.isSystem).thenReturn(false)
+            whenever(this.isSystem).thenReturn(isSystem)
+            val androidPackage = mockThrowOnUnmocked<AndroidPackage> {
+                whenever(this.packageName).thenReturn(pkgName)
+                whenever(this.isDebuggable).thenReturn(debuggable)
+            }
+            whenever(this.androidPackage).thenReturn(androidPackage)
         }
 
         private fun makeTestName(testCase: TestCase, caseName: String, type: Params.Type): String {
@@ -364,8 +362,8 @@
         var namedActorsMap: Map<String, Map<String, String>> = emptyMap(),
         var hasPermission: Boolean = false,
         var targetOverlayableInfo: OverlayableInfo? = null,
-        var targetPkgInfo: AndroidPackage? = null,
-        var actorPkgInfo: AndroidPackage? = null,
+        var targetPkgState: PackageState? = null,
+        var actorPkgState: PackageState? = null,
         vararg val packageNames: String = arrayOf("com.test.actor.one")
     ) : PackageManagerHelper {
 
@@ -380,7 +378,7 @@
             throw UnsupportedOperationException()
         }
 
-        override fun initializeForUser(userId: Int): ArrayMap<String, AndroidPackage> {
+        override fun initializeForUser(userId: Int): ArrayMap<String, PackageState> {
             throw UnsupportedOperationException()
         }
 
@@ -391,7 +389,7 @@
             userId: Int
         ) = targetOverlayableInfo?.takeIf {
             // Protect against this method being called with the wrong package name
-            targetPkgInfo == null || targetPkgInfo?.packageName == packageName
+            targetPkgState == null || targetPkgState?.packageName == packageName
         }
 
         override fun getPackagesForUid(uid: Int) = when (uid) {
@@ -407,7 +405,7 @@
         override fun doesTargetDefineOverlayable(targetPackageName: String?, userId: Int): Boolean {
             return targetOverlayableInfo?.takeIf {
                 // Protect against this method being called with the wrong package name
-                targetPkgInfo == null || targetPkgInfo?.packageName == targetPackageName
+                targetPkgState == null || targetPkgState?.packageName == targetPackageName
             } != null
         }
 
@@ -417,8 +415,8 @@
             }
         }
 
-        override fun getPackageForUser(packageName: String, userId: Int) =
-            listOfNotNull(targetPkgInfo, actorPkgInfo).find { it.packageName == packageName }
+        override fun getPackageStateForUser(packageName: String, userId: Int) =
+            listOfNotNull(targetPkgState, actorPkgState).find { it.packageName == packageName }
 
         override fun getConfigSignaturePackage(): String {
             throw UnsupportedOperationException()
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
index dab4335..3e82d45 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
@@ -40,6 +40,8 @@
 
 import com.android.internal.content.om.OverlayConfig;
 import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.AndroidPackageSplit;
+import com.android.server.pm.pkg.PackageState;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -357,18 +359,24 @@
             }
 
             @Nullable
-            private AndroidPackage getPackageForUser(int user) {
+            private PackageState getPackageForUser(int user) {
                 if (!installedUserIds.contains(user)) {
                     return null;
                 }
                 final AndroidPackage pkg = Mockito.mock(AndroidPackage.class);
                 when(pkg.getPackageName()).thenReturn(packageName);
-                when(pkg.getBaseApkPath()).thenReturn(apkPath);
                 when(pkg.getLongVersionCode()).thenReturn((long) versionCode);
                 when(pkg.getOverlayTarget()).thenReturn(targetPackageName);
                 when(pkg.getOverlayTargetOverlayableName()).thenReturn(targetOverlayableName);
                 when(pkg.getOverlayCategory()).thenReturn("Fake-category-" + targetPackageName);
-                return pkg;
+                var baseSplit = mock(AndroidPackageSplit.class);
+                when(baseSplit.getPath()).thenReturn(apkPath);
+                when(pkg.getSplits()).thenReturn(List.of(baseSplit));
+
+                var pkgState = Mockito.mock(PackageState.class);
+                when(pkgState.getPackageName()).thenReturn(packageName);
+                when(pkgState.getAndroidPackage()).thenReturn(pkg);
+                return pkgState;
             }
         }
     }
@@ -382,10 +390,10 @@
 
         @NonNull
         @Override
-        public ArrayMap<String, AndroidPackage> initializeForUser(int userId) {
-            final ArrayMap<String, AndroidPackage> packages = new ArrayMap<>();
+        public ArrayMap<String, PackageState> initializeForUser(int userId) {
+            final ArrayMap<String, PackageState> packages = new ArrayMap<>();
             mState.mPackages.forEach((key, value) -> {
-                final AndroidPackage pkg = value.getPackageForUser(userId);
+                final PackageState pkg = value.getPackageForUser(userId);
                 if (pkg != null) {
                     packages.put(key, pkg);
                 }
@@ -395,7 +403,7 @@
 
         @Nullable
         @Override
-        public AndroidPackage getPackageForUser(@NonNull String packageName, int userId) {
+        public PackageState getPackageStateForUser(@NonNull String packageName, int userId) {
             final FakeDeviceState.Package pkgState = mState.select(packageName, userId);
             return pkgState == null ? null : pkgState.getPackageForUser(userId);
         }
@@ -466,7 +474,7 @@
 
         private int getCrc(@NonNull final String path) {
             final FakeDeviceState.Package pkg = mState.selectFromPath(path);
-            Assert.assertNotNull(pkg);
+            Assert.assertNotNull("path = " + path, pkg);
             return pkg.versionCode;
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
index eb91671..77bdf19 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -28,11 +28,12 @@
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
-import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.PackageState;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnitRunner;
 
 
@@ -52,78 +53,90 @@
 
     @Test
     public void getSeInfoOptInToLatest() {
-        AndroidPackage pkg = makePackage(Build.VERSION_CODES.P);
+        var packageState = makePackageState(Build.VERSION_CODES.P);
         when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
-                argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+                argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
                 .thenReturn(true);
-        assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+        assertThat(SELinuxMMAC.getSeInfo(packageState, packageState.getAndroidPackage(), null,
+                        mMockCompatibility),
                 is("default:targetSdkVersion=" + LATEST_OPT_IN_VERSION));
     }
 
     @Test
     public void getSeInfoOptInToR() {
-        AndroidPackage pkg = makePackage(Build.VERSION_CODES.P);
+        var packageState = makePackageState(Build.VERSION_CODES.P);
         when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
-                argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+                argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
                 .thenReturn(true);
-        assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+        assertThat(SELinuxMMAC.getSeInfo(packageState, packageState.getAndroidPackage(), null,
+                        mMockCompatibility),
                 is("default:targetSdkVersion=" + R_OPT_IN_VERSION));
     }
 
     @Test
     public void getSeInfoNoOptIn() {
-        AndroidPackage pkg = makePackage(Build.VERSION_CODES.P);
+        var packageState = makePackageState(Build.VERSION_CODES.P);
         when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
-                argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+                argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
                 .thenReturn(false);
-        assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+        assertThat(SELinuxMMAC.getSeInfo(packageState, packageState.getAndroidPackage(), null,
+                        mMockCompatibility),
                 is("default:targetSdkVersion=28"));
     }
 
     @Test
     public void getSeInfoNoOptInButAlreadyLatest() {
-        AndroidPackage pkg = makePackage(LATEST_OPT_IN_VERSION);
+        var packageState = makePackageState(LATEST_OPT_IN_VERSION);
         when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
-                argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+                argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
                 .thenReturn(false);
-        assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+        assertThat(SELinuxMMAC.getSeInfo(packageState, packageState.getAndroidPackage(), null,
+                        mMockCompatibility),
                 is("default:targetSdkVersion=" + LATEST_OPT_IN_VERSION));
     }
 
     @Test
     public void getSeInfoTargetingCurDevelopment() {
-        AndroidPackage pkg = makePackage(Build.VERSION_CODES.CUR_DEVELOPMENT);
+        var packageState = makePackageState(Build.VERSION_CODES.CUR_DEVELOPMENT);
         when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
-                argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+                argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
                 .thenReturn(true);
-        assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+        assertThat(SELinuxMMAC.getSeInfo(packageState, packageState.getAndroidPackage(), null,
+                        mMockCompatibility),
                 is("default:targetSdkVersion=" + Build.VERSION_CODES.CUR_DEVELOPMENT));
     }
 
     @Test
     public void getSeInfoNoOptInButAlreadyR() {
-        AndroidPackage pkg = makePackage(R_OPT_IN_VERSION);
+        var packageState = makePackageState(R_OPT_IN_VERSION);
         when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
-                argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+                argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
                 .thenReturn(false);
-        assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+        assertThat(SELinuxMMAC.getSeInfo(packageState, packageState.getAndroidPackage(), null,
+                        mMockCompatibility),
                 is("default:targetSdkVersion=" + R_OPT_IN_VERSION));
     }
 
     @Test
     public void getSeInfoOptInRButLater() {
-        AndroidPackage pkg = makePackage(R_OPT_IN_VERSION + 1);
+        var packageState = makePackageState(R_OPT_IN_VERSION + 1);
         when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
-                argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+                argThat(argument -> argument.packageName.equals(packageState.getPackageName()))))
                 .thenReturn(true);
-        assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+        assertThat(SELinuxMMAC.getSeInfo(packageState, packageState.getAndroidPackage(), null,
+                        mMockCompatibility),
                 is("default:targetSdkVersion=" + (R_OPT_IN_VERSION + 1)));
     }
 
-    private AndroidPackage makePackage(int targetSdkVersion) {
-        return ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
-                .setTargetSdkVersion(targetSdkVersion)
-                .hideAsParsed())
-                .hideAsFinal();
+    private PackageState makePackageState(int targetSdkVersion) {
+        var packageState = Mockito.mock(PackageState.class);
+        when(packageState.getPackageName()).thenReturn(PACKAGE_NAME);
+        when(packageState.getAndroidPackage()).thenReturn(
+                ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+                        .setTargetSdkVersion(targetSdkVersion)
+                        .hideAsParsed())
+                        .hideAsFinal()
+        );
+        return packageState;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java b/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java
index 94a8ed3..bbe8907 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java
@@ -41,6 +41,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -108,8 +109,8 @@
             switchUser(USER_SYSTEM);
         }
 
-        final UserInfo foundGuest = mUserManager.findCurrentGuestUser();
-        int nextGuestId = foundGuest == null ? USER_NULL : foundGuest.id;
+        final List<UserInfo> guestUsers = mUserManager.getGuestUsers();
+        int nextGuestId = guestUsers.isEmpty() ? USER_NULL : guestUsers.get(0).id;
 
         for (int i = 0; i < NUM_ITERATIONS; i++) {
             final int currentGuestId = nextGuestId;
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java
index 7e7a434..319a280 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/AmbientDisplayPowerCalculatorTest.java
@@ -32,6 +32,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
+@SuppressWarnings("GuardedBy")
 public class AmbientDisplayPowerCalculatorTest {
     private static final double PRECISION = 0.00001;
     private static final long MINUTE_IN_MS = 60 * 1000;
@@ -46,19 +47,19 @@
         mStatsRule.initMeasuredEnergyStatsLocked();
         BatteryStatsImpl stats = mStatsRule.getBatteryStats();
 
-        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{300_000_000},
+        stats.updateDisplayEnergyConsumerStatsLocked(new long[]{300_000_000},
                 new int[]{Display.STATE_ON}, 0);
 
         stats.noteScreenStateLocked(0, Display.STATE_DOZE, 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS,
                 30 * MINUTE_IN_MS);
 
-        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{200_000_000},
+        stats.updateDisplayEnergyConsumerStatsLocked(new long[]{200_000_000},
                 new int[]{Display.STATE_DOZE}, 30 * MINUTE_IN_MS);
 
         stats.noteScreenStateLocked(0, Display.STATE_OFF, 120 * MINUTE_IN_MS, 120 * MINUTE_IN_MS,
                 120 * MINUTE_IN_MS);
 
-        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{100_000_000},
+        stats.updateDisplayEnergyConsumerStatsLocked(new long[]{100_000_000},
                 new int[]{Display.STATE_OFF}, 120 * MINUTE_IN_MS);
 
         AmbientDisplayPowerCalculator calculator =
@@ -73,7 +74,7 @@
         assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
                 .isWithin(PRECISION).of(27.777778);
         assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
     }
 
     @Test
@@ -88,13 +89,13 @@
 
         stats.noteScreenStateLocked(0, screenStates[0], 0, 0, 0);
         stats.noteScreenStateLocked(1, screenStates[1], 0, 0, 0);
-        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{300, 400}, screenStates, 0);
+        stats.updateDisplayEnergyConsumerStatsLocked(new long[]{300, 400}, screenStates, 0);
 
         // Switch display0 to doze
         screenStates[0] = Display.STATE_DOZE;
         stats.noteScreenStateLocked(0, screenStates[0], 30 * MINUTE_IN_MS, 30 * MINUTE_IN_MS,
                 30 * MINUTE_IN_MS);
-        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{200, 300},
+        stats.updateDisplayEnergyConsumerStatsLocked(new long[]{200, 300},
                 screenStates, 30 * MINUTE_IN_MS);
 
         // Switch display1 to doze
@@ -102,7 +103,7 @@
         stats.noteScreenStateLocked(1, Display.STATE_DOZE, 90 * MINUTE_IN_MS, 90 * MINUTE_IN_MS,
                 90 * MINUTE_IN_MS);
         // 100,000,000 uC should be attributed to display 0 doze here.
-        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{100_000_000, 700_000_000},
+        stats.updateDisplayEnergyConsumerStatsLocked(new long[]{100_000_000, 700_000_000},
                 screenStates, 90 * MINUTE_IN_MS);
 
         // Switch display0 to off
@@ -110,14 +111,14 @@
         stats.noteScreenStateLocked(0, screenStates[0], 120 * MINUTE_IN_MS, 120 * MINUTE_IN_MS,
                 120 * MINUTE_IN_MS);
         // 40,000,000 and 70,000,000 uC should be attributed to display 0 and 1 doze here.
-        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{40_000_000, 70_000_000},
+        stats.updateDisplayEnergyConsumerStatsLocked(new long[]{40_000_000, 70_000_000},
                 screenStates, 120 * MINUTE_IN_MS);
 
         // Switch display1 to off
         screenStates[1] = Display.STATE_OFF;
         stats.noteScreenStateLocked(1, screenStates[1], 150 * MINUTE_IN_MS, 150 * MINUTE_IN_MS,
                 150 * MINUTE_IN_MS);
-        stats.updateDisplayMeasuredEnergyStatsLocked(new long[]{100, 90_000_000}, screenStates,
+        stats.updateDisplayEnergyConsumerStatsLocked(new long[]{100, 90_000_000}, screenStates,
                 150 * MINUTE_IN_MS);
         // 90,000,000 uC should be attributed to display 1 doze here.
 
@@ -134,7 +135,7 @@
         assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
                 .isWithin(PRECISION).of(83.33333);
         assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
index c83610d..2ebe215 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
@@ -55,6 +55,7 @@
  * Build/Install/Run:
  * atest FrameworksServicesTests:BatteryExternalStatsWorkerTest
  */
+@SuppressWarnings("GuardedBy")
 public class BatteryExternalStatsWorkerTest {
     private BatteryExternalStatsWorker mBatteryExternalStatsWorker;
     private TestBatteryStatsImpl mBatteryStatsImpl;
@@ -133,7 +134,7 @@
         mBatteryExternalStatsWorker.systemServicesReady();
 
         final EnergyConsumerResult[] displayResults =
-                mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_DISPLAY).getNow(null);
+                mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_DISPLAY).getNow(null);
         // Results should only have the cpu cluster energy consumers
         final int[] receivedDisplayIds = new int[displayResults.length];
         for (int i = 0; i < displayResults.length; i++) {
@@ -143,25 +144,25 @@
         assertArrayEquals(displayIds, receivedDisplayIds);
 
         final EnergyConsumerResult[] wifiResults =
-                mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_WIFI).getNow(null);
+                mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_WIFI).getNow(null);
         // Results should only have the wifi energy consumer
         assertEquals(1, wifiResults.length);
         assertEquals(wifiId, wifiResults[0].id);
 
         final EnergyConsumerResult[] bluetoothResults =
-                mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_BT).getNow(null);
+                mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_BT).getNow(null);
         // Results should only have the bluetooth energy consumer
         assertEquals(1, bluetoothResults.length);
         assertEquals(btId, bluetoothResults[0].id);
 
         final EnergyConsumerResult[] mobileRadioResults =
-                mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_RADIO).getNow(null);
+                mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_RADIO).getNow(null);
         // Results should only have the mobile radio energy consumer
         assertEquals(1, mobileRadioResults.length);
         assertEquals(mobileRadioId, mobileRadioResults[0].id);
 
         final EnergyConsumerResult[] cpuResults =
-                mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_CPU).getNow(null);
+                mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_CPU).getNow(null);
         // Results should only have the cpu cluster energy consumers
         final int[] receivedCpuIds = new int[cpuResults.length];
         for (int i = 0; i < cpuResults.length; i++) {
@@ -171,7 +172,7 @@
         assertArrayEquals(cpuClusterIds, receivedCpuIds);
 
         final EnergyConsumerResult[] allResults =
-                mBatteryExternalStatsWorker.getMeasuredEnergyLocked(UPDATE_ALL).getNow(null);
+                mBatteryExternalStatsWorker.getEnergyConsumersLocked(UPDATE_ALL).getNow(null);
         // All energy consumer results should be available
         final int[] receivedAllIds = new int[allResults.length];
         for (int i = 0; i < allResults.length; i++) {
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index 22a7e8d..e65229f 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -27,8 +27,8 @@
 import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.BatteryStats.CpuUsageDetails;
+import android.os.BatteryStats.EnergyConsumerDetails;
 import android.os.BatteryStats.HistoryItem;
-import android.os.BatteryStats.MeasuredEnergyDetails;
 import android.os.Parcel;
 import android.util.Log;
 
@@ -272,33 +272,33 @@
         mHistory.setBatteryState(true /* charging */, BatteryManager.BATTERY_STATUS_CHARGING, 80,
                 1234);
 
-        MeasuredEnergyDetails details = new MeasuredEnergyDetails();
-        MeasuredEnergyDetails.EnergyConsumer consumer1 =
-                new MeasuredEnergyDetails.EnergyConsumer();
+        EnergyConsumerDetails details = new EnergyConsumerDetails();
+        EnergyConsumerDetails.EnergyConsumer consumer1 =
+                new EnergyConsumerDetails.EnergyConsumer();
         consumer1.type = 42;
         consumer1.ordinal = 0;
         consumer1.name = "A";
 
-        MeasuredEnergyDetails.EnergyConsumer consumer2 =
-                new MeasuredEnergyDetails.EnergyConsumer();
+        EnergyConsumerDetails.EnergyConsumer consumer2 =
+                new EnergyConsumerDetails.EnergyConsumer();
         consumer2.type = 777;
         consumer2.ordinal = 0;
         consumer2.name = "B/0";
 
-        MeasuredEnergyDetails.EnergyConsumer consumer3 =
-                new MeasuredEnergyDetails.EnergyConsumer();
+        EnergyConsumerDetails.EnergyConsumer consumer3 =
+                new EnergyConsumerDetails.EnergyConsumer();
         consumer3.type = 777;
         consumer3.ordinal = 1;
         consumer3.name = "B/1";
 
-        MeasuredEnergyDetails.EnergyConsumer consumer4 =
-                new MeasuredEnergyDetails.EnergyConsumer();
+        EnergyConsumerDetails.EnergyConsumer consumer4 =
+                new EnergyConsumerDetails.EnergyConsumer();
         consumer4.type = 314;
         consumer4.ordinal = 1;
         consumer4.name = "C";
 
         details.consumers =
-                new MeasuredEnergyDetails.EnergyConsumer[]{consumer1, consumer2, consumer3,
+                new EnergyConsumerDetails.EnergyConsumer[]{consumer1, consumer2, consumer3,
                         consumer4};
         details.chargeUC = new long[details.consumers.length];
         for (int i = 0; i < details.chargeUC.length; i++) {
@@ -306,7 +306,7 @@
         }
         details.chargeUC[3] = BatteryStats.POWER_DATA_UNAVAILABLE;
 
-        mHistory.recordMeasuredEnergyDetails(200, 200, details);
+        mHistory.recordEnergyConsumerDetails(200, 200, details);
 
         BatteryStatsHistoryIterator iterator = mHistory.iterate();
         BatteryStats.HistoryItem item;
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
index 773a2dc..998d22e 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsNoteTest.java
@@ -57,7 +57,7 @@
 
 import com.android.internal.os.BatteryStatsHistoryIterator;
 import com.android.internal.os.PowerProfile;
-import com.android.internal.power.MeasuredEnergyStats;
+import com.android.internal.power.EnergyConsumerStats;
 import com.android.server.power.stats.BatteryStatsImpl.DualTimer;
 
 import junit.framework.TestCase;
@@ -73,15 +73,6 @@
 
 /**
  * Test various BatteryStatsImpl noteStart methods.
- *
- * Build/Install/Run: bit FrameworksCoreTests:BatteryStatsNoteTest
- *
- * Alternatively,
- * Build: m FrameworksCoreTests
- * Install: adb install -r \
- *      ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
- * Run: adb shell am instrument -e class BatteryStatsNoteTest -w \
- *      com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
  */
 @SuppressWarnings("GuardedBy")
 public class BatteryStatsNoteTest extends TestCase {
@@ -1153,7 +1144,7 @@
         // Case A: uid1 off, uid2 off, battery off, screen off
         bi.updateTimeBasesLocked(battery, screen[0], clocks.realtime * 1000, 0);
         bi.setOnBatteryInternal(battery);
-        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{500_000}, screen, clocks.realtime);
+        bi.updateDisplayEnergyConsumerStatsLocked(new long[]{500_000}, screen, clocks.realtime);
         checkMeasuredCharge("A", uid1, blame1, uid2, blame2, globalDoze, bi);
 
         // Case B: uid1 off, uid2 off, battery ON,  screen off
@@ -1162,24 +1153,24 @@
         bi.updateTimeBasesLocked(battery, screen[0], clocks.realtime * 1000, 0);
         bi.setOnBatteryInternal(battery);
         clocks.realtime += 19;
-        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{510_000}, screen, clocks.realtime);
+        bi.updateDisplayEnergyConsumerStatsLocked(new long[]{510_000}, screen, clocks.realtime);
         checkMeasuredCharge("B", uid1, blame1, uid2, blame2, globalDoze, bi);
 
         // Case C: uid1 ON,  uid2 off, battery on,  screen off
         clocks.realtime += 18;
         setFgState(uid1, true, bi);
         clocks.realtime += 18;
-        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{520_000}, screen, clocks.realtime);
+        bi.updateDisplayEnergyConsumerStatsLocked(new long[]{520_000}, screen, clocks.realtime);
         checkMeasuredCharge("C", uid1, blame1, uid2, blame2, globalDoze, bi);
 
         // Case D: uid1 on,  uid2 off, battery on,  screen ON
         clocks.realtime += 17;
         screen[0] = Display.STATE_ON;
-        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{521_000}, screen, clocks.realtime);
+        bi.updateDisplayEnergyConsumerStatsLocked(new long[]{521_000}, screen, clocks.realtime);
         blame1 += 0; // Screen had been off during the measurement period
         checkMeasuredCharge("D.1", uid1, blame1, uid2, blame2, globalDoze, bi);
         clocks.realtime += 101;
-        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{530_000}, screen, clocks.realtime);
+        bi.updateDisplayEnergyConsumerStatsLocked(new long[]{530_000}, screen, clocks.realtime);
         blame1 += 530_000;
         checkMeasuredCharge("D.2", uid1, blame1, uid2, blame2, globalDoze, bi);
 
@@ -1187,7 +1178,7 @@
         clocks.realtime += 20;
         setFgState(uid2, true, bi);
         clocks.realtime += 40;
-        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{540_000}, screen, clocks.realtime);
+        bi.updateDisplayEnergyConsumerStatsLocked(new long[]{540_000}, screen, clocks.realtime);
         // In the past 60ms, sum of fg is 20+40+40=100ms. uid1 is blamed for 60/100; uid2 for 40/100
         blame1 += 540_000 * (20 + 40) / (20 + 40 + 40);
         blame2 += 540_000 * (0 + 40) / (20 + 40 + 40);
@@ -1197,7 +1188,7 @@
         clocks.realtime += 40;
         setFgState(uid2, false, bi);
         clocks.realtime += 120;
-        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{550_000}, screen, clocks.realtime);
+        bi.updateDisplayEnergyConsumerStatsLocked(new long[]{550_000}, screen, clocks.realtime);
         // In the past 160ms, sum f fg is 200ms. uid1 is blamed for 40+120 of it; uid2 for 40 of it.
         blame1 += 550_000 * (40 + 120) / (40 + 40 + 120);
         blame2 += 550_000 * (40 + 0) / (40 + 40 + 120);
@@ -1206,14 +1197,14 @@
         // Case G: uid1 on,  uid2 off,  battery on, screen DOZE
         clocks.realtime += 5;
         screen[0] = Display.STATE_DOZE;
-        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{570_000}, screen, clocks.realtime);
+        bi.updateDisplayEnergyConsumerStatsLocked(new long[]{570_000}, screen, clocks.realtime);
         blame1 += 570_000; // All of this pre-doze time is blamed on uid1.
         checkMeasuredCharge("G", uid1, blame1, uid2, blame2, globalDoze, bi);
 
         // Case H: uid1 on,  uid2 off,  battery on, screen ON
         clocks.realtime += 6;
         screen[0] = Display.STATE_ON;
-        bi.updateDisplayMeasuredEnergyStatsLocked(new long[]{580_000}, screen, clocks.realtime);
+        bi.updateDisplayEnergyConsumerStatsLocked(new long[]{580_000}, screen, clocks.realtime);
         blame1 += 0; // The screen had been doze during the energy period
         globalDoze += 580_000;
         checkMeasuredCharge("H", uid1, blame1, uid2, blame2, globalDoze, bi);
@@ -1262,11 +1253,11 @@
 
         newChargesA.put(uid1, 20_000);
         // Implicit newChargesA.put(uid2, 0);
-        bi.updateCustomMeasuredEnergyStatsLocked(bucketA, 500_000, newChargesA);
+        bi.updateCustomEnergyConsumerStatsLocked(bucketA, 500_000, newChargesA);
 
         newChargesB.put(uid1, 60_000);
         // Implicit newChargesB.put(uid2, 0);
-        bi.updateCustomMeasuredEnergyStatsLocked(bucketB, 700_000, newChargesB);
+        bi.updateCustomEnergyConsumerStatsLocked(bucketB, 700_000, newChargesB);
 
         checkCustomBatteryConsumption(
                 "A", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi);
@@ -1277,12 +1268,12 @@
 
         newChargesA.put(uid1, 7_000); blame1A += 7_000;
         // Implicit newChargesA.put(uid2, 0); blame2A += 0;
-        bi.updateCustomMeasuredEnergyStatsLocked(bucketA, 310_000, newChargesA);
+        bi.updateCustomEnergyConsumerStatsLocked(bucketA, 310_000, newChargesA);
         totalBlameA += 310_000;
 
         newChargesB.put(uid1, 63_000); blame1B += 63_000;
         newChargesB.put(uid2, 15_000); blame2B += 15_000;
-        bi.updateCustomMeasuredEnergyStatsLocked(bucketB, 790_000, newChargesB);
+        bi.updateCustomEnergyConsumerStatsLocked(bucketB, 790_000, newChargesB);
         totalBlameB += 790_000;
 
         checkCustomBatteryConsumption(
@@ -1292,10 +1283,10 @@
         // ----- Case C: battery still on
         newChargesA.delete(uid1); blame1A += 0;
         newChargesA.put(uid2, 16_000); blame2A += 16_000;
-        bi.updateCustomMeasuredEnergyStatsLocked(bucketA, 560_000, newChargesA);
+        bi.updateCustomEnergyConsumerStatsLocked(bucketA, 560_000, newChargesA);
         totalBlameA += 560_000;
 
-        bi.updateCustomMeasuredEnergyStatsLocked(bucketB, 10_000, null);
+        bi.updateCustomEnergyConsumerStatsLocked(bucketB, 10_000, null);
         totalBlameB += 10_000;
 
         checkCustomBatteryConsumption(
@@ -1303,8 +1294,8 @@
 
 
         // ----- Case D: battery still on
-        bi.updateCustomMeasuredEnergyStatsLocked(bucketA, 0, newChargesA);
-        bi.updateCustomMeasuredEnergyStatsLocked(bucketB, 15_000, new SparseLongArray(1));
+        bi.updateCustomEnergyConsumerStatsLocked(bucketA, 0, newChargesA);
+        bi.updateCustomEnergyConsumerStatsLocked(bucketB, 15_000, new SparseLongArray(1));
         totalBlameB += 15_000;
         checkCustomBatteryConsumption(
                 "D", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi);
@@ -2156,19 +2147,19 @@
 
     private void checkMeasuredCharge(String caseName, int uid1, long blame1, int uid2, long blame2,
             long globalDoze, MockBatteryStatsImpl bi) {
-        final int bucket = MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON;
+        final int bucket = EnergyConsumerStats.POWER_BUCKET_SCREEN_ON;
 
         assertEquals("Wrong uid1 blame for Case " + caseName, blame1,
-                bi.getUidStatsLocked(uid1).getMeasuredBatteryConsumptionUC(bucket));
+                bi.getUidStatsLocked(uid1).getEnergyConsumptionUC(bucket));
 
         assertEquals("Wrong uid2 blame for Case " + caseName, blame2,
-                bi.getUidStatsLocked(uid2).getMeasuredBatteryConsumptionUC(bucket));
+                bi.getUidStatsLocked(uid2).getEnergyConsumptionUC(bucket));
 
         assertEquals("Wrong total blame for Case " + caseName, blame1 + blame2,
-                bi.getScreenOnMeasuredBatteryConsumptionUC());
+                bi.getScreenOnEnergyConsumptionUC());
 
         assertEquals("Wrong doze for Case " + caseName, globalDoze,
-                bi.getScreenDozeMeasuredBatteryConsumptionUC());
+                bi.getScreenDozeEnergyConsumptionUC());
     }
 
     private void checkCustomBatteryConsumption(String caseName,
@@ -2177,11 +2168,11 @@
             int uid2, long blame2A, long blame2B,
             MockBatteryStatsImpl bi) {
 
-        final long[] actualTotal = bi.getCustomConsumerMeasuredBatteryConsumptionUC();
+        final long[] actualTotal = bi.getCustomEnergyConsumerBatteryConsumptionUC();
         final long[] actualUid1 =
-                bi.getUidStatsLocked(uid1).getCustomConsumerMeasuredBatteryConsumptionUC();
+                bi.getUidStatsLocked(uid1).getCustomEnergyConsumerBatteryConsumptionUC();
         final long[] actualUid2 =
-                bi.getUidStatsLocked(uid2).getCustomConsumerMeasuredBatteryConsumptionUC();
+                bi.getUidStatsLocked(uid2).getCustomEnergyConsumerBatteryConsumptionUC();
 
         assertNotNull(actualTotal);
         assertNotNull(actualUid1);
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsTests.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsTests.java
index 807df47..48290e5 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsTests.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsTests.java
@@ -50,14 +50,14 @@
         BstatsCpuTimesValidationTest.class,
         CameraPowerCalculatorTest.class,
         CpuPowerCalculatorTest.class,
-        CustomMeasuredPowerCalculatorTest.class,
+        CustomEnergyConsumerPowerCalculatorTest.class,
         FlashlightPowerCalculatorTest.class,
         GnssPowerCalculatorTest.class,
         IdlePowerCalculatorTest.class,
         KernelWakelockReaderTest.class,
         LongSamplingCounterTest.class,
         LongSamplingCounterArrayTest.class,
-        MeasuredEnergySnapshotTest.class,
+        EnergyConsumerSnapshotTest.class,
         MobileRadioPowerCalculatorTest.class,
         ScreenPowerCalculatorTest.class,
         SensorPowerCalculatorTest.class,
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index 7c1962c..bf2faac 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -35,7 +35,7 @@
 import androidx.test.InstrumentationRegistry;
 
 import com.android.internal.os.PowerProfile;
-import com.android.internal.power.MeasuredEnergyStats;
+import com.android.internal.power.EnergyConsumerStats;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
@@ -146,11 +146,13 @@
     public BatteryUsageStatsRule initMeasuredEnergyStatsLocked(
             String[] customPowerComponentNames) {
         final boolean[] supportedStandardBuckets =
-                new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
+                new boolean[EnergyConsumerStats.NUMBER_STANDARD_POWER_BUCKETS];
         Arrays.fill(supportedStandardBuckets, true);
-        mBatteryStats.initMeasuredEnergyStatsLocked(supportedStandardBuckets,
-                customPowerComponentNames);
-        mBatteryStats.informThatAllExternalStatsAreFlushed();
+        synchronized (mBatteryStats) {
+            mBatteryStats.initEnergyConsumerStatsLocked(supportedStandardBuckets,
+                    customPowerComponentNames);
+            mBatteryStats.informThatAllExternalStatsAreFlushed();
+        }
         return this;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
index e603ea5..266a226 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryUsageStatsTest.java
@@ -17,7 +17,7 @@
 package com.android.server.power.stats;
 
 import static android.os.BatteryConsumer.POWER_COMPONENT_ANY;
-import static android.os.BatteryConsumer.POWER_MODEL_MEASURED_ENERGY;
+import static android.os.BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION;
 import static android.os.BatteryConsumer.POWER_MODEL_UNDEFINED;
 import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
 import static android.os.BatteryConsumer.PROCESS_STATE_CACHED;
@@ -313,7 +313,7 @@
 
         addUidBatteryConsumer(builder, batteryStats, APP_UID1, null,
                 4321, 5432,
-                123, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 345, POWER_MODEL_MEASURED_ENERGY,
+                123, BatteryConsumer.POWER_MODEL_POWER_PROFILE, 345, POWER_MODEL_ENERGY_CONSUMPTION,
                 456, 567, 678,
                 1777, 7771, 1888, 8881, 1999, 9991, 321, 654);
 
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java
index 76dd0fd..4d4337c 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BluetoothPowerCalculatorTest.java
@@ -193,16 +193,16 @@
 
         assertBluetoothPowerAndDuration(
                 mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
-                0.10378, 3583, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                0.10378, 3583, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
         assertBluetoothPowerAndDuration(
                 mStatsRule.getUidBatteryConsumer(APP_UID),
-                0.22950, 8416, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                0.22950, 8416, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
         assertBluetoothPowerAndDuration(
                 mStatsRule.getDeviceBatteryConsumer(),
-                0.33333, 12000, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                0.33333, 12000, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
         assertBluetoothPowerAndDuration(
                 mStatsRule.getAppsBatteryConsumer(),
-                0.33329, 11999, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                0.33329, 11999, BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
     }
 
     @Test
@@ -256,7 +256,7 @@
         assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
                 .isWithin(PRECISION).of(0.8220561);
         assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_BLUETOOTH))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
         final BatteryConsumer.Key foreground = uidConsumer.getKey(
                 BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
index 9ad6a7c..ced996f3 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/CpuPowerCalculatorTest.java
@@ -42,7 +42,7 @@
 import com.android.internal.os.KernelSingleUidTimeReader;
 import com.android.internal.os.LongArrayMultiStateCounter;
 import com.android.internal.os.PowerProfile;
-import com.android.internal.power.MeasuredEnergyStats;
+import com.android.internal.power.EnergyConsumerStats;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -100,8 +100,8 @@
         MockitoAnnotations.initMocks(this);
 
         final boolean[] supportedPowerBuckets =
-                new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
-        supportedPowerBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
+                new boolean[EnergyConsumerStats.NUMBER_STANDARD_POWER_BUCKETS];
+        supportedPowerBuckets[EnergyConsumerStats.POWER_BUCKET_CPU] = true;
 
         when(mMockCpuUidFreqTimeReader.isFastCpuTimesReader()).thenReturn(true);
 
@@ -114,7 +114,7 @@
                 .setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader)
                 .setKernelSingleUidTimeReader(mMockKernelSingleUidTimeReader)
                 .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader)
-                .initMeasuredEnergyStatsLocked(supportedPowerBuckets, new String[0]);
+                .initEnergyConsumerStatsLocked(supportedPowerBuckets, new String[0]);
     }
 
     @Test
@@ -257,7 +257,7 @@
         assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
                 .isWithin(PRECISION).of(3.18877);
         assertThat(uidConsumer1.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
         assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar");
 
         UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
@@ -266,20 +266,20 @@
         assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
                 .isWithin(PRECISION).of(7.44072);
         assertThat(uidConsumer2.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
         assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull();
 
         final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
         assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
                 .isWithin(PRECISION).of(10.62949);
         assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
         final BatteryConsumer appsBatteryConsumer = mStatsRule.getDeviceBatteryConsumer();
         assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
                 .isWithin(PRECISION).of(10.62949);
         assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CPU))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CustomMeasuredPowerCalculatorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java
similarity index 90%
rename from services/tests/servicestests/src/com/android/server/power/stats/CustomMeasuredPowerCalculatorTest.java
rename to services/tests/servicestests/src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java
index 552b4f7..245faaf 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/CustomMeasuredPowerCalculatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/CustomEnergyConsumerPowerCalculatorTest.java
@@ -32,7 +32,8 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-public class CustomMeasuredPowerCalculatorTest {
+@SuppressWarnings("GuardedBy")
+public class CustomEnergyConsumerPowerCalculatorTest {
     private static final double PRECISION = 0.00001;
 
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
@@ -50,13 +51,13 @@
 
         SparseLongArray uidEnergies = new SparseLongArray();
         uidEnergies.put(APP_UID, 30_000_000);
-        batteryStats.updateCustomMeasuredEnergyStatsLocked(0, 100_000_000, uidEnergies);
+        batteryStats.updateCustomEnergyConsumerStatsLocked(0, 100_000_000, uidEnergies);
 
         uidEnergies.put(APP_UID, 120_000_000);
-        batteryStats.updateCustomMeasuredEnergyStatsLocked(1, 200_000_000, uidEnergies);
+        batteryStats.updateCustomEnergyConsumerStatsLocked(1, 200_000_000, uidEnergies);
 
-        CustomMeasuredPowerCalculator calculator =
-                new CustomMeasuredPowerCalculator(mStatsRule.getPowerProfile());
+        CustomEnergyConsumerPowerCalculator calculator =
+                new CustomEnergyConsumerPowerCalculator(mStatsRule.getPowerProfile());
 
         mStatsRule.apply(calculator);
 
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/MeasuredEnergySnapshotTest.java b/services/tests/servicestests/src/com/android/server/power/stats/EnergyConsumerSnapshotTest.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/power/stats/MeasuredEnergySnapshotTest.java
rename to services/tests/servicestests/src/com/android/server/power/stats/EnergyConsumerSnapshotTest.java
index 122f7eb..558f396 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/MeasuredEnergySnapshotTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/EnergyConsumerSnapshotTest.java
@@ -32,18 +32,18 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.server.power.stats.MeasuredEnergySnapshot.MeasuredEnergyDeltaData;
+import com.android.server.power.stats.EnergyConsumerSnapshot.EnergyConsumerDeltaData;
 
 import org.junit.Test;
 
 /**
- * Test class for {@link MeasuredEnergySnapshot}.
+ * Test class for {@link EnergyConsumerSnapshot}.
  *
  * To run the tests, use
  * atest FrameworksServicesTests:com.android.server.power.stats.MeasuredEnergySnapshotTest
  */
 @SmallTest
-public final class MeasuredEnergySnapshotTest {
+public final class EnergyConsumerSnapshotTest {
     private static final EnergyConsumer CONSUMER_DISPLAY = createEnergyConsumer(
             0, 0, EnergyConsumerType.DISPLAY, "Display");
     private static final  EnergyConsumer CONSUMER_OTHER_0 = createEnergyConsumer(
@@ -107,17 +107,17 @@
 
     @Test
     public void testUpdateAndGetDelta_empty() {
-        final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(ALL_ID_CONSUMER_MAP);
+        final EnergyConsumerSnapshot snapshot = new EnergyConsumerSnapshot(ALL_ID_CONSUMER_MAP);
         assertNull(snapshot.updateAndGetDelta(null, VOLTAGE_0));
         assertNull(snapshot.updateAndGetDelta(new EnergyConsumerResult[0], VOLTAGE_0));
     }
 
     @Test
     public void testUpdateAndGetDelta() {
-        final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(ALL_ID_CONSUMER_MAP);
+        final EnergyConsumerSnapshot snapshot = new EnergyConsumerSnapshot(ALL_ID_CONSUMER_MAP);
 
         // results0
-        MeasuredEnergyDeltaData delta = snapshot.updateAndGetDelta(RESULTS_0, VOLTAGE_0);
+        EnergyConsumerDeltaData delta = snapshot.updateAndGetDelta(RESULTS_0, VOLTAGE_0);
         if (delta != null) { // null is fine here. If non-null, it better be uninteresting though.
             assertNull(delta.displayChargeUC);
             assertNull(delta.otherTotalChargeUC);
@@ -204,10 +204,10 @@
     /** Test updateAndGetDelta() when the results have consumers absent from idToConsumerMap. */
     @Test
     public void testUpdateAndGetDelta_some() {
-        final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(SOME_ID_CONSUMER_MAP);
+        final EnergyConsumerSnapshot snapshot = new EnergyConsumerSnapshot(SOME_ID_CONSUMER_MAP);
 
         // results0
-        MeasuredEnergyDeltaData delta = snapshot.updateAndGetDelta(RESULTS_0, VOLTAGE_0);
+        EnergyConsumerDeltaData delta = snapshot.updateAndGetDelta(RESULTS_0, VOLTAGE_0);
         if (delta != null) { // null is fine here. If non-null, it better be uninteresting though.
             assertNull(delta.displayChargeUC);
             assertNull(delta.otherTotalChargeUC);
@@ -226,23 +226,23 @@
 
     @Test
     public void testGetOtherOrdinalNames() {
-        final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(ALL_ID_CONSUMER_MAP);
+        final EnergyConsumerSnapshot snapshot = new EnergyConsumerSnapshot(ALL_ID_CONSUMER_MAP);
         assertThat(snapshot.getOtherOrdinalNames()).asList()
                 .containsExactly("GPU", "HPU", "IPU &_");
     }
 
     @Test
     public void testGetOtherOrdinalNames_none() {
-        final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(SOME_ID_CONSUMER_MAP);
+        final EnergyConsumerSnapshot snapshot = new EnergyConsumerSnapshot(SOME_ID_CONSUMER_MAP);
         assertEquals(0, snapshot.getOtherOrdinalNames().length);
     }
 
     @Test
     public void getMeasuredEnergyDetails() {
-        final MeasuredEnergySnapshot snapshot = new MeasuredEnergySnapshot(ALL_ID_CONSUMER_MAP);
+        final EnergyConsumerSnapshot snapshot = new EnergyConsumerSnapshot(ALL_ID_CONSUMER_MAP);
         snapshot.updateAndGetDelta(RESULTS_0, VOLTAGE_0);
-        MeasuredEnergyDeltaData delta = snapshot.updateAndGetDelta(RESULTS_1, VOLTAGE_1);
-        BatteryStats.MeasuredEnergyDetails details = snapshot.getMeasuredEnergyDetails(delta);
+        EnergyConsumerDeltaData delta = snapshot.updateAndGetDelta(RESULTS_1, VOLTAGE_1);
+        BatteryStats.EnergyConsumerDetails details = snapshot.getEnergyConsumerDetails(delta);
         assertThat(details.consumers).hasLength(4);
         assertThat(details.chargeUC).isEqualTo(new long[]{2667, 3200000, 0, 0});
         assertThat(details.toString()).isEqualTo("DISPLAY=2667 HPU=3200000 GPU=0 IPU &_=0");
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java
index 5bc4311..3f2a6d0 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/GnssPowerCalculatorTest.java
@@ -33,6 +33,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
+@SuppressWarnings("GuardedBy")
 public class GnssPowerCalculatorTest {
     private static final double PRECISION = 0.00001;
 
@@ -89,7 +90,7 @@
         uidStats2.noteStopGps(5000);
 
         BatteryStatsImpl stats = mStatsRule.getBatteryStats();
-        stats.updateGnssMeasuredEnergyStatsLocked(30_000_000, 6000);
+        stats.updateGnssEnergyConsumerStatsLocked(30_000_000, 6000);
 
         GnssPowerCalculator calculator =
                 new GnssPowerCalculator(mStatsRule.getPowerProfile());
@@ -102,7 +103,7 @@
         assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
                 .isWithin(PRECISION).of(2.77777);
         assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
         UidBatteryConsumer consumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
         assertThat(consumer2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_GNSS))
@@ -110,18 +111,18 @@
         assertThat(consumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
                 .isWithin(PRECISION).of(5.55555);
         assertThat(consumer2.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
         BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
         assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
                 .isWithin(PRECISION).of(8.333333);
         assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
         BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
         assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
                 .isWithin(PRECISION).of(8.333333);
         assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_GNSS))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
index 5bc73bd..65e6486 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/MobileRadioPowerCalculatorTest.java
@@ -527,7 +527,7 @@
         stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 12000, 12000,
                 mNetworkStatsManager);
 
-        assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC()).isAtMost(0);
+        assertThat(uid.getMobileRadioEnergyConsumptionUC()).isAtMost(0);
         // 12000-8000 = 4000 ms == 4_000_000 us
         assertThat(uid.getMobileRadioActiveTimeInProcessState(BatteryConsumer.PROCESS_STATE_ANY))
                 .isEqualTo(4_000_000);
@@ -622,19 +622,19 @@
         assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
                 .isWithin(PRECISION).of(2.77778);
         assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
         BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
         assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
                 .isWithin(PRECISION).of(1.53934);
         assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
         UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
         assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
                 .isWithin(PRECISION).of(1.53934);
         assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
     }
 
     @Test
@@ -687,15 +687,15 @@
 
         mStatsRule.setTime(20000, 20000);
 
-        assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC())
+        assertThat(uid.getMobileRadioEnergyConsumptionUC())
                 .isIn(Range.open(20_000_000L, 21_000_000L));
-        assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC(
+        assertThat(uid.getMobileRadioEnergyConsumptionUC(
                 BatteryConsumer.PROCESS_STATE_FOREGROUND))
                 .isIn(Range.open(13_000_000L, 14_000_000L));
-        assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC(
+        assertThat(uid.getMobileRadioEnergyConsumptionUC(
                 BatteryConsumer.PROCESS_STATE_BACKGROUND))
                 .isIn(Range.open(7_000_000L, 8_000_000L));
-        assertThat(uid.getMobileRadioMeasuredBatteryConsumptionUC(
+        assertThat(uid.getMobileRadioEnergyConsumptionUC(
                 BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE))
                 .isEqualTo(0);
 
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index df4b896..19d2639 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -35,7 +35,7 @@
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
 import com.android.internal.os.KernelSingleUidTimeReader;
 import com.android.internal.os.PowerProfile;
-import com.android.internal.power.MeasuredEnergyStats;
+import com.android.internal.power.EnergyConsumerStats;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -78,11 +78,13 @@
 
     public void initMeasuredEnergyStats(String[] customBucketNames) {
         final boolean[] supportedStandardBuckets =
-                new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
+                new boolean[EnergyConsumerStats.NUMBER_STANDARD_POWER_BUCKETS];
         Arrays.fill(supportedStandardBuckets, true);
-        mMeasuredEnergyStatsConfig = new MeasuredEnergyStats.Config(supportedStandardBuckets,
-                customBucketNames, new int[0], new String[]{""});
-        mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(mMeasuredEnergyStatsConfig);
+        synchronized (this) {
+            mEnergyConsumerStatsConfig = new EnergyConsumerStats.Config(supportedStandardBuckets,
+                    customBucketNames, new int[0], new String[]{""});
+            mGlobalEnergyConsumerStats = new EnergyConsumerStats(mEnergyConsumerStatsConfig);
+        }
     }
 
     public TimeBase getOnBatteryTimeBase() {
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java
index 36faae4..3723079 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/ScreenPowerCalculatorTest.java
@@ -36,6 +36,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
+@SuppressWarnings("GuardedBy")
 public class ScreenPowerCalculatorTest {
     private static final double PRECISION = 0.00001;
     private static final int APP_UID1 = Process.FIRST_APPLICATION_UID + 42;
@@ -56,12 +57,12 @@
         BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
 
         batteryStats.noteScreenStateLocked(0, Display.STATE_ON, 0, 0, 0);
-        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{0},
+        batteryStats.updateDisplayEnergyConsumerStatsLocked(new long[]{0},
                 new int[]{Display.STATE_ON}, 0);
         setProcState(APP_UID1, ActivityManager.PROCESS_STATE_TOP, true,
                 0, 0);
 
-        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{200_000_000},
+        batteryStats.updateDisplayEnergyConsumerStatsLocked(new long[]{200_000_000},
                 new int[]{Display.STATE_ON}, 15 * MINUTE_IN_MS);
 
         setProcState(APP_UID1, ActivityManager.PROCESS_STATE_CACHED_EMPTY, false,
@@ -70,7 +71,7 @@
         setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP, true,
                 20 * MINUTE_IN_MS, 20 * MINUTE_IN_MS);
 
-        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{300_000_000},
+        batteryStats.updateDisplayEnergyConsumerStatsLocked(new long[]{300_000_000},
                 new int[]{Display.STATE_ON}, 60 * MINUTE_IN_MS);
 
         batteryStats.noteScreenStateLocked(0, Display.STATE_OFF,
@@ -78,7 +79,7 @@
         setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP_SLEEPING, false,
                 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
 
-        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{100_000_000},
+        batteryStats.updateDisplayEnergyConsumerStatsLocked(new long[]{100_000_000},
                 new int[]{Display.STATE_DOZE}, 120 * MINUTE_IN_MS);
 
         mStatsRule.setTime(120 * MINUTE_IN_US, 120 * MINUTE_IN_US);
@@ -98,7 +99,7 @@
         assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
                 .isWithin(PRECISION).of(64.81481);
         assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
         UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
         assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
@@ -110,7 +111,7 @@
         assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
                 .isWithin(PRECISION).of(101.85185);
         assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
         BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
         assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
@@ -120,7 +121,7 @@
         assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
                 .isWithin(PRECISION).of(166.66666);
         assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
         BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
         assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
@@ -129,7 +130,7 @@
         assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
                 .isWithin(PRECISION).of(166.66666);
         assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
     }
 
 
@@ -148,7 +149,7 @@
         batteryStats.noteScreenStateLocked(1, screenStates[1], 0, 0, 0);
         batteryStats.noteScreenBrightnessLocked(0, 255, 0, 0);
         setProcState(APP_UID1, ActivityManager.PROCESS_STATE_TOP, true, 0, 0);
-        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{300, 400}, screenStates, 0);
+        batteryStats.updateDisplayEnergyConsumerStatsLocked(new long[]{300, 400}, screenStates, 0);
 
         batteryStats.noteScreenBrightnessLocked(0, 100, 5 * MINUTE_IN_MS, 5 * MINUTE_IN_MS);
         batteryStats.noteScreenBrightnessLocked(0, 200, 10 * MINUTE_IN_MS, 10 * MINUTE_IN_MS);
@@ -164,7 +165,7 @@
                 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
         batteryStats.noteScreenStateLocked(1, screenStates[1], 80 * MINUTE_IN_MS,
                 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
-        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{600_000_000, 500},
+        batteryStats.updateDisplayEnergyConsumerStatsLocked(new long[]{600_000_000, 500},
                 screenStates, 80 * MINUTE_IN_MS);
 
         batteryStats.noteScreenBrightnessLocked(1, 25, 80 * MINUTE_IN_MS, 80 * MINUTE_IN_MS);
@@ -174,7 +175,7 @@
         screenStates[1] = Display.STATE_OFF;
         batteryStats.noteScreenStateLocked(1, screenStates[1], 110 * MINUTE_IN_MS,
                 110 * MINUTE_IN_MS, 110 * MINUTE_IN_MS);
-        batteryStats.updateDisplayMeasuredEnergyStatsLocked(new long[]{700, 800_000_000},
+        batteryStats.updateDisplayEnergyConsumerStatsLocked(new long[]{700, 800_000_000},
                 screenStates, 110 * MINUTE_IN_MS);
 
         setProcState(APP_UID2, ActivityManager.PROCESS_STATE_TOP_SLEEPING, false,
@@ -194,7 +195,7 @@
         assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
                 .isWithin(PRECISION).of(388.88888);
         assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
         UidBatteryConsumer uid1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
         assertThat(uid1.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
@@ -206,7 +207,7 @@
         assertThat(uid1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
                 .isWithin(PRECISION).of(41.66666);
         assertThat(uid1.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
         UidBatteryConsumer uid2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
         assertThat(uid2.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
@@ -218,7 +219,7 @@
         assertThat(uid2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
                 .isWithin(PRECISION).of(347.22222);
         assertThat(uid2.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
         BatteryConsumer appsConsumer = mStatsRule.getAppsBatteryConsumer();
         assertThat(appsConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_SCREEN))
@@ -226,7 +227,7 @@
         assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SCREEN))
                 .isWithin(PRECISION).of(388.88888);
         assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_SCREEN))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
index 037ac78..3c5c0a3 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
@@ -37,7 +37,7 @@
 import com.android.internal.os.KernelCpuUidTimeReader;
 import com.android.internal.os.KernelSingleUidTimeReader;
 import com.android.internal.os.PowerProfile;
-import com.android.internal.power.MeasuredEnergyStats;
+import com.android.internal.power.EnergyConsumerStats;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -137,10 +137,10 @@
     @Test
     public void testMeasuredEnergyBasedModel() {
         final boolean[] supportedPowerBuckets =
-                new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
-        supportedPowerBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
+                new boolean[EnergyConsumerStats.NUMBER_STANDARD_POWER_BUCKETS];
+        supportedPowerBuckets[EnergyConsumerStats.POWER_BUCKET_CPU] = true;
         mStatsRule.getBatteryStats()
-                .initMeasuredEnergyStatsLocked(supportedPowerBuckets, new String[0]);
+                .initEnergyConsumerStatsLocked(supportedPowerBuckets, new String[0]);
 
         prepareBatteryStats(new long[]{50000000, 100000000});
 
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java
index b6f3036..113be8b 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/WifiPowerCalculatorTest.java
@@ -204,7 +204,7 @@
         assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
                 .isWithin(PRECISION).of(0.2214666 / (0.2214666 + 0.645200) * 1_000_000 / 3600000);
         assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
         BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
         assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
@@ -212,13 +212,13 @@
         assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
                 .isWithin(PRECISION).of(0.27777);
         assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
         BatteryConsumer appsConsumer = mStatsRule.getDeviceBatteryConsumer();
         assertThat(appsConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
                 .isWithin(PRECISION).of(0.277777);
         assertThat(appsConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
     }
 
     @Test
@@ -258,7 +258,7 @@
         assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
                 .isWithin(PRECISION).of(1.0325211);
         assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
 
         final BatteryConsumer.Key foreground = uidConsumer.getKey(
                 BatteryConsumer.POWER_COMPONENT_WIFI,
@@ -327,6 +327,6 @@
         assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
                 .isWithin(PRECISION).of(0.8231573 / (0.8231573 + 0.8759216) * 1_000_000 / 3600000);
         assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
-                .isEqualTo(BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+                .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION);
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityInterceptorCallbackRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityInterceptorCallbackRegistryTest.java
new file mode 100644
index 0000000..3646f1a
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityInterceptorCallbackRegistryTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_FIRST_ORDERED_ID;
+import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_LAST_ORDERED_ID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.spy;
+
+import android.os.Process;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+/**
+ * Tests for the {@link ActivityInterceptorCallbackRegistry} class.
+ */
+@Presubmit
+@MediumTest
+@RunWith(WindowTestRunner.class)
+public final class ActivityInterceptorCallbackRegistryTest extends WindowTestsBase {
+
+    private ActivityInterceptorCallbackRegistry mRegistry;
+
+    @Before
+    public void setUp() {
+        mRegistry = spy(ActivityInterceptorCallbackRegistry.getInstance());
+        Mockito.doReturn(Process.SYSTEM_UID).when(mRegistry).getCallingUid();
+    }
+
+    @Test
+    public void registerActivityInterceptorCallbackFailIfNotSystemId() {
+        // default registry with test app uid
+        ActivityInterceptorCallbackRegistry registry = spy(
+                ActivityInterceptorCallbackRegistry.getInstance());
+        assertThrows(
+                SecurityException.class,
+                () ->  registry.registerActivityInterceptorCallback(MAINLINE_LAST_ORDERED_ID + 1,
+                        info -> null)
+        );
+    }
+
+    @Test
+    public void registerActivityInterceptorCallbackFailIfIdNotInRange() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->  mRegistry.registerActivityInterceptorCallback(MAINLINE_LAST_ORDERED_ID + 1,
+                        info -> null)
+        );
+
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->  mRegistry.registerActivityInterceptorCallback(MAINLINE_FIRST_ORDERED_ID - 1,
+                        info -> null)
+        );
+    }
+
+    @Test
+    public void registerActivityInterceptorCallbackFailIfCallbackIsNull() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->  mRegistry.registerActivityInterceptorCallback(MAINLINE_FIRST_ORDERED_ID,
+                        null)
+        );
+    }
+
+    @Test
+    public void registerActivityInterceptorCallbackSuccessfully() {
+        int size = mAtm.getActivityInterceptorCallbacks().size();
+        int orderId = MAINLINE_FIRST_ORDERED_ID;
+        mRegistry.registerActivityInterceptorCallback(orderId,
+                info -> null);
+        assertEquals(size + 1, mAtm.getActivityInterceptorCallbacks().size());
+        assertTrue(mAtm.getActivityInterceptorCallbacks().contains(orderId));
+    }
+
+    @Test
+    public void unregisterActivityInterceptorCallbackFailIfNotSystemId() {
+        // default registry with test app uid
+        ActivityInterceptorCallbackRegistry registry = spy(
+                ActivityInterceptorCallbackRegistry.getInstance());
+        assertThrows(
+                SecurityException.class,
+                () ->  registry.unregisterActivityInterceptorCallback(MAINLINE_LAST_ORDERED_ID + 1)
+        );
+    }
+
+    @Test
+    public void unRegisterActivityInterceptorCallbackFailIfIdNotInRange() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->  mRegistry.unregisterActivityInterceptorCallback(
+                        MAINLINE_LAST_ORDERED_ID + 1));
+    }
+
+    @Test
+    public void unregisterActivityInterceptorCallbackFailIfNotRegistered() {
+        assertThrows(
+                IllegalArgumentException.class,
+                () ->  mRegistry.unregisterActivityInterceptorCallback(MAINLINE_FIRST_ORDERED_ID)
+        );
+    }
+
+    @Test
+    public void unregisterActivityInterceptorCallbackSuccessfully() {
+        int size = mAtm.getActivityInterceptorCallbacks().size();
+        int orderId = MAINLINE_FIRST_ORDERED_ID;
+        mRegistry.registerActivityInterceptorCallback(orderId,
+                info -> null);
+        assertEquals(size + 1, mAtm.getActivityInterceptorCallbacks().size());
+        assertTrue(mAtm.getActivityInterceptorCallbacks().contains(orderId));
+
+        mRegistry.unregisterActivityInterceptorCallback(orderId);
+        assertEquals(size, mAtm.getActivityInterceptorCallbacks().size());
+        assertFalse(mAtm.getActivityInterceptorCallbacks().contains(orderId));
+
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index b0489da..4ee87d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -3288,7 +3288,7 @@
         assertFalse(app2.mActivityRecord.mImeInsetsFrozenUntilStartInput);
         verify(app2.mClient, atLeastOnce()).resized(any(), anyBoolean(), any(),
                 insetsStateCaptor.capture(), anyBoolean(), anyBoolean(), anyInt(), anyInt(),
-                anyInt());
+                anyBoolean());
         assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 8a15c30..7d16fb2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY;
 import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
 
@@ -25,15 +26,19 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_SDK_SANDBOX_ORDER_ID;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
@@ -68,6 +73,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 /**
@@ -353,16 +359,24 @@
 
     public void addMockInterceptorCallback(
             @Nullable Intent intent, @Nullable ActivityOptions activityOptions) {
+        addMockInterceptorCallback(intent, activityOptions, false);
+    }
+
+    public void addMockInterceptorCallback(
+            @Nullable Intent intent, @Nullable ActivityOptions activityOptions,
+            boolean skipResolving) {
         int size = mActivityInterceptorCallbacks.size();
         mActivityInterceptorCallbacks.put(size, new ActivityInterceptorCallback() {
             @Override
-            public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
+            public ActivityInterceptResult onInterceptActivityLaunch(@NonNull
+                    ActivityInterceptorInfo info) {
                 if (intent == null && activityOptions == null) {
                     return null;
                 }
                 return new ActivityInterceptResult(
-                        intent != null ? intent : info.intent,
-                        activityOptions != null ? activityOptions : info.checkedOptions);
+                        intent != null ? intent : info.getIntent(),
+                        activityOptions != null ? activityOptions : info.getCheckedOptions(),
+                        skipResolving);
             }
         });
     }
@@ -395,6 +409,30 @@
     }
 
     @Test
+    public void testInterceptionCallback_skipResolving() {
+        addMockInterceptorCallback(
+                new Intent("android.test.foo"),
+                ActivityOptions.makeBasic().setLaunchDisplayId(3), true);
+        ActivityInfo aInfo = mAInfo;
+        assertTrue(mInterceptor.intercept(null, null, aInfo, null, null, null, 0, 0, null));
+        assertEquals("android.test.foo", mInterceptor.mIntent.getAction());
+        assertEquals(3, mInterceptor.mActivityOptions.getLaunchDisplayId());
+        assertEquals(aInfo, mInterceptor.mAInfo); // mAInfo should not be resolved
+    }
+
+    @Test
+    public void testInterceptionCallback_NoSkipResolving() throws InterruptedException {
+        addMockInterceptorCallback(
+                new Intent("android.test.foo"),
+                ActivityOptions.makeBasic().setLaunchDisplayId(3));
+        ActivityInfo aInfo = mAInfo;
+        assertTrue(mInterceptor.intercept(null, null, aInfo, null, null, null, 0, 0, null));
+        assertEquals("android.test.foo", mInterceptor.mIntent.getAction());
+        assertEquals(3, mInterceptor.mActivityOptions.getLaunchDisplayId());
+        assertNotEquals(aInfo, mInterceptor.mAInfo); // mAInfo should be resolved after intercept
+    }
+
+    @Test
     public void testActivityLaunchedCallback_singleCallback() {
         addMockInterceptorCallback(null, null);
 
@@ -405,4 +443,30 @@
 
         verify(callback, times(1)).onActivityLaunched(any(), any(), any());
     }
+
+    @Test
+    public void testSandboxServiceInterceptionHappensToSandboxedActivityAction()
+            throws InterruptedException {
+
+        ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
+        mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
+
+        Intent intent = new Intent().setAction(ACTION_START_SANDBOXED_ACTIVITY);
+        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+
+        verify(spyCallback, times(1)).onInterceptActivityLaunch(
+                any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
+    }
+
+    @Test
+    public void testSandboxServiceInterceptionNotCalledForNotSandboxedActivityAction() {
+        ActivityInterceptorCallback spyCallback = Mockito.spy(info -> null);
+        mActivityInterceptorCallbacks.put(MAINLINE_SDK_SANDBOX_ORDER_ID, spyCallback);
+
+        Intent intent = new Intent();
+        mInterceptor.intercept(intent, null, mAInfo, null, null, null, 0, 0, null);
+
+        verify(spyCallback, never()).onInterceptActivityLaunch(
+                any(ActivityInterceptorCallback.ActivityInterceptorInfo.class));
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 43627f4..5eecb0a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -384,7 +384,7 @@
         doReturn(null).when(mMockPackageManager).resolveIntent(any(), any(), anyLong(), anyLong(),
                 anyInt(), anyBoolean(), anyInt());
         doReturn(null).when(mMockPackageManager).resolveIntentExported(any(), any(),
-                anyLong(), anyLong(), anyInt(), anyBoolean(), anyInt());
+                anyLong(), anyLong(), anyInt(), anyBoolean(), anyInt(), anyInt());
         doReturn(new ComponentName("", "")).when(mMockPackageManager).getSystemUiServiceComponent();
 
         // Never review permissions
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 368b750..3dcae91 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -25,8 +25,9 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.ActivityInterceptorCallback.FIRST_ORDERED_ID;
-import static com.android.server.wm.ActivityInterceptorCallback.LAST_ORDERED_ID;
+import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_FIRST_ORDERED_ID;
+import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_FIRST_ORDERED_ID;
+import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_LAST_ORDERED_ID;
 import static com.android.server.wm.ActivityRecord.State.PAUSED;
 import static com.android.server.wm.ActivityRecord.State.PAUSING;
 import static com.android.server.wm.ActivityRecord.State.RESUMED;
@@ -45,6 +46,7 @@
 import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -957,11 +959,12 @@
 
     @Test(expected = IllegalArgumentException.class)
     public void testRegisterActivityStartInterceptor_IndexTooSmall() {
-        mAtm.mInternal.registerActivityStartInterceptor(FIRST_ORDERED_ID - 1,
+        mAtm.mInternal.registerActivityStartInterceptor(SYSTEM_FIRST_ORDERED_ID - 1,
                 new ActivityInterceptorCallback() {
                     @Nullable
                     @Override
-                    public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
+                    public ActivityInterceptResult onInterceptActivityLaunch(
+                            @NonNull ActivityInterceptorInfo info) {
                         return null;
                     }
                 });
@@ -969,11 +972,12 @@
 
     @Test(expected = IllegalArgumentException.class)
     public void testRegisterActivityStartInterceptor_IndexTooLarge() {
-        mAtm.mInternal.registerActivityStartInterceptor(LAST_ORDERED_ID + 1,
+        mAtm.mInternal.registerActivityStartInterceptor(SYSTEM_LAST_ORDERED_ID + 1,
                 new ActivityInterceptorCallback() {
                     @Nullable
                     @Override
-                    public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
+                    public ActivityInterceptResult onInterceptActivityLaunch(
+                            @NonNull ActivityInterceptorInfo info) {
                         return null;
                     }
                 });
@@ -981,19 +985,21 @@
 
     @Test(expected = IllegalArgumentException.class)
     public void testRegisterActivityStartInterceptor_DuplicateId() {
-        mAtm.mInternal.registerActivityStartInterceptor(FIRST_ORDERED_ID,
+        mAtm.mInternal.registerActivityStartInterceptor(SYSTEM_FIRST_ORDERED_ID,
                 new ActivityInterceptorCallback() {
                     @Nullable
                     @Override
-                    public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
+                    public ActivityInterceptResult onInterceptActivityLaunch(
+                            @NonNull ActivityInterceptorInfo info) {
                         return null;
                     }
                 });
-        mAtm.mInternal.registerActivityStartInterceptor(FIRST_ORDERED_ID,
+        mAtm.mInternal.registerActivityStartInterceptor(SYSTEM_FIRST_ORDERED_ID,
                 new ActivityInterceptorCallback() {
                     @Nullable
                     @Override
-                    public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
+                    public ActivityInterceptResult onInterceptActivityLaunch(
+                            @NonNull ActivityInterceptorInfo info) {
                         return null;
                     }
                 });
@@ -1003,16 +1009,43 @@
     public void testRegisterActivityStartInterceptor() {
         assertEquals(0, mAtm.getActivityInterceptorCallbacks().size());
 
-        mAtm.mInternal.registerActivityStartInterceptor(FIRST_ORDERED_ID,
+        mAtm.mInternal.registerActivityStartInterceptor(SYSTEM_FIRST_ORDERED_ID,
                 new ActivityInterceptorCallback() {
                     @Nullable
                     @Override
-                    public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
+                    public ActivityInterceptResult onInterceptActivityLaunch(
+                            @NonNull ActivityInterceptorInfo info) {
                         return null;
                     }
                 });
 
         assertEquals(1, mAtm.getActivityInterceptorCallbacks().size());
-        assertTrue(mAtm.getActivityInterceptorCallbacks().contains(FIRST_ORDERED_ID));
+        assertTrue(mAtm.getActivityInterceptorCallbacks().contains(SYSTEM_FIRST_ORDERED_ID));
+    }
+
+    @Test
+    public void testSystemAndMainlineOrderIdsNotOverlapping() {
+        assertTrue(MAINLINE_FIRST_ORDERED_ID - SYSTEM_LAST_ORDERED_ID > 1);
+    }
+
+    @Test
+    public void testUnregisterActivityStartInterceptor() {
+        int size = mAtm.getActivityInterceptorCallbacks().size();
+        int orderId = SYSTEM_FIRST_ORDERED_ID;
+
+        mAtm.mInternal.registerActivityStartInterceptor(orderId,
+                (ActivityInterceptorCallback) info -> null);
+        assertEquals(size + 1, mAtm.getActivityInterceptorCallbacks().size());
+        assertTrue(mAtm.getActivityInterceptorCallbacks().contains(orderId));
+
+        mAtm.mInternal.unregisterActivityStartInterceptor(orderId);
+        assertEquals(size, mAtm.getActivityInterceptorCallbacks().size());
+        assertFalse(mAtm.getActivityInterceptorCallbacks().contains(orderId));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testUnregisterActivityStartInterceptor_IdNotExist() {
+        assertEquals(0, mAtm.getActivityInterceptorCallbacks().size());
+        mAtm.mInternal.unregisterActivityStartInterceptor(SYSTEM_FIRST_ORDERED_ID);
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index b45c37f..491f876d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -60,6 +60,7 @@
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
+import android.view.DisplayAddress;
 import android.view.Surface;
 import android.view.WindowManager;
 
@@ -101,6 +102,7 @@
     private static WindowManagerService sMockWm;
     private DisplayContent mMockDisplayContent;
     private DisplayPolicy mMockDisplayPolicy;
+    private DisplayAddress mMockDisplayAddress;
     private Context mMockContext;
     private Resources mMockRes;
     private SensorManager mMockSensorManager;
@@ -1091,9 +1093,11 @@
             when(mMockResolver.acquireProvider(Settings.AUTHORITY))
                     .thenReturn(mFakeSettingsProvider.getIContentProvider());
 
+            mMockDisplayAddress = mock(DisplayAddress.class);
+
             mMockDisplayWindowSettings = mock(DisplayWindowSettings.class);
-            mTarget = new DisplayRotation(sMockWm, mMockDisplayContent, mMockDisplayPolicy,
-                    mMockDisplayWindowSettings, mMockContext, new Object());
+            mTarget = new DisplayRotation(sMockWm, mMockDisplayContent, mMockDisplayAddress,
+                    mMockDisplayPolicy, mMockDisplayWindowSettings, mMockContext, new Object());
             reset(sMockWm);
 
             captureObservers();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index 6e72bf3..3f8acc6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -46,7 +46,7 @@
     @Override
     public void resized(ClientWindowFrames frames, boolean reportDraw,
             MergedConfiguration mergedConfig, InsetsState insetsState, boolean forceLayout,
-            boolean alwaysConsumeSystemBars, int displayId, int seqId, int resizeMode)
+            boolean alwaysConsumeSystemBars, int displayId, int seqId, boolean dragResizing)
             throws RemoteException {
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 99d1772..5e0e209 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -277,12 +277,9 @@
         assertFalse(imeWindow.canBeImeTarget());
 
         // Simulate the window is in split screen root task.
-        final DockedTaskDividerController controller =
-                mDisplayContent.getDockedDividerController();
         final Task rootTask = createTask(mDisplayContent,
                 WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
         spyOn(appWindow);
-        spyOn(controller);
         spyOn(rootTask);
         rootTask.setFocusable(false);
         doReturn(rootTask).when(appWindow).getRootTask();
@@ -776,7 +773,7 @@
                     anyBoolean() /* reportDraw */, any() /* mergedConfig */,
                     any() /* insetsState */, anyBoolean() /* forceLayout */,
                     anyBoolean() /* alwaysConsumeSystemBars */, anyInt() /* displayId */,
-                    anyInt() /* seqId */, anyInt() /* resizeMode */);
+                    anyInt() /* seqId */, anyBoolean() /* dragResizing */);
         } catch (RemoteException ignored) {
         }
         win.reportResized();
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
index ca11629..ff4268f 100644
--- a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
@@ -80,38 +80,46 @@
     /**
      * USB data status is not known.
      */
-    public static final int USB_DATA_STATUS_UNKNOWN = 0;
+    public static final int AIDL_USB_DATA_STATUS_UNKNOWN = 0;
 
     /**
      * USB data is enabled.
      */
-    public static final int USB_DATA_STATUS_ENABLED = 1;
+    public static final int AIDL_USB_DATA_STATUS_ENABLED = 1;
 
     /**
      * USB data is disabled as the port is too hot.
      */
-    public static final int USB_DATA_STATUS_DISABLED_OVERHEAT = 2;
+    public static final int AIDL_USB_DATA_STATUS_DISABLED_OVERHEAT = 2;
 
     /**
      * USB data is disabled due to contaminated port.
      */
-    public static final int USB_DATA_STATUS_DISABLED_CONTAMINANT = 3;
+    public static final int AIDL_USB_DATA_STATUS_DISABLED_CONTAMINANT = 3;
 
     /**
-     * USB data is disabled due to docking event.
+     * USB data(both host mode and device mode) is disabled due to docking event.
      */
-    public static final int USB_DATA_STATUS_DISABLED_DOCK = 4;
+    public static final int AIDL_USB_DATA_STATUS_DISABLED_DOCK = 4;
 
     /**
      * USB data is disabled by
      * {@link UsbPort#enableUsbData UsbPort.enableUsbData}.
      */
-    public static final int USB_DATA_STATUS_DISABLED_FORCE = 5;
+    public static final int AIDL_USB_DATA_STATUS_DISABLED_FORCE = 5;
 
     /**
      * USB data is disabled for debug.
      */
-    public static final int USB_DATA_STATUS_DISABLED_DEBUG = 6;
+    public static final int AIDL_USB_DATA_STATUS_DISABLED_DEBUG = 6;
+    /**
+     * USB host mode disabled due to docking event.
+     */
+    public static final int AIDL_USB_DATA_STATUS_DISABLED_DOCK_HOST_MODE = 7;
+    /**
+     * USB device mode disabled due to docking event.
+     */
+    public static final int AIDL_USB_DATA_STATUS_DISABLED_DOCK_DEVICE_MODE = 8;
 
     public @UsbHalVersion int getUsbHalVersion() throws RemoteException {
         synchronized (mLock) {
@@ -529,24 +537,43 @@
             int usbDataStatus = UsbPortStatus.DATA_STATUS_UNKNOWN;
             for (int i = 0; i < usbDataStatusHal.length; i++) {
                 switch (usbDataStatusHal[i]) {
-                    case USB_DATA_STATUS_ENABLED:
+                    case AIDL_USB_DATA_STATUS_ENABLED:
                         usbDataStatus |= UsbPortStatus.DATA_STATUS_ENABLED;
                         break;
-                    case USB_DATA_STATUS_DISABLED_OVERHEAT:
+                    case AIDL_USB_DATA_STATUS_DISABLED_OVERHEAT:
                         usbDataStatus |= UsbPortStatus.DATA_STATUS_DISABLED_OVERHEAT;
                         break;
-                    case USB_DATA_STATUS_DISABLED_CONTAMINANT:
+                    case AIDL_USB_DATA_STATUS_DISABLED_CONTAMINANT:
                         usbDataStatus |= UsbPortStatus.DATA_STATUS_DISABLED_CONTAMINANT;
                         break;
-                    case USB_DATA_STATUS_DISABLED_DOCK:
+                    /* Indicates both host and gadget mode being disabled. */
+                    case AIDL_USB_DATA_STATUS_DISABLED_DOCK:
                         usbDataStatus |= UsbPortStatus.DATA_STATUS_DISABLED_DOCK;
+                        usbDataStatus |= UsbPortStatus.DATA_STATUS_DISABLED_DOCK_HOST_MODE;
+                        usbDataStatus |= UsbPortStatus.DATA_STATUS_DISABLED_DOCK_DEVICE_MODE;
                         break;
-                    case USB_DATA_STATUS_DISABLED_FORCE:
+                    case AIDL_USB_DATA_STATUS_DISABLED_FORCE:
                         usbDataStatus |= UsbPortStatus.DATA_STATUS_DISABLED_FORCE;
                         break;
-                    case USB_DATA_STATUS_DISABLED_DEBUG:
+                    case AIDL_USB_DATA_STATUS_DISABLED_DEBUG:
                         usbDataStatus |= UsbPortStatus.DATA_STATUS_DISABLED_DEBUG;
                         break;
+                    /*
+                     * Set DATA_STATUS_DISABLED_DOCK when DATA_STATUS_DISABLED_DOCK_HOST_MODE
+                     * is set.
+                     */
+                    case AIDL_USB_DATA_STATUS_DISABLED_DOCK_HOST_MODE:
+                        usbDataStatus |= UsbPortStatus.DATA_STATUS_DISABLED_DOCK_HOST_MODE;
+                        usbDataStatus |= UsbPortStatus.DATA_STATUS_DISABLED_DOCK;
+                        break;
+                    /*
+                     * Set DATA_STATUS_DISABLED_DOCK when DATA_STATUS_DISABLED_DEVICE_DOCK
+                     * is set.
+                     */
+                    case AIDL_USB_DATA_STATUS_DISABLED_DOCK_DEVICE_MODE:
+                        usbDataStatus |= UsbPortStatus.DATA_STATUS_DISABLED_DOCK_DEVICE_MODE;
+                        usbDataStatus |= UsbPortStatus.DATA_STATUS_DISABLED_DOCK;
+                        break;
                     default:
                         usbDataStatus |= UsbPortStatus.DATA_STATUS_UNKNOWN;
                 }
diff --git a/telecomm/java/android/telecom/CallAttributes.aidl b/telecomm/java/android/telecom/CallAttributes.aidl
new file mode 100644
index 0000000..19bada7
--- /dev/null
+++ b/telecomm/java/android/telecom/CallAttributes.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+/**
+ * {@hide}
+ */
+parcelable CallAttributes;
\ No newline at end of file
diff --git a/telecomm/java/android/telecom/CallAttributes.java b/telecomm/java/android/telecom/CallAttributes.java
new file mode 100644
index 0000000..6d87981
--- /dev/null
+++ b/telecomm/java/android/telecom/CallAttributes.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * CallAttributes represents a set of properties that define a new Call.  Apps should build an
+ * instance of this class and use {@link TelecomManager#addCall} to start a new call with Telecom.
+ *
+ * <p>
+ * Apps should first register a {@link PhoneAccount} via {@link TelecomManager#registerPhoneAccount}
+ * and use the same {@link PhoneAccountHandle} registered with Telecom when creating an
+ * instance of CallAttributes.
+ */
+public final class CallAttributes implements Parcelable {
+
+    /** PhoneAccountHandle associated with the App managing calls **/
+    private final PhoneAccountHandle mPhoneAccountHandle;
+
+    /** Display name of the person on the other end of the call **/
+    private final CharSequence mDisplayName;
+
+    /** Address of the call. Note, this can be extended to a meeting link **/
+    private final Uri mAddress;
+
+    /** The direction (Outgoing/Incoming) of the new Call **/
+    @Direction private final int mDirection;
+
+    /** Information related to data being transmitted (voice, video, etc. ) **/
+    @CallType private final int mCallType;
+
+    /** Allows a package to opt into capabilities on the telecom side, on a per-call basis **/
+    @CallCapability private final int mCallCapabilities;
+
+    /** @hide **/
+    public static final String CALL_CAPABILITIES_KEY = "TelecomCapabilities";
+
+    private CallAttributes(@NonNull PhoneAccountHandle phoneAccountHandle,
+            @NonNull CharSequence displayName,
+            @NonNull Uri address,
+            int direction,
+            int callType,
+            int callCapabilities) {
+        mPhoneAccountHandle = phoneAccountHandle;
+        mDisplayName = displayName;
+        mAddress = address;
+        mDirection = direction;
+        mCallType = callType;
+        mCallCapabilities = callCapabilities;
+    }
+
+    /** @hide */
+    @IntDef(value = {DIRECTION_INCOMING, DIRECTION_OUTGOING})
+    public @interface Direction {
+    }
+    /**
+     * Indicates that the call is an incoming call.
+     */
+    public static final int DIRECTION_INCOMING = 1;
+    /**
+     * Indicates that the call is an outgoing call.
+     */
+    public static final int DIRECTION_OUTGOING = 2;
+
+    /** @hide */
+    @IntDef(value = {AUDIO_CALL, VIDEO_CALL})
+    public @interface CallType {
+    }
+    /**
+     * Used when answering or dialing a call to indicate that the call does not have a video
+     * component
+     */
+    public static final int AUDIO_CALL = 1;
+    /**
+     * Indicates video transmission is supported
+     */
+    public static final int VIDEO_CALL = 2;
+
+    /** @hide */
+    @IntDef(value = {SUPPORTS_SET_INACTIVE, SUPPORTS_STREAM, SUPPORTS_TRANSFER}, flag = true)
+    public @interface CallCapability {
+    }
+    /**
+     * The call being created can be set to inactive (traditionally referred to as hold).  This
+     * means that once a new call goes active, if the active call needs to be held in order to
+     * place or receive an incoming call, the active call will be placed on hold.  otherwise, the
+     * active call may be disconnected.
+     */
+    public static final int SUPPORTS_SET_INACTIVE = 1 << 1;
+    /**
+     * The call can be streamed from a root device to another device to continue the call without
+     * completely transferring it.
+     */
+    public static final int SUPPORTS_STREAM = 1 << 2;
+    /**
+     * The call can be completely transferred from one endpoint to another
+     */
+    public static final int SUPPORTS_TRANSFER = 1 << 3;
+
+    /**
+     * Build an instance of {@link CallAttributes}. In order to build a valid instance, a
+     * {@link PhoneAccountHandle}, call {@link Direction}, display name, and {@link Uri} address
+     * are required.
+     *
+     * <p>
+     * Note: Pass in the same {@link PhoneAccountHandle} that was used to register a
+     * {@link PhoneAccount} with Telecom. see {@link TelecomManager#registerPhoneAccount}
+     */
+    public static final class Builder {
+        // required and final fields
+        private final PhoneAccountHandle mPhoneAccountHandle;
+        @Direction private final int mDirection;
+        private final CharSequence mDisplayName;
+        private final Uri mAddress;
+        // optional fields
+        @CallType private int mCallType = CallAttributes.AUDIO_CALL;
+        @CallCapability private int mCallCapabilities = SUPPORTS_SET_INACTIVE;
+
+        /**
+         * Constructor for the CallAttributes.Builder class
+         *
+         * @param phoneAccountHandle that belongs to package registered with Telecom
+         * @param callDirection of the new call that will be added to Telecom
+         * @param displayName of the caller for incoming calls or initiating user for outgoing calls
+         * @param address of the caller for incoming calls or destination for outgoing calls
+         */
+        public Builder(@NonNull PhoneAccountHandle phoneAccountHandle,
+                @Direction int callDirection, @NonNull CharSequence displayName,
+                @NonNull Uri address) {
+            if (!isInRange(DIRECTION_INCOMING, DIRECTION_OUTGOING, callDirection)) {
+                throw new IllegalArgumentException(TextUtils.formatSimple("CallDirection=[%d] is"
+                                + " invalid. CallDirections value should be within [%d, %d]",
+                        callDirection, DIRECTION_INCOMING, DIRECTION_OUTGOING));
+            }
+            Objects.requireNonNull(phoneAccountHandle);
+            Objects.requireNonNull(displayName);
+            Objects.requireNonNull(address);
+            mPhoneAccountHandle = phoneAccountHandle;
+            mDirection = callDirection;
+            mDisplayName = displayName;
+            mAddress = address;
+        }
+
+        /**
+         * @param callType see {@link CallType} for valid arguments
+         * @return Builder
+         */
+        @NonNull
+        public Builder setCallType(@CallType int callType) {
+            if (!isInRange(AUDIO_CALL, VIDEO_CALL, callType)) {
+                throw new IllegalArgumentException(TextUtils.formatSimple("CallType=[%d] is"
+                                + " invalid. CallTypes value should be within [%d, %d]",
+                        callType, AUDIO_CALL, VIDEO_CALL));
+            }
+            mCallType = callType;
+            return this;
+        }
+
+        /**
+         * @param callCapabilities see {@link CallCapability} for valid arguments
+         * @return Builder
+         */
+        @NonNull
+        public Builder setCallCapabilities(@CallCapability int callCapabilities) {
+            mCallCapabilities = callCapabilities;
+            return this;
+        }
+
+        /**
+         * Build an instance of {@link CallAttributes} based on the last values passed to the
+         * setters or default values.
+         *
+         * @return an instance of {@link CallAttributes}
+         */
+        @NonNull
+        public CallAttributes build() {
+            return new CallAttributes(mPhoneAccountHandle, mDisplayName, mAddress, mDirection,
+                    mCallType, mCallCapabilities);
+        }
+
+        /** @hide */
+        private boolean isInRange(int floor, int ceiling, int value) {
+            return value >= floor && value <= ceiling;
+        }
+    }
+
+    /**
+     * The {@link PhoneAccountHandle} that should be registered to Telecom to allow calls.  The
+     * {@link PhoneAccountHandle} should be registered before creating a CallAttributes instance.
+     *
+     * @return the {@link PhoneAccountHandle} for this package that allows this call to be created
+     */
+    @NonNull public PhoneAccountHandle getPhoneAccountHandle() {
+        return mPhoneAccountHandle;
+    }
+
+    /**
+     * @return display name of the incoming caller or the person being called for an outgoing call
+     */
+    @NonNull public CharSequence getDisplayName() {
+        return mDisplayName;
+    }
+
+    /**
+     * @return address of the incoming caller
+     *           or the address of the person being called for an outgoing call
+     */
+    @NonNull public Uri getAddress() {
+        return mAddress;
+    }
+
+    /**
+     * @return the direction of the new call.
+     */
+    public @Direction int getDirection() {
+        return mDirection;
+    }
+
+    /**
+     * @return Information related to data being transmitted (voice, video, etc. )
+     */
+    public @CallType int getCallType() {
+        return mCallType;
+    }
+
+    /**
+     * @return The allowed capabilities of the new call
+     */
+    public @CallCapability int getCallCapabilities() {
+        return mCallCapabilities;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@Nullable Parcel dest, int flags) {
+        dest.writeParcelable(mPhoneAccountHandle, flags);
+        dest.writeCharSequence(mDisplayName);
+        dest.writeParcelable(mAddress, flags);
+        dest.writeInt(mDirection);
+        dest.writeInt(mCallType);
+        dest.writeInt(mCallCapabilities);
+    }
+
+    /**
+     * Responsible for creating CallAttribute objects for deserialized Parcels.
+     */
+    public static final @android.annotation.NonNull
+            Parcelable.Creator<CallAttributes> CREATOR =
+            new Parcelable.Creator<>() {
+                @Override
+                public CallAttributes createFromParcel(Parcel source) {
+                    return new CallAttributes(source.readParcelable(getClass().getClassLoader(),
+                            android.telecom.PhoneAccountHandle.class),
+                            source.readCharSequence(),
+                            source.readParcelable(getClass().getClassLoader(),
+                                    android.net.Uri.class),
+                            source.readInt(),
+                            source.readInt(),
+                            source.readInt());
+                }
+
+                @Override
+                public CallAttributes[] newArray(int size) {
+                    return new CallAttributes[size];
+                }
+            };
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append("{ CallAttributes: [phoneAccountHandle: ")
+                .append(mPhoneAccountHandle)  /* PhoneAccountHandle#toString handles PII */
+                .append("], [contactName: ")
+                .append(Log.pii(mDisplayName))
+                .append("], [address=")
+                .append(Log.pii(mAddress))
+                .append("], [direction=")
+                .append(mDirection)
+                .append("], [callType=")
+                .append(mCallType)
+                .append("], [mCallCapabilities=")
+                .append(mCallCapabilities)
+                .append("]  }");
+
+        return sb.toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || obj.getClass() != this.getClass()) {
+            return false;
+        }
+        CallAttributes that = (CallAttributes) obj;
+        return this.mDirection == that.mDirection
+                && this.mCallType == that.mCallType
+                && this.mCallCapabilities == that.mCallCapabilities
+                && Objects.equals(this.mPhoneAccountHandle, that.mPhoneAccountHandle)
+                && Objects.equals(this.mAddress, that.mAddress)
+                && Objects.equals(this.mDisplayName, that.mDisplayName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPhoneAccountHandle, mAddress, mDisplayName,
+                mDirection, mCallType, mCallCapabilities);
+    }
+}
diff --git a/telecomm/java/android/telecom/CallControl.aidl b/telecomm/java/android/telecom/CallControl.aidl
new file mode 100644
index 0000000..0f780e6
--- /dev/null
+++ b/telecomm/java/android/telecom/CallControl.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+/**
+ * {@hide}
+ */
+parcelable CallControl;
diff --git a/telecomm/java/android/telecom/CallControl.java b/telecomm/java/android/telecom/CallControl.java
new file mode 100644
index 0000000..3bda6f4
--- /dev/null
+++ b/telecomm/java/android/telecom/CallControl.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import static android.telecom.CallException.TRANSACTION_EXCEPTION_KEY;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.OutcomeReceiver;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+
+import com.android.internal.telecom.ClientTransactionalServiceRepository;
+import com.android.internal.telecom.ICallControl;
+
+import java.util.concurrent.Executor;
+
+/**
+ * CallControl provides client side control of a call.  Each Call will get an individual CallControl
+ * instance in which the client can alter the state of the associated call.
+ *
+ * <p>
+ * Each method is Transactional meaning that it can succeed or fail. If a transaction succeeds,
+ * the {@link OutcomeReceiver#onResult} will be called by Telecom.  Otherwise, the
+ * {@link OutcomeReceiver#onError} is called and provides a {@link CallException} that details why
+ * the operation failed.
+ */
+public final class CallControl implements AutoCloseable {
+    private static final String TAG = CallControl.class.getSimpleName();
+    private static final String INTERFACE_ERROR_MSG = "Call Control is not available";
+    private final String mCallId;
+    private final ICallControl mServerInterface;
+    private final PhoneAccountHandle mPhoneAccountHandle;
+    private final ClientTransactionalServiceRepository mRepository;
+
+    /** @hide */
+    public CallControl(@NonNull String callId, @Nullable ICallControl serverInterface,
+            @NonNull ClientTransactionalServiceRepository repository,
+            @NonNull PhoneAccountHandle pah) {
+        mCallId = callId;
+        mServerInterface = serverInterface;
+        mRepository = repository;
+        mPhoneAccountHandle = pah;
+    }
+
+    /**
+     * @return the callId Telecom assigned to this CallControl object which should be attached to
+     *  an individual call.
+     */
+    @NonNull
+    public ParcelUuid getCallId() {
+        return ParcelUuid.fromString(mCallId);
+    }
+
+    /**
+     * Request Telecom set the call state to active.
+     *
+     * @param executor The {@link Executor} on which the {@link OutcomeReceiver} callback
+     *                will be called on.
+     * @param callback that will be completed on the Telecom side that details success or failure
+     *                of the requested operation.
+     *
+     *                 {@link OutcomeReceiver#onResult} will be called if Telecom has successfully
+     *                 switched the call state to active
+     *
+     *                 {@link OutcomeReceiver#onError} will be called if Telecom has failed to set
+     *                 the call state to active.  A {@link CallException} will be passed
+     *                 that details why the operation failed.
+     */
+    public void setActive(@CallbackExecutor @NonNull Executor executor,
+            @NonNull OutcomeReceiver<Void, CallException> callback) {
+        if (mServerInterface != null) {
+            try {
+                mServerInterface.setActive(mCallId,
+                        new CallControlResultReceiver("setActive", executor, callback));
+
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
+        } else {
+            throw new IllegalStateException(INTERFACE_ERROR_MSG);
+        }
+    }
+
+    /**
+     * Request Telecom set the call state to inactive. This the same as hold for two call endpoints
+     * but can be extended to setting a meeting to inactive.
+     *
+     * @param executor The {@link Executor} on which the {@link OutcomeReceiver} callback
+     *                will be called on.
+     * @param callback that will be completed on the Telecom side that details success or failure
+     *                of the requested operation.
+     *
+     *                 {@link OutcomeReceiver#onResult} will be called if Telecom has successfully
+     *                 switched the call state to inactive
+     *
+     *                 {@link OutcomeReceiver#onError} will be called if Telecom has failed to set
+     *                 the call state to inactive.  A {@link CallException} will be passed
+     *                 that details why the operation failed.
+     */
+    public void setInactive(@CallbackExecutor @NonNull Executor executor,
+            @NonNull OutcomeReceiver<Void, CallException> callback) {
+        if (mServerInterface != null) {
+            try {
+                mServerInterface.setInactive(mCallId,
+                        new CallControlResultReceiver("setInactive", executor, callback));
+
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
+        } else {
+            throw new IllegalStateException(INTERFACE_ERROR_MSG);
+        }
+    }
+
+    /**
+     * Request Telecom set the call state to disconnect.
+     *
+     * @param executor The {@link Executor} on which the {@link OutcomeReceiver} callback
+     *                will be called on.
+     * @param callback that will be completed on the Telecom side that details success or failure
+     *                of the requested operation.
+     *
+     *                 {@link OutcomeReceiver#onResult} will be called if Telecom has successfully
+     *                 disconnected the call.
+     *
+     *                 {@link OutcomeReceiver#onError} will be called if Telecom has failed to
+     *                 disconnect the call.  A {@link CallException} will be passed
+     *                 that details why the operation failed.
+     */
+    public void disconnect(@NonNull DisconnectCause disconnectCause,
+            @CallbackExecutor @NonNull Executor executor,
+            @NonNull OutcomeReceiver<Void, CallException> callback) {
+        if (mServerInterface != null) {
+            try {
+                mServerInterface.disconnect(mCallId, disconnectCause,
+                        new CallControlResultReceiver("disconnect", executor, callback));
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
+        } else {
+            throw new IllegalStateException(INTERFACE_ERROR_MSG);
+        }
+    }
+
+    /**
+     * Request Telecom reject the incoming call.
+     *
+     * @param executor The {@link Executor} on which the {@link OutcomeReceiver} callback
+     *                will be called on.
+     * @param callback that will be completed on the Telecom side that details success or failure
+     *                of the requested operation.
+     *
+     *                 {@link OutcomeReceiver#onResult} will be called if Telecom has successfully
+     *                 rejected the incoming call.
+     *
+     *                 {@link OutcomeReceiver#onError} will be called if Telecom has failed to
+     *                 reject the incoming call.  A {@link CallException} will be passed
+     *                 that details why the operation failed.
+     */
+    public void rejectCall(@CallbackExecutor @NonNull Executor executor,
+            @NonNull OutcomeReceiver<Void, CallException> callback) {
+        if (mServerInterface != null) {
+            try {
+                mServerInterface.rejectCall(mCallId,
+                        new CallControlResultReceiver("rejectCall", executor, callback));
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
+        } else {
+            throw new IllegalStateException(INTERFACE_ERROR_MSG);
+        }
+    }
+
+    /**
+     * This method should be called after
+     * {@link CallControl#disconnect(DisconnectCause, Executor, OutcomeReceiver)} or
+     * {@link CallControl#rejectCall(Executor, OutcomeReceiver)}
+     * to destroy all references of this object and avoid memory leaks.
+     */
+    @Override
+    public void close() {
+        mRepository.removeCallFromServiceWrapper(mPhoneAccountHandle, mCallId);
+    }
+
+    /**
+     * Since {@link OutcomeReceiver}s cannot be passed via AIDL, a ResultReceiver (which can) must
+     * wrap the Clients {@link OutcomeReceiver} passed in and await for the Telecom Server side
+     * response in {@link ResultReceiver#onReceiveResult(int, Bundle)}.
+     * @hide */
+    private class CallControlResultReceiver extends ResultReceiver {
+        private final String mCallingMethod;
+        private final Executor mExecutor;
+        private final OutcomeReceiver<Void, CallException> mClientCallback;
+
+        CallControlResultReceiver(String method, Executor executor,
+                OutcomeReceiver<Void, CallException> clientCallback) {
+            super(null);
+            mCallingMethod = method;
+            mExecutor = executor;
+            mClientCallback = clientCallback;
+        }
+
+        @Override
+        protected void onReceiveResult(int resultCode, Bundle resultData) {
+            Log.d(CallControl.TAG, "%s: oRR: resultCode=[%s]", mCallingMethod, resultCode);
+            super.onReceiveResult(resultCode, resultData);
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                if (resultCode == TelecomManager.TELECOM_TRANSACTION_SUCCESS) {
+                    mExecutor.execute(() -> mClientCallback.onResult(null));
+                } else {
+                    mExecutor.execute(() ->
+                            mClientCallback.onError(getTransactionException(resultData)));
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+    }
+
+    /** @hide */
+    private CallException getTransactionException(Bundle resultData) {
+        String message = "unknown error";
+        if (resultData != null && resultData.containsKey(TRANSACTION_EXCEPTION_KEY)) {
+            return resultData.getParcelable(TRANSACTION_EXCEPTION_KEY,
+                    CallException.class);
+        }
+        return new CallException(message, CallException.CODE_ERROR_UNKNOWN);
+    }
+}
diff --git a/telecomm/java/android/telecom/CallEventCallback.java b/telecomm/java/android/telecom/CallEventCallback.java
new file mode 100644
index 0000000..a26291f
--- /dev/null
+++ b/telecomm/java/android/telecom/CallEventCallback.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+
+import android.annotation.NonNull;
+
+import java.util.function.Consumer;
+
+/**
+ * CallEventCallback relays updates to a call from the Telecom framework.
+ * This can include operations which the app must implement on a Call due to the presence of other
+ * calls on the device, requests relayed from a Bluetooth device, or from another calling surface.
+ *
+ * <p>
+ * CallEventCallbacks with {@link Consumer}s are transactional, meaning that a client must
+ * complete the {@link Consumer} via {@link Consumer#accept(Object)} in order to complete the
+ * CallEventCallback. If a CallEventCallback can be completed, the
+ * {@link Consumer#accept(Object)} should be called with {@link Boolean#TRUE}. Otherwise,
+ * {@link Consumer#accept(Object)} should be called with {@link Boolean#FALSE} to represent the
+ * CallEventCallback cannot be completed on the client side.
+ *
+ * <p>
+ * Note: Each CallEventCallback has a timeout of 5000 milliseconds. Failing to complete the
+ * {@link Consumer} before the timeout will result in a failed transaction.
+ */
+public interface CallEventCallback {
+    /**
+     * Telecom is informing the client to set the call active
+     *
+     * @param wasCompleted The {@link Consumer} to be completed. If the client can set the call
+     *                     active on their end, the {@link Consumer#accept(Object)} should be
+     *                     called with {@link Boolean#TRUE}. Otherwise,
+     *                     {@link Consumer#accept(Object)} should be called with
+     *                     {@link Boolean#FALSE}.
+     */
+    void onSetActive(@NonNull Consumer<Boolean> wasCompleted);
+
+    /**
+     * Telecom is informing the client to set the call inactive. This is the same as holding a call
+     * for two endpoints but can be extended to setting a meeting inactive.
+     *
+     * @param wasCompleted The {@link Consumer} to be completed. If the client can set the call
+     *                     inactive on their end, the {@link Consumer#accept(Object)} should be
+     *                     called with {@link Boolean#TRUE}. Otherwise,
+     *                     {@link Consumer#accept(Object)} should be called with
+     *                     {@link Boolean#FALSE}.
+     */
+    void onSetInactive(@NonNull Consumer<Boolean> wasCompleted);
+
+    /**
+     * Telecom is informing the client to answer an incoming call and set it to active.
+     *
+     * @param videoState   see {@link android.telecom.CallAttributes.CallType} for valid states
+     * @param wasCompleted The {@link Consumer} to be completed. If the client can answer the call
+     *                     on their end, {@link Consumer#accept(Object)} should be called with
+     *                     {@link Boolean#TRUE}. Otherwise, {@link Consumer#accept(Object)} should
+     *                     be called with {@link Boolean#FALSE}.
+     */
+    void onAnswer(@android.telecom.CallAttributes.CallType int videoState,
+            @NonNull Consumer<Boolean> wasCompleted);
+
+    /**
+     * Telecom is informing the client to reject the incoming call
+     *
+     * @param wasCompleted The {@link Consumer} to be completed. If the client can reject the
+     *                     incoming call, {@link Consumer#accept(Object)} should be called with
+     *                     {@link Boolean#TRUE}. Otherwise, {@link Consumer#accept(Object)}
+     *                     should  be called with {@link Boolean#FALSE}.
+     */
+    void onReject(@NonNull Consumer<Boolean> wasCompleted);
+
+    /**
+     * Telecom is informing the client to disconnect the call
+     *
+     * @param wasCompleted The {@link Consumer} to be completed. If the client can disconnect the
+     *                     call on their end, {@link Consumer#accept(Object)} should be called with
+     *                     {@link Boolean#TRUE}. Otherwise, {@link Consumer#accept(Object)}
+     *                     should  be called with {@link Boolean#FALSE}.
+     */
+    void onDisconnect(@NonNull Consumer<Boolean> wasCompleted);
+
+    /**
+     * update the client on the new {@link CallAudioState}
+     *
+     * @param callAudioState that is currently being used
+     */
+    void onCallAudioStateChanged(@NonNull CallAudioState callAudioState);
+}
diff --git a/telecomm/java/android/telecom/CallException.aidl b/telecomm/java/android/telecom/CallException.aidl
new file mode 100644
index 0000000..a16af12
--- /dev/null
+++ b/telecomm/java/android/telecom/CallException.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+/**
+ * {@hide}
+ */
+parcelable CallException;
\ No newline at end of file
diff --git a/telecomm/java/android/telecom/CallException.java b/telecomm/java/android/telecom/CallException.java
new file mode 100644
index 0000000..0b0de6b
--- /dev/null
+++ b/telecomm/java/android/telecom/CallException.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecom;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class defines exceptions that can be thrown when using Telecom APIs with
+ * {@link android.os.OutcomeReceiver}s.  Most of these exceptions are thrown when changing a call
+ * state with {@link CallControl}s or {@link CallEventCallback}s.
+ */
+public final class CallException extends RuntimeException implements Parcelable {
+    /** @hide **/
+    public static final String TRANSACTION_EXCEPTION_KEY = "TelecomTransactionalExceptionKey";
+
+    /**
+     * The operation has failed due to an unknown or unspecified error.
+     */
+    public static final int CODE_ERROR_UNKNOWN = 1;
+
+    /**
+     * The operation has failed due to Telecom failing to hold the current active call for the
+     * call attempting to become the new active call.  The client should end the current active call
+     * and re-try the failed operation.
+     */
+    public static final int CODE_CANNOT_HOLD_CURRENT_ACTIVE_CALL = 2;
+
+    /**
+     * The operation has failed because Telecom has already removed the call from the server side
+     * and destroyed all the objects associated with it.  The client should re-add the call.
+     */
+    public static final int CODE_CALL_IS_NOT_BEING_TRACKED = 3;
+
+    /**
+     * The operation has failed because Telecom cannot set the requested call as the current active
+     * call.  The client should end the current active call and re-try the operation.
+     */
+    public static final int CODE_CALL_CANNOT_BE_SET_TO_ACTIVE = 4;
+
+    /**
+     * The operation has failed because there is either no PhoneAccount registered with Telecom
+     * for the given operation, or the limit of calls has been reached. The client should end the
+     * current active call and re-try the failed operation.
+     */
+    public static final int CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME = 5;
+
+    /**
+     * The operation has failed because the operation failed to complete before the timeout
+     */
+    public static final int CODE_OPERATION_TIMED_OUT = 6;
+
+    private int mCode = CODE_ERROR_UNKNOWN;
+    private final String mMessage;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString8(mMessage);
+        dest.writeInt(mCode);
+    }
+
+    /**
+     * Responsible for creating CallAttribute objects for deserialized Parcels.
+     */
+    public static final @android.annotation.NonNull
+            Parcelable.Creator<CallException> CREATOR = new Parcelable.Creator<>() {
+                    @Override
+                    public CallException createFromParcel(Parcel source) {
+                        return new CallException(source.readString8(), source.readInt());
+                    }
+
+                    @Override
+                    public CallException[] newArray(int size) {
+                        return new CallException[size];
+                    }
+            };
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "CODE_ERROR_", value = {
+            CODE_ERROR_UNKNOWN,
+            CODE_CANNOT_HOLD_CURRENT_ACTIVE_CALL,
+            CODE_CALL_IS_NOT_BEING_TRACKED,
+            CODE_CALL_CANNOT_BE_SET_TO_ACTIVE,
+            CODE_CALL_NOT_PERMITTED_AT_PRESENT_TIME,
+            CODE_OPERATION_TIMED_OUT
+    })
+    public @interface CallErrorCode {
+    }
+
+    /**
+     * Constructor for a new CallException when only message can be specified.
+     * {@code CODE_ERROR_UNKNOWN} will be default code returned when calling {@code getCode}
+     *
+     * @param message related to why the exception was created
+     */
+    public CallException(@Nullable String message) {
+        super(getMessage(message, CODE_ERROR_UNKNOWN));
+        mMessage = message;
+    }
+
+    /**
+     * Constructor for a new CallException that has a defined error code in this class
+     *
+     * @param message related to why the exception was created
+     * @param code defined above that caused this exception to be created
+     */
+    public CallException(@Nullable String message, @CallErrorCode int code) {
+        super(getMessage(message, code));
+        mCode = code;
+        mMessage = message;
+    }
+
+    /**
+     * @return one of the error codes defined in this class that was passed into the constructor
+     */
+    public @CallErrorCode int getCode() {
+        return mCode;
+    }
+
+    private static String getMessage(String message, int code) {
+        StringBuilder builder;
+        if (!TextUtils.isEmpty(message)) {
+            builder = new StringBuilder(message);
+            builder.append(" (code: ");
+            builder.append(code);
+            builder.append(")");
+            return builder.toString();
+        } else {
+            return "code: " + code;
+        }
+    }
+}
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index ec18c6a..047ab3a 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -418,7 +418,26 @@
      */
     public static final int CAPABILITY_VOICE_CALLING_AVAILABLE = 0x20000;
 
-    /* NEXT CAPABILITY: 0x40000 */
+
+    /**
+     * Flag indicating that this {@link PhoneAccount} supports the use TelecomManager APIs that
+     * utilize {@link android.os.OutcomeReceiver}s or {@link java.util.function.Consumer}s.
+     * Be aware, if this capability is set, {@link #CAPABILITY_SELF_MANAGED} will be amended by
+     * Telecom when this {@link PhoneAccount} is registered via
+     * {@link TelecomManager#registerPhoneAccount(PhoneAccount)}.
+     *
+     * <p>
+     * {@link android.os.OutcomeReceiver}s and {@link java.util.function.Consumer}s represent
+     * transactional operations because the operation can succeed or fail.  An app wishing to use
+     * transactional operations should define behavior for a successful and failed TelecomManager
+     * API call.
+     *
+     * @see #CAPABILITY_SELF_MANAGED
+     * @see #getCapabilities
+     */
+    public static final int CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS = 0x40000;
+
+    /* NEXT CAPABILITY: [0x80000, 0x100000, 0x200000] */
 
     /**
      * URI scheme for telephone number URIs.
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index af37ed5..7c86a75a 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -18,6 +18,7 @@
 import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
 
 import android.Manifest;
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -38,6 +39,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.OutcomeReceiver;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -49,6 +51,8 @@
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.telecom.ClientTransactionalServiceRepository;
+import com.android.internal.telecom.ClientTransactionalServiceWrapper;
 import com.android.internal.telecom.ITelecomService;
 
 import java.lang.annotation.Retention;
@@ -1056,6 +1060,14 @@
 
     private final ITelecomService mTelecomServiceOverride;
 
+    /** @hide **/
+    private final ClientTransactionalServiceRepository mTransactionalServiceRepository =
+            new ClientTransactionalServiceRepository();
+    /** @hide **/
+    public static final int TELECOM_TRANSACTION_SUCCESS = 0;
+    /** @hide **/
+    public static final String TRANSACTION_CALL_ID_KEY = "TelecomCallId";
+
     /**
      * @hide
      */
@@ -2640,6 +2652,92 @@
     }
 
     /**
+     * Adds a new call with the specified {@link CallAttributes} to the telecom service. This method
+     * can be used to add both incoming and outgoing calls.
+     *
+     * <p>
+     * The difference between this API call and {@link TelecomManager#placeCall(Uri, Bundle)} or
+     * {@link TelecomManager#addNewIncomingCall(PhoneAccountHandle, Bundle)} is that this API
+     * will asynchronously provide an update on whether the new call was added successfully via
+     * an {@link OutcomeReceiver}.  Additionally, callbacks will run on the executor thread that was
+     * passed in.
+     *
+     * <p>
+     * Note: Only packages that register with
+     * {@link PhoneAccount#CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS}
+     * can utilize this API. {@link PhoneAccount}s that set the capabilities
+     * {@link PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION},
+     * {@link PhoneAccount#CAPABILITY_CALL_PROVIDER},
+     * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}
+     * are not supported and will cause an exception to be thrown.
+     *
+     * <p>
+     * Usage example:
+     * <pre>
+     *
+     *  // An app should first define their own construct of a Call that overrides all the
+     *  // {@link CallEventCallback}s
+     *  private class MyVoipCall implements CallEventCallback {
+     *    // override all the {@link CallEventCallback}s
+     *  }
+     *
+     * PhoneAccountHandle handle = new PhoneAccountHandle(
+     *                          new ComponentName("com.example.voip.app",
+     *                                            "com.example.voip.app.NewCallActivity"), "123");
+     *
+     * CallAttributes callAttributes = new CallAttributes.Builder(handle,
+     *                                             CallAttributes.DIRECTION_OUTGOING,
+     *                                            "John Smith", Uri.fromParts("tel", "123", null))
+     *                                            .build();
+     *
+     * telecomManager.addCall(callAttributes, Runnable::run, new OutcomeReceiver() {
+     *                              public void onResult(CallControl callControl) {
+     *                                 // The call has been added successfully
+     *                              }
+     *                           }, new MyVoipCall());
+     * </pre>
+     *
+     * @param callAttributes    attributes of the new call (incoming or outgoing, address, etc. )
+     * @param executor          thread to run background CallEventCallback updates on
+     * @param pendingControl    OutcomeReceiver that receives the result of addCall transaction
+     * @param callEventCallback object that overrides CallEventCallback
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_OWN_CALLS)
+    @SuppressLint("SamShouldBeLast")
+    public void addCall(@NonNull CallAttributes callAttributes,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<CallControl, CallException> pendingControl,
+            @NonNull CallEventCallback callEventCallback) {
+        Objects.requireNonNull(callAttributes);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(pendingControl);
+        Objects.requireNonNull(callEventCallback);
+
+        ITelecomService service = getTelecomService();
+        if (service != null) {
+            try {
+                // create or add the new call to a service wrapper w/ the same phoneAccountHandle
+                ClientTransactionalServiceWrapper transactionalServiceWrapper =
+                        mTransactionalServiceRepository.addNewCallForTransactionalServiceWrapper(
+                                callAttributes.getPhoneAccountHandle());
+
+                // couple all the args passed by the client
+                String newCallId = transactionalServiceWrapper.trackCall(callAttributes, executor,
+                        pendingControl, callEventCallback);
+
+                // send args to server to process new call
+                service.addCall(callAttributes, transactionalServiceWrapper.getCallEventCallback(),
+                        newCallId, mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException addCall: " + e);
+                e.rethrowFromSystemServer();
+            }
+        } else {
+            throw new IllegalStateException("Telecom service is not present");
+        }
+    }
+
+    /**
      * Handles {@link Intent#ACTION_CALL} intents trampolined from UserCallActivity.
      * @param intent The {@link Intent#ACTION_CALL} intent to handle.
      * @param callingPackageProxy The original package that called this before it was trampolined.
diff --git a/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceRepository.java b/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceRepository.java
new file mode 100644
index 0000000..2eebbdb
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceRepository.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telecom;
+
+import android.telecom.PhoneAccountHandle;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @hide
+ */
+public class ClientTransactionalServiceRepository {
+
+    private static final Map<PhoneAccountHandle, ClientTransactionalServiceWrapper> LOOKUP_TABLE =
+            new ConcurrentHashMap<>();
+
+    /**
+     * creates a new {@link ClientTransactionalServiceWrapper} if this is the first call being
+     * tracked for a particular package Or adds a new call for an existing
+     * {@link ClientTransactionalServiceWrapper}
+     *
+     * @param phoneAccountHandle for a particular package requesting to create a call
+     * @return the {@link ClientTransactionalServiceWrapper} that is tied tot the PhoneAccountHandle
+     */
+    public ClientTransactionalServiceWrapper addNewCallForTransactionalServiceWrapper(
+            PhoneAccountHandle phoneAccountHandle) {
+
+        ClientTransactionalServiceWrapper service = null;
+        if (!hasExistingServiceWrapper(phoneAccountHandle)) {
+            service = new ClientTransactionalServiceWrapper(phoneAccountHandle, this);
+        } else {
+            service = getTransactionalServiceWrapper(phoneAccountHandle);
+        }
+
+        LOOKUP_TABLE.put(phoneAccountHandle, service);
+
+        return service;
+    }
+
+    private ClientTransactionalServiceWrapper getTransactionalServiceWrapper(
+            PhoneAccountHandle pah) {
+        return LOOKUP_TABLE.get(pah);
+    }
+
+    private boolean hasExistingServiceWrapper(PhoneAccountHandle pah) {
+        return LOOKUP_TABLE.containsKey(pah);
+    }
+
+    /**
+     * @param pah that is tied to a particular package with potential tracked calls
+     * @return if the {@link ClientTransactionalServiceWrapper} was successfully removed
+     */
+    public boolean removeServiceWrapper(PhoneAccountHandle pah) {
+        if (!hasExistingServiceWrapper(pah)) {
+            return false;
+        }
+        LOOKUP_TABLE.remove(pah);
+        return true;
+    }
+
+    /**
+     * @param pah    that is tied to a particular package with potential tracked calls
+     * @param callId of the TransactionalCall that you want to remove
+     * @return if the call was successfully removed from the service wrapper
+     */
+    public boolean removeCallFromServiceWrapper(PhoneAccountHandle pah, String callId) {
+        if (!hasExistingServiceWrapper(pah)) {
+            return false;
+        }
+        ClientTransactionalServiceWrapper service = LOOKUP_TABLE.get(pah);
+        service.untrackCall(callId);
+        return true;
+    }
+
+}
diff --git a/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java b/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java
new file mode 100644
index 0000000..682dba1
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telecom;
+
+import static android.telecom.TelecomManager.TELECOM_TRANSACTION_SUCCESS;
+
+import android.os.Binder;
+import android.os.OutcomeReceiver;
+import android.os.ResultReceiver;
+import android.telecom.CallAttributes;
+import android.telecom.CallAudioState;
+import android.telecom.CallControl;
+import android.telecom.CallEventCallback;
+import android.telecom.CallException;
+import android.telecom.PhoneAccountHandle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * wraps {@link CallEventCallback} and {@link CallControl} on a
+ * per-{@link  android.telecom.PhoneAccountHandle} basis to track ongoing calls.
+ *
+ * @hide
+ */
+public class ClientTransactionalServiceWrapper {
+
+    private static final String TAG = ClientTransactionalServiceWrapper.class.getSimpleName();
+    private final PhoneAccountHandle mPhoneAccountHandle;
+    private final ClientTransactionalServiceRepository mRepository;
+    private final ConcurrentHashMap<String, TransactionalCall> mCallIdToTransactionalCall =
+            new ConcurrentHashMap<>();
+    private static final String EXECUTOR_FAIL_MSG =
+            "Telecom hit an exception while handling a CallEventCallback on an executor: ";
+
+    public ClientTransactionalServiceWrapper(PhoneAccountHandle handle,
+            ClientTransactionalServiceRepository repo) {
+        mPhoneAccountHandle = handle;
+        mRepository = repo;
+    }
+
+    /**
+     * remove the given call from the class HashMap
+     *
+     * @param callId that is tied to TransactionalCall object
+     */
+    public void untrackCall(String callId) {
+        Log.i(TAG, TextUtils.formatSimple("removeCall: with id=[%s]", callId));
+        if (mCallIdToTransactionalCall.containsKey(callId)) {
+            // remove the call from the hashmap
+            TransactionalCall call = mCallIdToTransactionalCall.remove(callId);
+            // null out interface to avoid memory leaks
+            CallControl control = call.getCallControl();
+            if (control != null) {
+                call.setCallControl(null);
+            }
+        }
+        // possibly cleanup service wrapper if there are no more calls
+        if (mCallIdToTransactionalCall.size() == 0) {
+            mRepository.removeServiceWrapper(mPhoneAccountHandle);
+        }
+    }
+
+    /**
+     * start tracking a newly created call for a particular package
+     *
+     * @param callAttributes of the new call
+     * @param executor       to run callbacks on
+     * @param pendingControl that allows telecom to call into the client
+     * @param callback       that overrides the CallEventCallback
+     * @return the callId of the newly created call
+     */
+    public String trackCall(CallAttributes callAttributes, Executor executor,
+            OutcomeReceiver<CallControl, CallException> pendingControl,
+            CallEventCallback callback) {
+        // generate a new id for this new call
+        String newCallId = UUID.randomUUID().toString();
+
+        // couple the objects passed from the client side
+        mCallIdToTransactionalCall.put(newCallId, new TransactionalCall(newCallId, callAttributes,
+                executor, pendingControl, callback));
+
+        return newCallId;
+    }
+
+    public ICallEventCallback getCallEventCallback() {
+        return mCallEventCallback;
+    }
+
+    /**
+     * Consumers that is to be completed by the client and the result relayed back to telecom server
+     * side via a {@link ResultReceiver}. see com.android.server.telecom.TransactionalServiceWrapper
+     * for how the response is handled.
+     */
+    private class ReceiverWrapper implements Consumer<Boolean> {
+        private final ResultReceiver mRepeaterReceiver;
+
+        ReceiverWrapper(ResultReceiver resultReceiver) {
+            mRepeaterReceiver = resultReceiver;
+        }
+
+        @Override
+        public void accept(Boolean clientCompletedCallbackSuccessfully) {
+            if (clientCompletedCallbackSuccessfully) {
+                mRepeaterReceiver.send(TELECOM_TRANSACTION_SUCCESS, null);
+            } else {
+                mRepeaterReceiver.send(CallException.CODE_ERROR_UNKNOWN, null);
+            }
+        }
+
+        @Override
+        public Consumer<Boolean> andThen(Consumer<? super Boolean> after) {
+            return Consumer.super.andThen(after);
+        }
+    }
+
+    private final ICallEventCallback mCallEventCallback = new ICallEventCallback.Stub() {
+
+        private static final String ON_SET_ACTIVE = "onSetActive";
+        private static final String ON_SET_INACTIVE = "onSetInactive";
+        private static final String ON_ANSWER = "onAnswer";
+        private static final String ON_REJECT = "onReject";
+        private static final String ON_DISCONNECT = "onDisconnect";
+
+        private void handleCallEventCallback(String action, String callId, int code,
+                ResultReceiver ackResultReceiver) {
+            Log.i(TAG, TextUtils.formatSimple("hCEC: id=[%s], action=[%s]", callId, action));
+            // lookup the callEventCallback associated with the particular call
+            TransactionalCall call = mCallIdToTransactionalCall.get(callId);
+
+            if (call != null) {
+                // Get the CallEventCallback interface
+                CallEventCallback callback = call.getCallEventCallback();
+                // Get Receiver to wait on client ack
+                ReceiverWrapper outcomeReceiverWrapper = new ReceiverWrapper(ackResultReceiver);
+
+                // wait for the client to complete the CallEventCallback
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    call.getExecutor().execute(() -> {
+                        switch (action) {
+                            case ON_SET_ACTIVE:
+                                callback.onSetActive(outcomeReceiverWrapper);
+                                break;
+                            case ON_SET_INACTIVE:
+                                callback.onSetInactive(outcomeReceiverWrapper);
+                                break;
+                            case ON_REJECT:
+                                callback.onReject(outcomeReceiverWrapper);
+                                untrackCall(callId);
+                                break;
+                            case ON_DISCONNECT:
+                                callback.onDisconnect(outcomeReceiverWrapper);
+                                untrackCall(callId);
+                                break;
+                            case ON_ANSWER:
+                                callback.onAnswer(code, outcomeReceiverWrapper);
+                                break;
+                        }
+                    });
+                } catch (Exception e) {
+                    Log.e(TAG, EXECUTOR_FAIL_MSG + e);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        }
+
+        @Override
+        public void onAddCallControl(String callId, int resultCode, ICallControl callControl,
+                CallException transactionalException) {
+            Log.i(TAG, TextUtils.formatSimple("oACC: id=[%s], code=[%d]", callId, resultCode));
+            TransactionalCall call = mCallIdToTransactionalCall.get(callId);
+
+            if (call != null) {
+                OutcomeReceiver<CallControl, CallException> pendingControl =
+                        call.getPendingControl();
+
+                if (resultCode == TELECOM_TRANSACTION_SUCCESS) {
+
+                    // create the interface object that the client will interact with
+                    CallControl control = new CallControl(callId, callControl, mRepository,
+                            mPhoneAccountHandle);
+                    // give the client the object via the OR that was passed into addCall
+                    pendingControl.onResult(control);
+
+                    // store for later reference
+                    call.setCallControl(control);
+                } else {
+                    pendingControl.onError(transactionalException);
+                    mCallIdToTransactionalCall.remove(callId);
+                }
+
+            } else {
+                untrackCall(callId);
+                Log.e(TAG, "oACC: TransactionalCall object not found for call w/ id=" + callId);
+            }
+        }
+
+        @Override
+        public void onSetActive(String callId, ResultReceiver resultReceiver) {
+            handleCallEventCallback(ON_SET_ACTIVE, callId, 0, resultReceiver);
+        }
+
+
+        @Override
+        public void onSetInactive(String callId, ResultReceiver resultReceiver) {
+            handleCallEventCallback(ON_SET_INACTIVE, callId, 0, resultReceiver);
+        }
+
+        @Override
+        public void onAnswer(String callId, int videoState, ResultReceiver resultReceiver) {
+            handleCallEventCallback(ON_ANSWER, callId, videoState, resultReceiver);
+        }
+
+        @Override
+        public void onReject(String callId, ResultReceiver resultReceiver) {
+            handleCallEventCallback(ON_REJECT, callId, 0, resultReceiver);
+        }
+
+        @Override
+        public void onDisconnect(String callId, ResultReceiver resultReceiver) {
+            handleCallEventCallback(ON_DISCONNECT, callId, 0, resultReceiver);
+        }
+
+        @Override
+        public void onCallAudioStateChanged(String callId, CallAudioState callAudioState) {
+            Log.i(TAG, TextUtils.formatSimple("onCallAudioStateChanged: callId=[%s]", callId));
+            // lookup the callEventCallback associated with the particular call
+            TransactionalCall call = mCallIdToTransactionalCall.get(callId);
+            if (call != null) {
+                CallEventCallback callback = call.getCallEventCallback();
+                Executor executor = call.getExecutor();
+                executor.execute(() -> {
+                    callback.onCallAudioStateChanged(callAudioState);
+                });
+            }
+        }
+
+        @Override
+        public void removeCallFromTransactionalServiceWrapper(String callId) {
+            untrackCall(callId);
+        }
+    };
+}
diff --git a/telecomm/java/com/android/internal/telecom/ICallControl.aidl b/telecomm/java/com/android/internal/telecom/ICallControl.aidl
new file mode 100644
index 0000000..bf68c5e
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ICallControl.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telecom;
+
+import android.telecom.CallControl;
+import android.telecom.DisconnectCause;
+import android.os.ResultReceiver;
+
+/**
+ * {@hide}
+ */
+oneway interface ICallControl {
+    void setActive(String callId, in ResultReceiver callback);
+    void setInactive(String callId, in ResultReceiver callback);
+    void disconnect(String callId, in DisconnectCause disconnectCause, in ResultReceiver callback);
+    void rejectCall(String callId, in ResultReceiver callback);
+}
\ No newline at end of file
diff --git a/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl b/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl
new file mode 100644
index 0000000..7f5825a
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telecom;
+
+import android.telecom.CallControl;
+import com.android.internal.telecom.ICallControl;
+import android.os.ResultReceiver;
+import android.telecom.CallAudioState;
+import android.telecom.CallException;
+
+/**
+ * {@hide}
+ */
+oneway interface ICallEventCallback {
+    // publicly exposed. Client should override
+    void onAddCallControl(String callId, int resultCode, in ICallControl callControl,
+     in CallException exception);
+    void onSetActive(String callId, in ResultReceiver callback);
+    void onSetInactive(String callId, in ResultReceiver callback);
+    void onAnswer(String callId, int videoState, in ResultReceiver callback);
+    void onReject(String callId, in ResultReceiver callback);
+    void onDisconnect(String callId, in ResultReceiver callback);
+    void onCallAudioStateChanged(String callId, in CallAudioState callAudioState);
+    // hidden methods that help with cleanup
+    void removeCallFromTransactionalServiceWrapper(String callId);
+}
\ No newline at end of file
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index f1a6dd1..fdcb974 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -25,6 +25,8 @@
 import android.os.UserHandle;
 import android.telecom.PhoneAccount;
 import android.content.pm.ParceledListSlice;
+import android.telecom.CallAttributes;
+import com.android.internal.telecom.ICallEventCallback;
 
 /**
  * Interface used to interact with Telecom. Mostly this is used by TelephonyManager for passing
@@ -391,4 +393,10 @@
      */
     boolean isInSelfManagedCall(String packageName, in UserHandle userHandle,
         String callingPackage);
+
+    /**
+     * @see TelecomServiceImpl#addCall
+     */
+    void addCall(in CallAttributes callAttributes, in ICallEventCallback callback, String callId,
+        String callingPackage);
 }
diff --git a/telecomm/java/com/android/internal/telecom/TransactionalCall.java b/telecomm/java/com/android/internal/telecom/TransactionalCall.java
new file mode 100644
index 0000000..d9c8210
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecom/TransactionalCall.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telecom;
+
+import android.os.OutcomeReceiver;
+import android.telecom.CallAttributes;
+import android.telecom.CallControl;
+import android.telecom.CallEventCallback;
+import android.telecom.CallException;
+
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class TransactionalCall {
+
+    private final String mCallId;
+    private final CallAttributes mCallAttributes;
+    private final Executor mExecutor;
+    private final OutcomeReceiver<CallControl, CallException> mPendingControl;
+    private final CallEventCallback mCallEventCallback;
+    private CallControl mCallControl;
+
+    public TransactionalCall(String callId, CallAttributes callAttributes,
+            Executor executor, OutcomeReceiver<CallControl, CallException>  pendingControl,
+            CallEventCallback callEventCallback) {
+        mCallId = callId;
+        mCallAttributes = callAttributes;
+        mExecutor = executor;
+        mPendingControl = pendingControl;
+        mCallEventCallback = callEventCallback;
+    }
+
+    public void setCallControl(CallControl callControl) {
+        mCallControl = callControl;
+    }
+
+    public CallControl getCallControl() {
+        return mCallControl;
+    }
+
+    public String getCallId() {
+        return mCallId;
+    }
+
+    public CallAttributes getCallAttributes() {
+        return mCallAttributes;
+    }
+
+    public Executor getExecutor() {
+        return mExecutor;
+    }
+
+    public OutcomeReceiver<CallControl, CallException> getPendingControl() {
+        return mPendingControl;
+    }
+
+    public CallEventCallback getCallEventCallback() {
+        return mCallEventCallback;
+    }
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index a5203c4..6f462b1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4468,6 +4468,18 @@
             "data_switch_validation_timeout_long";
 
     /**
+     * The minimum timeout of UDP port 4500 NAT / firewall entries on the Internet PDN of this
+     * carrier network. This will be used by Android platform VPNs to tune IPsec NAT keepalive
+     * interval. If this value is too low to provide uninterrupted inbound connectivity, then
+     * Android system VPNs may indicate to applications that the VPN cannot support long-lived
+     * TCP connections.
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final String KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT =
+            "min_udp_port_4500_nat_timeout_sec_int";
+
+    /**
      * Specifies whether the system should prefix the EAP method to the anonymous identity.
      * The following prefix will be added if this key is set to TRUE:
      *   EAP-AKA: "0"
@@ -7036,6 +7048,79 @@
         public static final String KEY_SMS_OVER_IMS_SUPPORTED_RATS_INT_ARRAY =
                 KEY_PREFIX + "sms_over_ims_supported_rats_int_array";
 
+        /**
+         * Maximum Retry Count for Failure, If the Retry Count exceeds this value,
+         * it must display to User Interface as sending failed
+         */
+        public static final String KEY_SMS_MAX_RETRY_COUNT_INT =
+                KEY_PREFIX + "sms_max_retry_count_int";
+
+        /**
+         * Maximum Retry Count for SMS over IMS on Failure, If the Retry Count exceeds this value,
+         * and if the retry count is less than KEY_SMS_MAX_RETRY_COUNT_INT
+         * sending SMS should fallback to CS
+         */
+        public static final String KEY_SMS_MAX_RETRY_COUNT_OVER_IMS_INT =
+                KEY_PREFIX + "sms_max_retry_count_over_ims_int";
+
+        /**
+         * Delay Timer Value in milliseconds
+         * Retry SMS over IMS after this Timer expires
+         */
+        public static final String KEY_SMS_OVER_IMS_SEND_RETRY_DELAY_MILLIS_INT =
+                KEY_PREFIX + "sms_rover_ims_send_retry_delay_millis_int";
+
+        /**
+         * TR1 Timer Value in milliseconds,
+         * Waits for RP-Ack from network for MO SMS.
+         */
+        public static final String KEY_SMS_TR1_TIMER_MILLIS_INT =
+                KEY_PREFIX + "sms_tr1_timer_millis_int";
+
+        /**
+         * TR2 Timer Value in milliseconds,
+         * Waits for RP-Ack from Transfer Layer for MT SMS.
+         */
+        public static final String KEY_SMS_TR2_TIMER_MILLIS_INT =
+                KEY_PREFIX + "sms_tr2_timer_millis_int";
+
+        /**
+         * SMS RP-Cause Values for which SMS should be retried over IMS
+         *
+         * <p>Possible values are,
+         * {@link SmsManager#SMS_RP_CAUSE_UNALLOCATED_NUMBER}
+         * {@link SmsManager#SMS_RP_CAUSE_OPERATOR_DETERMINED_BARRING}
+         * {@link SmsManager#SMS_RP_CAUSE_CALL_BARRING}
+         * {@link SmsManager#SMS_RP_CAUSE_RESERVED}
+         * {@link SmsManager#SMS_RP_CAUSE_SHORT_MESSAGE_TRANSFER_REJECTED}
+         * {@link SmsManager#SMS_RP_CAUSE_DESTINATION_OUT_OF_ORDER}
+         * {@link SmsManager#SMS_RP_CAUSE_UNIDENTIFIED_SUBSCRIBER}
+         * {@link SmsManager#SMS_RP_CAUSE_FACILITY_REJECTED}
+         * {@link SmsManager#SMS_RP_CAUSE_UNKNOWN_SUBSCRIBER}
+         * {@link SmsManager#SMS_RP_CAUSE_NETWORK_OUT_OF_ORDER}
+         * {@link SmsManager#SMS_RP_CAUSE_TEMPORARY_FAILURE}
+         * {@link SmsManager#SMS_RP_CAUSE_CONGESTION}
+         * {@link SmsManager#SMS_RP_CAUSE_RESOURCES_UNAVAILABLE}
+         * {@link SmsManager#SMS_RP_CAUSE_FACILITY_NOT_SUBSCRIBED}
+         * {@link SmsManager#SMS_RP_CAUSE_FACILITY_NOT_IMPLEMENTED}
+         * {@link SmsManager#SMS_RP_CAUSE_INVALID_MESSAGE_REFERENCE_VALUE}
+         * {@link SmsManager#SMS_RP_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE}
+         * {@link SmsManager#SMS_RP_CAUSE_INVALID_MANDATORY_INFORMATION}
+         * {@link SmsManager#SMS_RP_CAUSE_MESSAGE_TYPE_NON_EXISTENT}
+         * {@link SmsManager#SMS_RP_CAUSE_MESSAGE_INCOMPATIBLE_WITH_PROTOCOL_STATE}
+         * {@link SmsManager#SMS_RP_CAUSE_INFORMATION_ELEMENT_NON_EXISTENT}
+         * {@link SmsManager#SMS_RP_CAUSE_PROTOCOL_ERROR}
+         * {@link SmsManager#SMS_RP_CAUSE_INTERWORKING_UNSPECIFIED
+         */
+        public static final String KEY_SMS_RP_CAUSE_VALUES_TO_RETRY_OVER_IMS_INT_ARRAY =
+                KEY_PREFIX + "sms_rp_cause_values_to_retry_over_ims_int_array";
+
+        /**
+         * SMS RP-Cause Values for which Sending SMS should fallback
+         */
+        public static final String KEY_SMS_RP_CAUSE_VALUES_TO_FALLBACK_INT_ARRAY =
+                KEY_PREFIX + "sms_rp_cause_values_to_fallback_int_array";
+
         private static PersistableBundle getDefaults() {
             PersistableBundle defaults = new PersistableBundle();
             defaults.putBoolean(KEY_SMS_OVER_IMS_SUPPORTED_BOOL, true);
@@ -7043,6 +7128,45 @@
 
             defaults.putInt(KEY_SMS_OVER_IMS_FORMAT_INT, SMS_FORMAT_3GPP);
 
+            defaults.putInt(KEY_SMS_MAX_RETRY_COUNT_INT, 3);
+            defaults.putInt(KEY_SMS_MAX_RETRY_COUNT_OVER_IMS_INT, 3);
+            defaults.putInt(KEY_SMS_OVER_IMS_SEND_RETRY_DELAY_MILLIS_INT,
+                    2000);
+            defaults.putInt(KEY_SMS_TR1_TIMER_MILLIS_INT, 130000);
+            defaults.putInt(KEY_SMS_TR2_TIMER_MILLIS_INT, 15000);
+
+            defaults.putIntArray(
+                    KEY_SMS_RP_CAUSE_VALUES_TO_RETRY_OVER_IMS_INT_ARRAY,
+                    new int[] {
+                        SmsManager.SMS_RP_CAUSE_TEMPORARY_FAILURE
+                    });
+            defaults.putIntArray(
+                    KEY_SMS_RP_CAUSE_VALUES_TO_FALLBACK_INT_ARRAY,
+                    new int[] {
+                        SmsManager.SMS_RP_CAUSE_UNALLOCATED_NUMBER,
+                        SmsManager.SMS_RP_CAUSE_OPERATOR_DETERMINED_BARRING,
+                        SmsManager.SMS_RP_CAUSE_CALL_BARRING,
+                        SmsManager.SMS_RP_CAUSE_RESERVED,
+                        SmsManager.SMS_RP_CAUSE_SHORT_MESSAGE_TRANSFER_REJECTED,
+                        SmsManager.SMS_RP_CAUSE_DESTINATION_OUT_OF_ORDER,
+                        SmsManager.SMS_RP_CAUSE_UNIDENTIFIED_SUBSCRIBER,
+                        SmsManager.SMS_RP_CAUSE_FACILITY_REJECTED,
+                        SmsManager.SMS_RP_CAUSE_UNKNOWN_SUBSCRIBER,
+                        SmsManager.SMS_RP_CAUSE_NETWORK_OUT_OF_ORDER,
+                        SmsManager.SMS_RP_CAUSE_CONGESTION,
+                        SmsManager.SMS_RP_CAUSE_RESOURCES_UNAVAILABLE,
+                        SmsManager.SMS_RP_CAUSE_FACILITY_NOT_SUBSCRIBED,
+                        SmsManager.SMS_RP_CAUSE_FACILITY_NOT_IMPLEMENTED,
+                        SmsManager.SMS_RP_CAUSE_INVALID_MESSAGE_REFERENCE_VALUE,
+                        SmsManager.SMS_RP_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE,
+                        SmsManager.SMS_RP_CAUSE_INVALID_MANDATORY_INFORMATION,
+                        SmsManager.SMS_RP_CAUSE_MESSAGE_TYPE_NON_EXISTENT,
+                        SmsManager.SMS_RP_CAUSE_MESSAGE_INCOMPATIBLE_WITH_PROTOCOL_STATE,
+                        SmsManager.SMS_RP_CAUSE_INFORMATION_ELEMENT_NON_EXISTENT,
+                        SmsManager.SMS_RP_CAUSE_PROTOCOL_ERROR,
+                        SmsManager.SMS_RP_CAUSE_INTERWORKING_UNSPECIFIED
+                    });
+
             defaults.putIntArray(
                     KEY_SMS_OVER_IMS_SUPPORTED_RATS_INT_ARRAY,
                     new int[] {
@@ -9986,6 +10110,7 @@
         sDefaults.putStringArray(KEY_MMI_TWO_DIGIT_NUMBER_PATTERN_STRING_ARRAY, new String[0]);
         sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT,
                 CellSignalStrengthLte.USE_RSRP);
+        sDefaults.putInt(KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT, 300);
         // Default wifi configurations.
         sDefaults.putAll(Wifi.getDefaults());
         sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false);
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 8106819..c53b463 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -297,6 +297,106 @@
      */
     public static final int SMS_MESSAGE_PERIOD_NOT_SPECIFIED = -1;
 
+    // RP-Cause Values For MO SMS as per TS 124 011, table 8.4.
+
+    /** @hide */
+    @IntDef(prefix = { "SMS_RP_CAUSE" }, value = {
+        SmsManager.SMS_RP_CAUSE_UNALLOCATED_NUMBER,
+        SmsManager.SMS_RP_CAUSE_OPERATOR_DETERMINED_BARRING,
+        SmsManager.SMS_RP_CAUSE_CALL_BARRING,
+        SmsManager.SMS_RP_CAUSE_RESERVED,
+        SmsManager.SMS_RP_CAUSE_SHORT_MESSAGE_TRANSFER_REJECTED,
+        SmsManager.SMS_RP_CAUSE_DESTINATION_OUT_OF_ORDER,
+        SmsManager.SMS_RP_CAUSE_UNIDENTIFIED_SUBSCRIBER,
+        SmsManager.SMS_RP_CAUSE_FACILITY_REJECTED,
+        SmsManager.SMS_RP_CAUSE_UNKNOWN_SUBSCRIBER,
+        SmsManager.SMS_RP_CAUSE_NETWORK_OUT_OF_ORDER,
+        SmsManager.SMS_RP_CAUSE_TEMPORARY_FAILURE,
+        SmsManager.SMS_RP_CAUSE_CONGESTION,
+        SmsManager.SMS_RP_CAUSE_RESOURCES_UNAVAILABLE,
+        SmsManager.SMS_RP_CAUSE_FACILITY_NOT_SUBSCRIBED,
+        SmsManager.SMS_RP_CAUSE_FACILITY_NOT_IMPLEMENTED,
+        SmsManager.SMS_RP_CAUSE_INVALID_MESSAGE_REFERENCE_VALUE,
+        SmsManager.SMS_RP_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE,
+        SmsManager.SMS_RP_CAUSE_INVALID_MANDATORY_INFORMATION,
+        SmsManager.SMS_RP_CAUSE_MESSAGE_TYPE_NON_EXISTENT,
+        SmsManager.SMS_RP_CAUSE_MESSAGE_INCOMPATIBLE_WITH_PROTOCOL_STATE,
+        SmsManager.SMS_RP_CAUSE_INFORMATION_ELEMENT_NON_EXISTENT,
+        SmsManager.SMS_RP_CAUSE_PROTOCOL_ERROR,
+        SmsManager.SMS_RP_CAUSE_INTERWORKING_UNSPECIFIED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SMS_RP_CAUSE {}
+
+    /** Unallocated Number Cause */
+    public static final int SMS_RP_CAUSE_UNALLOCATED_NUMBER = 1;
+
+    /** RP-Cause for Operator Barring */
+    public static final int SMS_RP_CAUSE_OPERATOR_DETERMINED_BARRING = 8;
+
+    /** RP-Cause Value for Call Barring */
+    public static final int SMS_RP_CAUSE_CALL_BARRING = 10;
+
+    /** RP-Cause value for Reserved Number */
+    public static final int SMS_RP_CAUSE_RESERVED = 11;
+
+    /** RP-Cause Value for Message Transfer Rejected by Network */
+    public static final int SMS_RP_CAUSE_SHORT_MESSAGE_TRANSFER_REJECTED = 21;
+
+    /** RP-Cause Value for Destination is Out of Order */
+    public static final int SMS_RP_CAUSE_DESTINATION_OUT_OF_ORDER = 27;
+
+    /** RP-Cause Value when Subscriber is not Identified */
+    public static final int SMS_RP_CAUSE_UNIDENTIFIED_SUBSCRIBER = 28;
+
+    /** RP-Cause Value when SMS Facility if Rejected by Operator */
+    public static final int SMS_RP_CAUSE_FACILITY_REJECTED = 29;
+
+    /** RP-Cause Value when Subscriber is not Identified */
+    public static final int SMS_RP_CAUSE_UNKNOWN_SUBSCRIBER = 30;
+
+    /** RP-Cause Value when network is out of order*/
+    public static final int SMS_RP_CAUSE_NETWORK_OUT_OF_ORDER = 38;
+
+    /** RP-Cause Value For Temporary failure*/
+    public static final int SMS_RP_CAUSE_TEMPORARY_FAILURE = 41;
+
+    /** RP-Cause Value for SMS Failure due to Congestion in network*/
+    public static final int SMS_RP_CAUSE_CONGESTION = 42;
+
+    /** RP-Cause Value when Network Resources are unavailable */
+    public static final int SMS_RP_CAUSE_RESOURCES_UNAVAILABLE = 47;
+
+    /** RP-Cause Value when SMS Facilty is not subscribed by Reote device */
+    public static final int SMS_RP_CAUSE_FACILITY_NOT_SUBSCRIBED = 50;
+
+    /** RP-Cause Value when network does not provide the received service */
+    public static final int SMS_RP_CAUSE_FACILITY_NOT_IMPLEMENTED = 69;
+
+    /** RP-Cause Value when RP-MessageRefere */
+    public static final int SMS_RP_CAUSE_INVALID_MESSAGE_REFERENCE_VALUE = 81;
+
+    /** RP-Cause Value when network does not provide the received service */
+    public static final int SMS_RP_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE = 95;
+
+    /** RP-Cause Value when network does not provide the received service */
+    public static final int SMS_RP_CAUSE_INVALID_MANDATORY_INFORMATION = 96;
+
+    /** RP-Cause Value when network does not provide the received service */
+    public static final int SMS_RP_CAUSE_MESSAGE_TYPE_NON_EXISTENT = 97;
+
+    /** RP-Cause Value when network does not provide the received service */
+    public static final int SMS_RP_CAUSE_MESSAGE_INCOMPATIBLE_WITH_PROTOCOL_STATE = 98;
+
+    /** RP-Cause Value when network does not provide the received service */
+    public static final int SMS_RP_CAUSE_INFORMATION_ELEMENT_NON_EXISTENT  = 99;
+
+    /** RP-Cause Value when network does not provide the received service */
+    public static final int SMS_RP_CAUSE_PROTOCOL_ERROR = 111;
+
+    /** RP-Cause Value when network does not provide the received service */
+    public static final int SMS_RP_CAUSE_INTERWORKING_UNSPECIFIED = 127;
+
     /** @hide */
     @IntDef(prefix = { "PREMIUM_SMS_CONSENT" }, value = {
         SmsManager.PREMIUM_SMS_CONSENT_UNKNOWN,
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 8fc8c7d..b63fbe6 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -887,6 +887,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public void updateDeviceId(int deviceId) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public int getDeviceId() {
         throw new UnsupportedOperationException();
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index 948288a..566ec9a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -23,7 +23,6 @@
 import com.android.launcher3.tapl.LauncherInstrumentation
 import com.android.server.wm.flicker.junit.FlickerBuilderProvider
 import com.android.server.wm.traces.common.ComponentNameMatcher
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import org.junit.Assume
 import org.junit.AssumptionViolatedException
 import org.junit.Test
@@ -41,12 +40,6 @@
     protected val tapl: LauncherInstrumentation = LauncherInstrumentation()
 ) {
     init {
-        flicker.scenario.setIsTablet(
-            WindowManagerStateHelper(instrumentation, clearCacheAfterParsing = false)
-                .currentState
-                .wmState
-                .isTablet
-        )
         tapl.setExpectedRotationCheckEnabled(true)
     }
 
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index 28576bf..2ad5771 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -137,9 +137,17 @@
         void onScanResultReady();
 
         /**
+         * Deprecated in Android 14. Newer wificond implementation should call
+         * onScanRequestFailed().
          * Called when a scan has failed.
+         * @deprecated The usage is replaced by {@link ScanEventCallback#onScanFailed(int)}
          */
+
         void onScanFailed();
+        /**
+         * Called when a scan has failed with errorCode.
+         */
+        default void onScanFailed(int errorCode) {}
     }
 
     /**
@@ -230,6 +238,18 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
+
+        @Override
+        public void OnScanRequestFailed(int errorCode) {
+            Log.d(TAG, "Scan failed event with error code: " + errorCode);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mExecutor.execute(() -> mCallback.onScanFailed(
+                        toFrameworkScanStatusCode(errorCode)));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     }
 
     /**
@@ -1030,6 +1050,32 @@
     }
 
     /**
+     * @deprecated replaced by {@link #startScan2(String, int, Set, List, Bundle)}
+     */
+    @Deprecated
+    public boolean startScan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType,
+            @SuppressLint("NullableCollection") @Nullable Set<Integer> freqs,
+            @SuppressLint("NullableCollection") @Nullable List<byte[]> hiddenNetworkSSIDs,
+            @SuppressLint("NullableCollection") @Nullable Bundle extraScanningParams) {
+        IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
+        if (scannerImpl == null) {
+            Log.e(TAG, "No valid wificond scanner interface handler for iface=" + ifaceName);
+            return false;
+        }
+        SingleScanSettings settings = createSingleScanSettings(scanType, freqs, hiddenNetworkSSIDs,
+                extraScanningParams);
+        if (settings == null) {
+            return false;
+        }
+        try {
+            return scannerImpl.scan(settings);
+        } catch (RemoteException e1) {
+            Log.e(TAG, "Failed to request scan due to remote exception");
+        }
+        return false;
+    }
+
+    /**
      * Start a scan using the specified parameters. A scan is an asynchronous operation. The
      * result of the operation is returned in the {@link ScanEventCallback} registered when
      * setting up an interface using
@@ -1049,24 +1095,41 @@
      * @param hiddenNetworkSSIDs List of hidden networks to be scanned for, a null indicates that
      *                           no hidden frequencies will be scanned for.
      * @param extraScanningParams bundle of extra scanning parameters.
-     * @return Returns true on success, false on failure (e.g. when called before the interface
-     * has been set up).
+     * @return Returns one of the scan status codes defined in {@code WifiScanner#REASON_*}
      */
-    public boolean startScan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType,
+    public int startScan2(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType,
             @SuppressLint("NullableCollection") @Nullable Set<Integer> freqs,
             @SuppressLint("NullableCollection") @Nullable List<byte[]> hiddenNetworkSSIDs,
             @SuppressLint("NullableCollection") @Nullable Bundle extraScanningParams) {
         IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
         if (scannerImpl == null) {
             Log.e(TAG, "No valid wificond scanner interface handler for iface=" + ifaceName);
-            return false;
+            return WifiScanner.REASON_INVALID_ARGS;
         }
+        SingleScanSettings settings = createSingleScanSettings(scanType, freqs, hiddenNetworkSSIDs,
+                extraScanningParams);
+        if (settings == null) {
+            return WifiScanner.REASON_INVALID_ARGS;
+        }
+        try {
+            int status = scannerImpl.scanRequest(settings);
+            return toFrameworkScanStatusCode(status);
+        } catch (RemoteException e1) {
+            Log.e(TAG, "Failed to request scan due to remote exception");
+        }
+        return WifiScanner.REASON_UNSPECIFIED;
+    }
+
+    private SingleScanSettings createSingleScanSettings(@WifiAnnotations.ScanType int scanType,
+            @SuppressLint("NullableCollection") @Nullable Set<Integer> freqs,
+            @SuppressLint("NullableCollection") @Nullable List<byte[]> hiddenNetworkSSIDs,
+            @SuppressLint("NullableCollection") @Nullable Bundle extraScanningParams) {
         SingleScanSettings settings = new SingleScanSettings();
         try {
             settings.scanType = getScanType(scanType);
         } catch (IllegalArgumentException e) {
             Log.e(TAG, "Invalid scan type ", e);
-            return false;
+            return null;
         }
         settings.channelSettings  = new ArrayList<>();
         settings.hiddenNetworks  = new ArrayList<>();
@@ -1094,12 +1157,25 @@
             }
         }
 
-        try {
-            return scannerImpl.scan(settings);
-        } catch (RemoteException e1) {
-            Log.e(TAG, "Failed to request scan due to remote exception");
+        return settings;
+    }
+
+    private int toFrameworkScanStatusCode(int scanStatus) {
+        switch(scanStatus) {
+            case IWifiScannerImpl.SCAN_STATUS_SUCCESS:
+                return WifiScanner.REASON_SUCCEEDED;
+            case IWifiScannerImpl.SCAN_STATUS_FAILED_BUSY:
+                return WifiScanner.REASON_BUSY;
+            case IWifiScannerImpl.SCAN_STATUS_FAILED_ABORT:
+                return WifiScanner.REASON_ABORT;
+            case IWifiScannerImpl.SCAN_STATUS_FAILED_NODEV:
+                return WifiScanner.REASON_NO_DEV;
+            case IWifiScannerImpl.SCAN_STATUS_FAILED_INVALID_ARGS:
+                return WifiScanner.REASON_INVALID_ARGS;
+            case IWifiScannerImpl.SCAN_STATUS_FAILED_GENERIC:
+            default:
+                return WifiScanner.REASON_UNSPECIFIED;
         }
-        return false;
     }
 
     /**