Merge "Connect getStatus/getStatusExt between TunerFrontend and FrontendClient" into sc-dev
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 49433f1..86364af 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -82,12 +82,13 @@
         "android.hardware.vibrator-V1.3-java",
         "framework-protos",
         "stable.core.platform.api.stubs",
-        // There are a few classes from modules used as type arguments that
-        // need to be resolved by metalava. For now, we can use a previously
-        // finalized stub library to resolve them. If a new class gets added,
-        // this may be need to be revisited to use a manually maintained stub
-        // library with empty classes in order to resolve those references.
-        "sdk_system_30_android",
+        // There are a few classes from modules used by the core that
+        // need to be resolved by metalava. We use a prebuilt stub of the
+        // full sdk to ensure we can resolve them. If a new class gets added,
+        // the prebuilts/sdk/current needs to be updated.
+        "sdk_system_current_android",
+        // NOTE: The below can be removed once the prebuilt stub contains IKE.
+        "sdk_system_current_android.net.ipsec.ike",
     ],
     high_mem: true, // Lots of sources => high memory use, see b/170701554
     installable: false,
@@ -404,7 +405,11 @@
         "android_defaults_stubs_current",
         "android_stubs_dists_default",
     ],
-    libs: ["sdk_system_29_android"],
+    libs: [
+        "sdk_system_current_android",
+        // NOTE: The below can be removed once the prebuilt stub contains IKE.
+        "sdk_system_current_android.net.ipsec.ike",
+    ],
     static_libs: ["art.module.public.api.stubs"],
     dist: {
         dir: "apistubs/android/module-lib",
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index e8a2817..d249f2a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -16,7 +16,6 @@
 
 package com.android.server.job.controllers;
 
-import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 
@@ -332,7 +331,7 @@
         if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
             final long bandwidth = capabilities.getLinkDownstreamBandwidthKbps();
             // If we don't know the bandwidth, all we can do is hope the job finishes in time.
-            if (bandwidth != LINK_BANDWIDTH_UNSPECIFIED) {
+            if (bandwidth > 0) {
                 // Divide by 8 to convert bits to bytes.
                 final long estimatedMillis = ((downloadBytes * DateUtils.SECOND_IN_MILLIS)
                         / (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8));
@@ -350,7 +349,7 @@
         if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
             final long bandwidth = capabilities.getLinkUpstreamBandwidthKbps();
             // If we don't know the bandwidth, all we can do is hope the job finishes in time.
-            if (bandwidth != LINK_BANDWIDTH_UNSPECIFIED) {
+            if (bandwidth > 0) {
                 // Divide by 8 to convert bits to bytes.
                 final long estimatedMillis = ((uploadBytes * DateUtils.SECOND_IN_MILLIS)
                         / (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8));
@@ -380,18 +379,16 @@
 
     private static boolean isStrictSatisfied(JobStatus jobStatus, Network network,
             NetworkCapabilities capabilities, Constants constants) {
-        final NetworkCapabilities required;
         // A restricted job that's out of quota MUST use an unmetered network.
         if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX
                 && !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
-            required = new NetworkCapabilities(
+            final NetworkCapabilities required = new NetworkCapabilities.Builder(
                     jobStatus.getJob().getRequiredNetwork().networkCapabilities)
-                    .addCapability(NET_CAPABILITY_NOT_METERED);
+                    .addCapability(NET_CAPABILITY_NOT_METERED).build();
+            return required.satisfiedByNetworkCapabilities(capabilities);
         } else {
-            required = jobStatus.getJob().getRequiredNetwork().networkCapabilities;
+            return jobStatus.getJob().getRequiredNetwork().canBeSatisfiedBy(capabilities);
         }
-
-        return required.satisfiedByNetworkCapabilities(capabilities);
     }
 
     private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
@@ -402,9 +399,9 @@
         }
 
         // See if we match after relaxing any unmetered request
-        final NetworkCapabilities relaxed = new NetworkCapabilities(
+        final NetworkCapabilities relaxed = new NetworkCapabilities.Builder(
                 jobStatus.getJob().getRequiredNetwork().networkCapabilities)
-                        .removeCapability(NET_CAPABILITY_NOT_METERED);
+                        .removeCapability(NET_CAPABILITY_NOT_METERED).build();
         if (relaxed.satisfiedByNetworkCapabilities(capabilities)) {
             // TODO: treat this as "maybe" response; need to check quotas
             return jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC;
diff --git a/core/api/current.txt b/core/api/current.txt
index a945889..5679a3d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12298,7 +12298,7 @@
     method @Deprecated public abstract void removePackageFromPreferred(@NonNull String);
     method public abstract void removePermission(@NonNull String);
     method @RequiresPermission(value="android.permission.WHITELIST_RESTRICTED_PERMISSIONS", conditional=true) public boolean removeWhitelistedRestrictedPermission(@NonNull String, @NonNull String, int);
-    method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
+    method public void requestChecksums(@NonNull String, boolean, int, @NonNull java.util.List<java.security.cert.Certificate>, @NonNull android.content.pm.PackageManager.OnChecksumsReadyListener) throws java.security.cert.CertificateEncodingException, android.content.pm.PackageManager.NameNotFoundException;
     method @Nullable public abstract android.content.pm.ResolveInfo resolveActivity(@NonNull android.content.Intent, int);
     method @Nullable public abstract android.content.pm.ProviderInfo resolveContentProvider(@NonNull String, int);
     method @Nullable public abstract android.content.pm.ResolveInfo resolveService(@NonNull android.content.Intent, int);
@@ -12318,7 +12318,6 @@
     field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
     field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
     field public static final int DONT_KILL_APP = 1; // 0x1
-    field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
     field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
     field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
     field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays";
@@ -12504,6 +12503,10 @@
     ctor public PackageManager.NameNotFoundException(String);
   }
 
+  @java.lang.FunctionalInterface public static interface PackageManager.OnChecksumsReadyListener {
+    method public void onChecksumsReady(@NonNull java.util.List<android.content.pm.ApkChecksum>);
+  }
+
   public static final class PackageManager.Property implements android.os.Parcelable {
     method public int describeContents();
     method public boolean getBoolean();
@@ -15025,6 +15028,7 @@
     field public static final int RGB_565 = 4; // 0x4
     field public static final int UNKNOWN = 0; // 0x0
     field public static final int Y8 = 538982489; // 0x20203859
+    field public static final int YCBCR_P010 = 54; // 0x36
     field public static final int YUV_420_888 = 35; // 0x23
     field public static final int YUV_422_888 = 39; // 0x27
     field public static final int YUV_444_888 = 40; // 0x28
@@ -31444,6 +31448,7 @@
     method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
     method public boolean hasUserRestriction(String);
     method public boolean isDemoUser();
+    method public static boolean isHeadlessSystemUserMode();
     method public boolean isManagedProfile();
     method public boolean isQuietModeEnabled(android.os.UserHandle);
     method public boolean isSystemUser();
@@ -40390,6 +40395,7 @@
     field public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL = "ims.enable_presence_group_subscribe_bool";
     field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool";
     field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool";
+    field public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT = "ims.non_rcs_capabilities_cache_expiration_sec_int";
     field public static final String KEY_PREFIX = "ims.";
     field public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = "ims.rcs_bulk_capability_exchange_bool";
     field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 1977babe..053f6cb 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10331,7 +10331,7 @@
     ctor public ExternalStorageService();
     method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method public abstract void onEndSession(@NonNull String) throws java.io.IOException;
-    method public void onFreeCacheRequested(@NonNull java.util.UUID, long);
+    method public void onFreeCache(@NonNull java.util.UUID, long) throws java.io.IOException;
     method public abstract void onStartSession(@NonNull String, int, @NonNull android.os.ParcelFileDescriptor, @NonNull java.io.File, @NonNull java.io.File) throws java.io.IOException;
     method public abstract void onVolumeStateChanged(@NonNull android.os.storage.StorageVolume) throws java.io.IOException;
     field public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 2; // 0x2
@@ -11704,6 +11704,7 @@
     method public boolean canManageSubscription(@NonNull android.telephony.SubscriptionInfo, @NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getActiveSubscriptionIdList();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String);
+    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public byte[] getAllSimSpecificSettingsForBackup();
     method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getCompleteActiveSubscriptionIdList();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
@@ -11711,6 +11712,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int);
     method public void requestEmbeddedSubscriptionInfoListRefresh();
     method public void requestEmbeddedSubscriptionInfoListRefresh(int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[]);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultDataSubId(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultSmsSubId(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e88b6b9..79917d0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -28,6 +28,7 @@
     field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
     field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
+    field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
     field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
     field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
     field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
@@ -119,6 +120,7 @@
 
   public class ActivityOptions {
     method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
+    method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
     method public static void setExitTransitionTimeout(long);
     method public void setLaunchActivityType(int);
     method public void setLaunchTaskId(int);
@@ -390,11 +392,10 @@
     method public boolean isFactoryResetProtectionPolicySupported();
     method @NonNull public static String operationToString(int);
     method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
+    method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int);
     method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
     method @NonNull public static String unsafeOperationReasonToString(int);
     field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
-    field public static final String ACTION_MANAGED_PROFILE_CREATED = "android.app.action.MANAGED_PROFILE_CREATED";
-    field public static final String ACTION_PROVISIONED_MANAGED_DEVICE = "android.app.action.PROVISIONED_MANAGED_DEVICE";
     field public static final int CODE_ACCOUNTS_NOT_EMPTY = 6; // 0x6
     field public static final int CODE_CANNOT_ADD_MANAGED_PROFILE = 11; // 0xb
     field public static final int CODE_DEVICE_ADMIN_NOT_SUPPORTED = 13; // 0xd
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 2b5e18d..28da1c3 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.INVALID_DISPLAY;
@@ -310,6 +311,9 @@
     private static final String KEY_REMOTE_TRANSITION =
             "android:activity.remoteTransition";
 
+    private static final String KEY_OVERRIDE_TASK_TRANSITION =
+            "android:activity.overrideTaskTransition";
+
     /**
      * @see #setLaunchCookie
      * @hide
@@ -393,6 +397,7 @@
     private RemoteAnimationAdapter mRemoteAnimationAdapter;
     private IBinder mLaunchCookie;
     private IRemoteTransition mRemoteTransition;
+    private boolean mOverrideTaskTransition;
 
     /**
      * Create an ActivityOptions specifying a custom animation to run when
@@ -476,6 +481,40 @@
     }
 
     /**
+     * Create an ActivityOptions specifying a custom animation to run when the activity in the
+     * different task is displayed.
+     *
+     * @param context Who is defining this.  This is the application that the
+     * animation resources will be loaded from.
+     * @param enterResId A resource ID of the animation resource to use for
+     * the incoming activity.  Use 0 for no animation.
+     * @param exitResId A resource ID of the animation resource to use for
+     * the outgoing activity.  Use 0 for no animation.
+     * @param handler If <var>listener</var> is non-null this must be a valid
+     * Handler on which to dispatch the callback; otherwise it should be null.
+     * @param startedListener Optional OnAnimationStartedListener to find out when the
+     * requested animation has started running.  If for some reason the animation
+     * is not executed, the callback will happen immediately.
+     * @param finishedListener Optional OnAnimationFinishedListener when the animation
+     * has finished running.
+     *
+     * @return Returns a new ActivityOptions object that you can use to
+     * supply these options as the options Bundle when starting an activity.
+     * @hide
+     */
+    @RequiresPermission(START_TASKS_FROM_RECENTS)
+    @TestApi
+    public static @NonNull ActivityOptions makeCustomTaskAnimation(@NonNull Context context,
+            int enterResId, int exitResId, @Nullable Handler handler,
+            @Nullable OnAnimationStartedListener startedListener,
+            @Nullable OnAnimationFinishedListener finishedListener) {
+        ActivityOptions opts = makeCustomAnimation(context, enterResId, exitResId, handler,
+                startedListener, finishedListener);
+        opts.mOverrideTaskTransition = true;
+        return opts;
+    }
+
+    /**
      * Creates an ActivityOptions specifying a custom animation to run in place on an existing
      * activity.
      *
@@ -1107,6 +1146,7 @@
         mLaunchCookie = opts.getBinder(KEY_LAUNCH_COOKIE);
         mRemoteTransition = IRemoteTransition.Stub.asInterface(opts.getBinder(
                 KEY_REMOTE_TRANSITION));
+        mOverrideTaskTransition = opts.getBoolean(KEY_OVERRIDE_TASK_TRANSITION);
     }
 
     /**
@@ -1561,6 +1601,12 @@
         return mLaunchCookie;
     }
 
+
+    /** @hide */
+    public boolean getOverrideTaskTransition() {
+        return mOverrideTaskTransition;
+    }
+
     /**
      * Update the current values in this ActivityOptions from those supplied
      * in <var>otherOptions</var>.  Any values
@@ -1789,6 +1835,9 @@
         if (mRemoteTransition != null) {
             b.putBinder(KEY_REMOTE_TRANSITION, mRemoteTransition.asBinder());
         }
+        if (mOverrideTaskTransition) {
+            b.putBoolean(KEY_OVERRIDE_TASK_TRANSITION, mOverrideTaskTransition);
+        }
         return b;
     }
 
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index b51d4ac..8ac9139 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -39,11 +39,13 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApkChecksum;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ChangedPackages;
 import android.content.pm.Checksum;
 import android.content.pm.ComponentInfo;
 import android.content.pm.FeatureInfo;
+import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageManager;
@@ -880,10 +882,10 @@
     @Override
     public void requestChecksums(@NonNull String packageName, boolean includeSplits,
             @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
-            @NonNull IntentSender statusReceiver)
+            @NonNull OnChecksumsReadyListener onChecksumsReadyListener)
             throws CertificateEncodingException, NameNotFoundException {
         Objects.requireNonNull(packageName);
-        Objects.requireNonNull(statusReceiver);
+        Objects.requireNonNull(onChecksumsReadyListener);
         Objects.requireNonNull(trustedInstallers);
         try {
             if (trustedInstallers == TRUST_ALL) {
@@ -895,8 +897,17 @@
                         "trustedInstallers has to be one of TRUST_ALL/TRUST_NONE or a non-empty "
                                 + "list of certificates.");
             }
+            IOnChecksumsReadyListener onChecksumsReadyListenerDelegate =
+                    new IOnChecksumsReadyListener.Stub() {
+                        @Override
+                        public void onChecksumsReady(List<ApkChecksum> checksums)
+                                throws RemoteException {
+                            onChecksumsReadyListener.onChecksumsReady(checksums);
+                        }
+                    };
             mPM.requestChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
-                    encodeCertificates(trustedInstallers), statusReceiver, getUserId());
+                    encodeCertificates(trustedInstallers), onChecksumsReadyListenerDelegate,
+                    getUserId());
         } catch (ParcelableException e) {
             e.maybeRethrow(PackageManager.NameNotFoundException.class);
             throw new RuntimeException(e);
diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java
index 14ed414..cbe2995 100644
--- a/core/java/android/app/WindowContext.java
+++ b/core/java/android/app/WindowContext.java
@@ -15,8 +15,7 @@
  */
 package android.app;
 
-import static android.view.WindowManagerGlobal.ADD_OKAY;
-import static android.view.WindowManagerGlobal.ADD_TOO_MANY_TOKENS;
+import static android.view.WindowManagerImpl.createWindowContextWindowManager;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -28,8 +27,8 @@
 import android.os.RemoteException;
 import android.view.Display;
 import android.view.IWindowManager;
+import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
-import android.view.WindowManagerImpl;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -46,10 +45,10 @@
  */
 @UiContext
 public class WindowContext extends ContextWrapper {
-    private final WindowManagerImpl mWindowManager;
+    private final WindowManager mWindowManager;
     private final IWindowManager mWms;
     private final WindowTokenClient mToken;
-    private boolean mOwnsToken;
+    private boolean mListenerRegistered;
 
     /**
      * Default constructor. Will generate a {@link WindowTokenClient} and attach this context to
@@ -86,25 +85,14 @@
 
         mToken.attachContext(this);
 
-        mWindowManager = new WindowManagerImpl(this);
-        mWindowManager.setDefaultToken(mToken);
+        mWindowManager = createWindowContextWindowManager(this);
 
-        int result;
         try {
-            // Register the token with WindowManager. This will also call back with the current
-            // config back to the client.
-            result = mWms.addWindowTokenWithOptions(
-                    mToken, type, getDisplayId(), options, getPackageName());
+            mListenerRegistered = mWms.registerWindowContextListener(mToken, type, getDisplayId(),
+                    options);
         }  catch (RemoteException e) {
-            mOwnsToken = false;
             throw e.rethrowFromSystemServer();
         }
-        if (result == ADD_TOO_MANY_TOKENS) {
-            throw new UnsupportedOperationException("createWindowContext failed! Too many unused "
-                    + "window contexts. Please see Context#createWindowContext documentation for "
-                    + "detail.");
-        }
-        mOwnsToken = result == ADD_OKAY;
         Reference.reachabilityFence(this);
     }
 
@@ -131,10 +119,10 @@
     /** Used for test to invoke because we can't invoke finalize directly. */
     @VisibleForTesting
     public void release() {
-        if (mOwnsToken) {
+        if (mListenerRegistered) {
+            mListenerRegistered = false;
             try {
-                mWms.removeWindowToken(mToken, getDisplayId());
-                mOwnsToken = false;
+                mWms.unregisterWindowContextListener(mToken);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 642bce4..06fe9d7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1730,8 +1730,12 @@
      * Broadcast action to notify ManagedProvisioning that
      * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction has changed.
      * @hide
+     * @deprecated No longer needed as ManagedProvisioning no longer handles
+     * {@link UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE} restriction changing.
      */
+    // TODO(b/177221010): Remove when Managed Provisioning no longer depend on it.
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
     public static final String ACTION_DATA_SHARING_RESTRICTION_CHANGED =
             "android.app.action.DATA_SHARING_RESTRICTION_CHANGED";
 
@@ -5469,26 +5473,6 @@
             "android.app.action.ACTION_SHOW_NEW_USER_DISCLAIMER";
 
     /**
-     * Broadcast action: notify managed provisioning that the device has been provisioned.
-     *
-     * @hide
-     */
-    @TestApi
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_PROVISIONED_MANAGED_DEVICE =
-            "android.app.action.PROVISIONED_MANAGED_DEVICE";
-
-    /**
-     * Broadcast action: notify managed provisioning that a new managed profile is created.
-     *
-     * @hide
-     */
-    @TestApi
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MANAGED_PROFILE_CREATED =
-            "android.app.action.MANAGED_PROFILE_CREATED";
-
-    /**
      * Widgets are enabled in keyguard
      */
     public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
@@ -13268,4 +13252,23 @@
             }
         }
     }
+
+    /**
+     * Resets the default cross profile intent filters that were set during
+     * {@link #createAndProvisionManagedProfile} between {@code userId} and all it's managed
+     * profiles if any.
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+    public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+        if (mService != null) {
+            try {
+                mService.resetDefaultCrossProfileIntentFilters(userId);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index f9ee153..cf0b31e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -498,4 +498,6 @@
 
     UserHandle createAndProvisionManagedProfile(in ManagedProfileProvisioningParams provisioningParams, in String callerPackage);
     void provisionFullyManagedDevice(in FullyManagedDeviceProvisioningParams provisioningParams, in String callerPackage);
+
+    void resetDefaultCrossProfileIntentFilters(int userId);
 }
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 587e883..742d05c 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -41,6 +41,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -460,24 +461,40 @@
                                                    Set<PathWithRequiredFlags> excludes,
                                                    Map<String, Set<PathWithRequiredFlags>> includes)
                 throws IOException, XmlPullParserException {
+            verifyTopLevelTag(parser, "full-backup-content");
+
+            parseRules(parser, excludes, includes, Optional.empty());
+
+            logParsingResults(excludes, includes);
+        }
+
+        private void verifyTopLevelTag(XmlPullParser parser, String tag)
+                throws XmlPullParserException, IOException {
             int event = parser.getEventType(); // START_DOCUMENT
             while (event != XmlPullParser.START_TAG) {
                 event = parser.next();
             }
 
-            if (!"full-backup-content".equals(parser.getName())) {
+            if (!tag.equals(parser.getName())) {
                 throw new XmlPullParserException("Xml file didn't start with correct tag" +
-                        " (<full-backup-content>). Found \"" + parser.getName() + "\"");
+                        " (" + tag + " ). Found \"" + parser.getName() + "\"");
             }
 
             if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
                 Log.v(TAG_XML_PARSER, "\n");
                 Log.v(TAG_XML_PARSER, "====================================================");
-                Log.v(TAG_XML_PARSER, "Found valid fullBackupContent; parsing xml resource.");
+                Log.v(TAG_XML_PARSER, "Found valid " + tag + "; parsing xml resource.");
                 Log.v(TAG_XML_PARSER, "====================================================");
                 Log.v(TAG_XML_PARSER, "");
             }
+        }
 
+        private void parseRules(XmlPullParser parser,
+                Set<PathWithRequiredFlags> excludes,
+                Map<String, Set<PathWithRequiredFlags>> includes,
+                Optional<Integer> maybeRequiredFlags)
+                throws IOException, XmlPullParserException {
+            int event;
             while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
                 switch (event) {
                     case XmlPullParser.START_TAG:
@@ -498,13 +515,7 @@
                             break;
                         }
 
-                        int requiredFlags = 0; // no transport flags are required by default
-                        if (TAG_INCLUDE.equals(parser.getName())) {
-                            // requiredFlags are only supported for <include /> tag, for <exclude />
-                            // we should always leave them as the default = 0
-                            requiredFlags = getRequiredFlagsFromString(
-                                    parser.getAttributeValue(null, "requireFlags"));
-                        }
+                        int requiredFlags = getRequiredFlagsForRule(parser, maybeRequiredFlags);
 
                         // retrieve the include/exclude set we'll be adding this rule to
                         Set<PathWithRequiredFlags> activeSet = parseCurrentTagForDomain(
@@ -542,7 +553,7 @@
 
                         // Special case for sharedpref files (not dirs) also add ".xml" suffix file.
                         if ("sharedpref".equals(domainFromXml) && !canonicalFile.isDirectory() &&
-                            !canonicalFile.getCanonicalPath().endsWith(".xml")) {
+                                !canonicalFile.getCanonicalPath().endsWith(".xml")) {
                             final String canonicalXmlPath =
                                     canonicalFile.getCanonicalPath() + ".xml";
                             activeSet.add(new PathWithRequiredFlags(canonicalXmlPath,
@@ -554,6 +565,10 @@
                         }
                 }
             }
+        }
+
+        private void logParsingResults(Set<PathWithRequiredFlags> excludes,
+                Map<String, Set<PathWithRequiredFlags>> includes) {
             if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
                 Log.v(TAG_XML_PARSER, "\n");
                 Log.v(TAG_XML_PARSER, "Xml resource parsing complete.");
@@ -613,6 +628,24 @@
             return flags;
         }
 
+        private int getRequiredFlagsForRule(XmlPullParser parser,
+                Optional<Integer> maybeRequiredFlags) {
+            if (maybeRequiredFlags.isPresent()) {
+                // This is the new config format where required flags are specified for the whole
+                // section, not per rule.
+                return maybeRequiredFlags.get();
+            }
+
+            if (TAG_INCLUDE.equals(parser.getName())) {
+                // In the legacy config, requiredFlags are only supported for <include /> tag,
+                // for <exclude /> we should always leave them as the default = 0.
+                return getRequiredFlagsFromString(
+                        parser.getAttributeValue(null, "requireFlags"));
+            }
+
+            return 0;
+        }
+
         private Set<PathWithRequiredFlags> parseCurrentTagForDomain(XmlPullParser parser,
                 Set<PathWithRequiredFlags> excludes,
                 Map<String, Set<PathWithRequiredFlags>> includes, String domain)
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 987de3f..5d28216 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -75,6 +75,7 @@
 import android.view.DisplayAdjustments;
 import android.view.View;
 import android.view.ViewDebug;
+import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams.WindowType;
 import android.view.autofill.AutofillManager.AutofillClient;
@@ -6110,18 +6111,19 @@
      *
      * // WindowManager.LayoutParams initialization
      * ...
+     * // The types used in addView and createWindowContext must match.
      * mParams.type = TYPE_APPLICATION_OVERLAY;
      * ...
      *
-     * mWindowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
+     * windowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
      * </pre>
      *
      * <p>
-     * This context's configuration and resources are adjusted to a display area where the windows
-     * with provided type will be added. <b>Note that all windows associated with the same context
-     * will have an affinity and can only be moved together between different displays or areas on a
-     * display.</b> If there is a need to add different window types, or non-associated windows,
-     * separate Contexts should be used.
+     * This context's configuration and resources are adjusted to an area of the display where
+     * the windows with provided type will be added. <b>Note that all windows associated with the
+     * same context will have an affinity and can only be moved together between different displays
+     * or areas on a display.</b> If there is a need to add different window types, or
+     * non-associated windows, separate Contexts should be used.
      * </p>
      * <p>
      * Creating a window context is an expensive operation. Misuse of this API may lead to a huge
@@ -6129,7 +6131,43 @@
      * An approach is to create one window context with specific window type and display and
      * use it everywhere it's needed.
      * </p>
+     * <p>
+     * After {@link Build.VERSION_CODES#S}, window context provides the capability to receive
+     * configuration changes for existing token by overriding the
+     * {@link android.view.WindowManager.LayoutParams#token token} of the
+     * {@link android.view.WindowManager.LayoutParams} passed in
+     * {@link WindowManager#addView(View, LayoutParams)}. This is useful when an application needs
+     * to attach its window to an existing activity for window token sharing use-case.
+     * </p>
+     * <p>
+     * Note that the window context in {@link Build.VERSION_CODES#R} didn't have this
+     * capability. This is a no-op for the window context in {@link Build.VERSION_CODES#R}.
+     * </p>
+     * Below is sample code to <b>attach an existing token to a window context:</b>
+     * <pre class="prettyprint">
+     * final DisplayManager dm = anyContext.getSystemService(DisplayManager.class);
+     * final Display primaryDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+     * final Context windowContext = anyContext.createWindowContext(primaryDisplay,
+     *         TYPE_APPLICATION, null);
      *
+     * // Get an existing token.
+     * final IBinder existingToken = activity.getWindow().getAttributes().token;
+     *
+     * // The types used in addView() and createWindowContext() must match.
+     * final WindowManager.LayoutParams params = new WindowManager.LayoutParams(TYPE_APPLICATION);
+     * params.token = existingToken;
+     *
+     * // After WindowManager#addView(), the server side will extract the provided token from
+     * // LayoutParams#token (existingToken in the sample code), and switch to propagate
+     * // configuration changes from the node associated with the provided token.
+     * windowContext.getSystemService(WindowManager.class).addView(overlayView, mParams);
+     * </pre>
+     * <p>
+     * Note that using {@link android.app.Application} or {@link android.app.Service} context for
+     * UI-related queries may result in layout or continuity issues on devices with variable screen
+     * sizes (e.g. foldables) or in multi-window modes, since these non-UI contexts may not reflect
+     * the {@link Configuration} changes for the visual container.
+     * </p>
      * @param type Window type in {@link WindowManager.LayoutParams}
      * @param options A bundle used to pass window-related options
      * @return A {@link Context} that can be used to create
@@ -6141,9 +6179,7 @@
      * @see #LAYOUT_INFLATER_SERVICE
      * @see #WALLPAPER_SERVICE
      * @throws UnsupportedOperationException if this {@link Context} does not attach to a display,
-     * such as {@link android.app.Application Application} or {@link android.app.Service Service},
-     * or the current number of window contexts without adding any view by
-     * {@link WindowManager#addView} <b>exceeds five</b>.
+     * such as {@link android.app.Application Application} or {@link android.app.Service Service}.
      */
     @UiContext
     @NonNull
diff --git a/core/java/android/content/pm/IOnChecksumsReadyListener.aidl b/core/java/android/content/pm/IOnChecksumsReadyListener.aidl
new file mode 100644
index 0000000..7963ce1
--- /dev/null
+++ b/core/java/android/content/pm/IOnChecksumsReadyListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.content.pm.ApkChecksum;
+
+/**
+ * Listener that gets notified when checksums are available.
+ * {@hide}
+ */
+oneway interface IOnChecksumsReadyListener {
+    void onChecksumsReady(in List<ApkChecksum> checksums);
+}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index b8829bb..a46876e 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -27,6 +27,7 @@
 import android.content.pm.FeatureInfo;
 import android.content.pm.IDexModuleRegisterCallback;
 import android.content.pm.InstallSourceInfo;
+import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageDeleteObserver2;
@@ -754,7 +755,7 @@
 
     void notifyPackagesReplacedReceived(in String[] packages);
 
-    void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId);
+    void requestChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IOnChecksumsReadyListener onChecksumsReadyListener, int userId);
 
     //------------------------------------------------------------------------
     //
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d09d83f..b95b991b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3858,13 +3858,6 @@
     public static final String EXTRA_FAILURE_EXISTING_PERMISSION
             = "android.content.pm.extra.FAILURE_EXISTING_PERMISSION";
 
-    /**
-     * Extra field name for the ID of a package pending verification. Passed to
-     * a package verifier and is used to call back to
-     * @see #requestChecksums
-     */
-    public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
-
    /**
     * Permission flag: The permission is set in its current state
     * by the user and apps can still request it at runtime.
@@ -8709,9 +8702,20 @@
      */
     public static final @NonNull List<Certificate> TRUST_NONE = Collections.singletonList(null);
 
+    /** Listener that gets notified when checksums are available. */
+    @FunctionalInterface
+    public interface OnChecksumsReadyListener {
+        /**
+         * Called when the checksums are available.
+         *
+         * @param checksums array of checksums.
+         */
+        void onChecksumsReady(@NonNull List<ApkChecksum> checksums);
+    }
+
     /**
      * Requesting the checksums for APKs within a package.
-     * The checksums will be returned asynchronously via statusReceiver.
+     * The checksums will be returned asynchronously via onChecksumsReadyListener.
      *
      * By default returns all readily available checksums:
      * - enforced by platform,
@@ -8730,15 +8734,14 @@
      *                          {@link #TRUST_ALL} will return checksums from any installer,
      *                          {@link #TRUST_NONE} disables optimized installer-enforced checksums,
      *                          otherwise the list has to be non-empty list of certificates.
-     * @param statusReceiver called once when the results are available as
-     *                       {@link #EXTRA_CHECKSUMS} of type {@link ApkChecksum}[].
+     * @param onChecksumsReadyListener called once when the results are available.
      * @throws CertificateEncodingException if an encoding error occurs for trustedInstallers.
      * @throws IllegalArgumentException if the list of trusted installer certificates is empty.
      * @throws NameNotFoundException if a package with the given name cannot be found on the system.
      */
     public void requestChecksums(@NonNull String packageName, boolean includeSplits,
             @Checksum.Type int required, @NonNull List<Certificate> trustedInstallers,
-            @NonNull IntentSender statusReceiver)
+            @NonNull OnChecksumsReadyListener onChecksumsReadyListener)
             throws CertificateEncodingException, NameNotFoundException {
         throw new UnsupportedOperationException("requestChecksums not implemented in subclass");
     }
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 84a2acc..b016ed6 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -82,4 +82,5 @@
 
     boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork);
     boolean isUidRestrictedOnMeteredNetworks(int uid);
+    boolean checkUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered, boolean isBackgroundRestricted);
 }
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index ed169e7..3e6237d 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -464,6 +464,31 @@
     }
 
     /**
+     * Figure out if networking is blocked for a given set of conditions.
+     *
+     * This is used by ConnectivityService via passing stale copies of conditions, so it must not
+     * take any locks.
+     *
+     * @param uid The target uid.
+     * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
+     * @param isNetworkMetered True if the network is metered.
+     * @param isBackgroundRestricted True if data saver is enabled.
+     *
+     * @return true if networking is blocked for the UID under the specified conditions.
+     *
+     * @hide
+     */
+    public boolean checkUidNetworkingBlocked(int uid, int uidRules,
+            boolean isNetworkMetered, boolean isBackgroundRestricted) {
+        try {
+            return mService.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
+                    isBackgroundRestricted);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Check that the given uid is restricted from doing networking on metered networks.
      *
      * @param uid The target uid.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 77183ac..ea1ce37 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1694,10 +1694,13 @@
     }
 
     /**
-     * @hide
-     * @return Whether the device is running in a headless system user mode. It means the headless
-     * user (system user) runs system services and system UI, but is not associated with any real
-     * person. Secondary users can be created to be associated with real person.
+     * Checks whether the device is running in a headless system user mode.
+     *
+     * <p>Headless system user mode means the {@link #isSystemUser() system user} runs system
+     * services and some system UI, but it is not associated with any real person and additional
+     * users must be created to be associated with real persons.
+     *
+     * @return whether the device is running in a headless system user mode.
      */
     public static boolean isHeadlessSystemUserMode() {
         return RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER;
diff --git a/core/java/android/service/storage/ExternalStorageService.java b/core/java/android/service/storage/ExternalStorageService.java
index 0123c36..a750b68 100644
--- a/core/java/android/service/storage/ExternalStorageService.java
+++ b/core/java/android/service/storage/ExternalStorageService.java
@@ -158,7 +158,7 @@
      * @param volumeUuid uuid of the {@link StorageVolume} from which cache needs to be freed
      * @param bytes number of bytes which need to be freed
      */
-    public void onFreeCacheRequested(@NonNull UUID volumeUuid, @BytesLong long bytes) {
+    public void onFreeCache(@NonNull UUID volumeUuid, @BytesLong long bytes) throws IOException {
         throw new UnsupportedOperationException("onFreeCacheRequested not implemented");
     }
 
@@ -202,7 +202,7 @@
                 RemoteCallback callback) {
             mHandler.post(() -> {
                 try {
-                    onFreeCacheRequested(StorageManager.convert(volumeUuid), bytes);
+                    onFreeCache(StorageManager.convert(volumeUuid), bytes);
                     sendResult(sessionId, null /* throwable */, callback);
                 } catch (Throwable t) {
                     sendResult(sessionId, t, callback);
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 4168064..0ba1dfe 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -25,8 +25,8 @@
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.app.KeyguardManager;
-import android.app.WindowConfiguration;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -59,8 +59,12 @@
  * an application window, excluding the system decorations.  The application display area may
  * be smaller than the real display area because the system subtracts the space needed
  * for decor elements such as the status bar.  Use {@link WindowMetrics#getBounds()} to query the
- * application window bounds. Generally, use {@link WindowManager#getCurrentWindowMetrics()} to
- * query the metrics and perform UI-related actions.</li>
+ * application window bounds.</li>
+ * <li>The real display area specifies the part of the display that contains content
+ * including the system decorations.  Even so, the real display area may be smaller than the
+ * physical size of the display if the window manager is emulating a smaller display
+ * using (adb shell wm size).  Use the following methods to query the
+ * real display area: {@link #getRealSize}, {@link #getRealMetrics}.</li>
  * </ul>
  * </p><p>
  * A logical display does not necessarily represent a particular physical display device
@@ -673,9 +677,9 @@
     @UnsupportedAppUsage
     public DisplayAdjustments getDisplayAdjustments() {
         if (mResources != null) {
-            final DisplayAdjustments currentAdjustments = mResources.getDisplayAdjustments();
-            if (!mDisplayAdjustments.equals(currentAdjustments)) {
-                mDisplayAdjustments = new DisplayAdjustments(currentAdjustments);
+            final DisplayAdjustments currentAdjustements = mResources.getDisplayAdjustments();
+            if (!mDisplayAdjustments.equals(currentAdjustements)) {
+                mDisplayAdjustments = new DisplayAdjustments(currentAdjustements);
             }
         }
 
@@ -1213,34 +1217,30 @@
     }
 
     /**
-     * Provides the largest {@link Point outSize} an app may expect in the current system state,
-     * without subtracting any window decor.
+     * Gets the real size of the display without subtracting any window decor or
+     * applying any compatibility scale factors.
      * <p>
-     * The size describes the largest potential area the window might occupy. The size is adjusted
-     * based on the current rotation of the display.
+     * The size is adjusted based on the current rotation of the display.
      * </p><p>
      * The real size may be smaller than the physical size of the screen when the
      * window manager is emulating a smaller display (using adb shell wm size).
-     * </p>
+     * </p><p>
+     * In general, {@link #getRealSize(Point)} and {@link WindowManager#getMaximumWindowMetrics()}
+     * report the same bounds except that certain areas of the display may not be available to
+     * windows created in the {@link WindowManager}'s {@link Context}.
+     *
+     * For example, imagine a device which has a multi-task mode that limits windows to half of the
+     * screen. In this case, {@link WindowManager#getMaximumWindowMetrics()} reports the
+     * bounds of the screen half where the window is located, while {@link #getRealSize(Point)}
+     * still reports the bounds of the whole display.
      *
      * @param outSize Set to the real size of the display.
+     *
+     * @see WindowManager#getMaximumWindowMetrics()
      */
     public void getRealSize(Point outSize) {
         synchronized (this) {
             updateDisplayInfoLocked();
-            if (shouldReportMaxBounds()) {
-                final Rect bounds = mResources.getConfiguration()
-                        .windowConfiguration.getMaxBounds();
-                outSize.x = bounds.width();
-                outSize.y = bounds.height();
-                if (DEBUG) {
-                    Log.d(TAG, "getRealSize determined from max bounds: " + outSize
-                            + " for uid " + Process.myUid());
-                }
-                // Skip adjusting by fixed rotation, since if it is necessary, the configuration
-                // should already reflect the expected rotation.
-                return;
-            }
             outSize.x = mDisplayInfo.logicalWidth;
             outSize.y = mDisplayInfo.logicalHeight;
             if (mMayAdjustByFixedRotation) {
@@ -1250,11 +1250,9 @@
     }
 
     /**
-     * Provides the largest {@link DisplayMetrics outMetrics} an app may expect in the current
-     * system state, without subtracting any window decor.
+     * Gets display metrics based on the real size of this display.
      * <p>
-     * The size describes the largest potential area the window might occupy. The size is adjusted
-     * based on the current rotation of the display.
+     * The size is adjusted based on the current rotation of the display.
      * </p><p>
      * The real size may be smaller than the physical size of the screen when the
      * window manager is emulating a smaller display (using adb shell wm size).
@@ -1265,18 +1263,6 @@
     public void getRealMetrics(DisplayMetrics outMetrics) {
         synchronized (this) {
             updateDisplayInfoLocked();
-            if (shouldReportMaxBounds()) {
-                mDisplayInfo.getMaxBoundsMetrics(outMetrics,
-                        CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO,
-                        mResources.getConfiguration());
-                if (DEBUG) {
-                    Log.d(TAG, "getRealMetrics determined from max bounds: " + outMetrics
-                            + " for uid " + Process.myUid());
-                }
-                // Skip adjusting by fixed rotation, since if it is necessary, the configuration
-                // should already reflect the expected rotation.
-                return;
-            }
             mDisplayInfo.getLogicalMetrics(outMetrics,
                     CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
             if (mMayAdjustByFixedRotation) {
@@ -1286,20 +1272,6 @@
     }
 
     /**
-     * Determines if {@link WindowConfiguration#getMaxBounds()} should be reported as the
-     * display dimensions. The max bounds field may be smaller than the logical dimensions
-     * when apps need to be sandboxed.
-     * @return {@code true} when max bounds should be applied.
-     */
-    private boolean shouldReportMaxBounds() {
-        if (mResources == null) {
-            return false;
-        }
-        final Configuration config = mResources.getConfiguration();
-        return config != null && !config.windowConfiguration.getMaxBounds().isEmpty();
-    }
-
-    /**
      * Gets the state of the display, such as whether it is on or off.
      *
      * @return The state of the display: one of {@link #STATE_OFF}, {@link #STATE_ON},
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 8a44504..2a00b5a 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -24,7 +24,6 @@
 import static android.view.DisplayInfoProto.NAME;
 
 import android.annotation.Nullable;
-import android.app.WindowConfiguration;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -616,29 +615,11 @@
         getMetricsWithSize(outMetrics, ci, configuration, appWidth, appHeight);
     }
 
-    /**
-     * Populates {@code outMetrics} with details of the logical display. Bounds are limited
-     * by the logical size of the display.
-     *
-     * @param outMetrics the {@link DisplayMetrics} to be populated
-     * @param compatInfo the {@link CompatibilityInfo} to be applied
-     * @param configuration the {@link Configuration}
-     */
     public void getLogicalMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
             Configuration configuration) {
         getMetricsWithSize(outMetrics, compatInfo, configuration, logicalWidth, logicalHeight);
     }
 
-    /**
-     * Similar to {@link #getLogicalMetrics}, but the limiting bounds are determined from
-     * {@link WindowConfiguration#getMaxBounds()}
-     */
-    public void getMaxBoundsMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
-            Configuration configuration) {
-        Rect bounds = configuration.windowConfiguration.getMaxBounds();
-        getMetricsWithSize(outMetrics, compatInfo, configuration, bounds.width(), bounds.height());
-    }
-
     public int getNaturalWidth() {
         return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ?
                 logicalWidth : logicalHeight;
diff --git a/core/java/android/view/IRemoteAnimationRunner.aidl b/core/java/android/view/IRemoteAnimationRunner.aidl
index 423e23d..1f64fb8 100644
--- a/core/java/android/view/IRemoteAnimationRunner.aidl
+++ b/core/java/android/view/IRemoteAnimationRunner.aidl
@@ -30,11 +30,15 @@
     /**
      * Called when the process needs to start the remote animation.
      *
+     * @param transition The old transition type. Must be one of WindowManager.TRANSIT_OLD_* values.
      * @param apps The list of apps to animate.
+     * @param wallpapers The list of wallpapers to animate.
+     * @param nonApps The list of non-app windows such as Bubbles to animate.
      * @param finishedCallback The callback to invoke when the animation is finished.
      */
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
-    void onAnimationStart(in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers,
+    void onAnimationStart(int transit, in RemoteAnimationTarget[] apps,
+            in RemoteAnimationTarget[] wallpapers, in RemoteAnimationTarget[] nonApps,
             in IRemoteAnimationFinishedCallback finishedCallback);
 
     /**
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 7843411..ae8afca 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -117,7 +117,7 @@
     // These can only be called when holding the MANAGE_APP_TOKENS permission.
     void setEventDispatching(boolean enabled);
 
-    /** @return {@code true} if this binder is a registered window token. */
+    /** Returns {@code true} if this binder is a registered window token. */
     boolean isWindowToken(in IBinder binder);
     /**
      * Adds window token for a given type.
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 0939336..6a34a15 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -111,6 +111,9 @@
         mControl = new InsetsAnimationControlImpl(controls, frame, state, listener,
                 types, mCallbacks, durationMs, interpolator, animationType, translator);
         InsetsAnimationThread.getHandler().post(() -> {
+            if (mControl.isCancelled()) {
+                return;
+            }
             Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW,
                     "InsetsAsyncAnimation: " + WindowInsets.Type.toString(types), types);
             listener.onReady(mControl, types);
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index fe6b6e4..219190f 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -604,13 +604,13 @@
                 return Type.CAPTION_BAR;
             case ITYPE_IME:
                 return Type.IME;
-            case ITYPE_TOP_GESTURES:
-            case ITYPE_BOTTOM_GESTURES:
             case ITYPE_TOP_MANDATORY_GESTURES:
             case ITYPE_BOTTOM_MANDATORY_GESTURES:
             case ITYPE_LEFT_MANDATORY_GESTURES:
             case ITYPE_RIGHT_MANDATORY_GESTURES:
                 return Type.MANDATORY_SYSTEM_GESTURES;
+            case ITYPE_TOP_GESTURES:
+            case ITYPE_BOTTOM_GESTURES:
             case ITYPE_LEFT_GESTURES:
             case ITYPE_RIGHT_GESTURES:
                 return Type.SYSTEM_GESTURES;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 18ef80c..036a703 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -315,7 +315,7 @@
      * In that case we receive a call back from {@link ActivityThread} and this flag is used to
      * preserve the initial value.
      *
-     * @see #performConfigurationChange(Configuration, Configuration, boolean, int)
+     * @see #performConfigurationChange(MergedConfiguration, boolean, int)
      */
     private boolean mForceNextConfigUpdate;
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 269ccbb..dc81bc9 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -690,10 +690,6 @@
     <!-- Made protected in S (was added in R) -->
     <protected-broadcast android:name="com.android.internal.intent.action.BUGREPORT_REQUESTED" />
 
-    <!-- Added in S -->
-    <protected-broadcast android:name="android.app.action.MANAGED_PROFILE_CREATED" />
-    <protected-broadcast android:name="android.app.action.PROVISIONED_MANAGED_DEVICE" />
-
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
@@ -2539,7 +2535,7 @@
     <permission android:name="android.permission.REAL_GET_TASKS"
         android:protectionLevel="signature|privileged" />
 
-    <!-- Allows an application to start a task from a ActivityManager#RecentTaskInfo.
+    <!-- @TestApi Allows an application to start a task from a ActivityManager#RecentTaskInfo.
          @hide -->
     <permission android:name="android.permission.START_TASKS_FROM_RECENTS"
         android:protectionLevel="signature|privileged|recents" />
diff --git a/core/res/res/color-car/car_borderless_button_text_color.xml b/core/res/res/color-car/car_borderless_button_text_color.xml
index 1cdd6cd..0a86e40 100644
--- a/core/res/res/color-car/car_borderless_button_text_color.xml
+++ b/core/res/res/color-car/car_borderless_button_text_color.xml
@@ -16,5 +16,6 @@
 <!-- Default text colors for car buttons when enabled/disabled. -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:color="@*android:color/car_grey_700" android:state_enabled="false"/>
+    <item android:color="@*android:color/car_grey_700" android:state_ux_restricted="true"/>
     <item android:color="?android:attr/colorButtonNormal"/>
 </selector>
diff --git a/core/res/res/color-car/car_switch_track.xml b/core/res/res/color-car/car_switch_track.xml
new file mode 100644
index 0000000..8ca67dd
--- /dev/null
+++ b/core/res/res/color-car/car_switch_track.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<!-- copy of switch_track_material, but with a ux restricted state -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+          android:color="?attr/colorForeground"
+          android:alpha="?attr/disabledAlpha" />
+    <item android:state_ux_restricted="true"
+          android:color="?attr/colorForeground"
+          android:alpha="?attr/disabledAlpha" />
+    <item android:state_checked="true"
+          android:color="?attr/colorControlActivated" />
+    <item android:color="?attr/colorForeground" />
+</selector>
diff --git a/core/res/res/drawable-car/car_button_background.xml b/core/res/res/drawable-car/car_button_background.xml
index e568aeb..13b0ec1 100644
--- a/core/res/res/drawable-car/car_button_background.xml
+++ b/core/res/res/drawable-car/car_button_background.xml
@@ -25,6 +25,22 @@
                     android:color="#0059B3"/>
         </shape>
     </item>
+    <item android:state_focused="true" android:state_pressed="true" android:state_ux_restricted="true">
+        <shape android:shape="rectangle">
+            <corners android:radius="@*android:dimen/car_button_radius"/>
+            <solid android:color="@*android:color/car_grey_300"/>
+            <stroke android:width="4dp"
+                    android:color="#0059B3"/>
+        </shape>
+    </item>
+    <item android:state_focused="true" android:state_ux_restricted="true">
+        <shape android:shape="rectangle">
+            <corners android:radius="@*android:dimen/car_button_radius"/>
+            <solid android:color="@*android:color/car_grey_300"/>
+            <stroke android:width="8dp"
+                    android:color="#0059B3"/>
+        </shape>
+    </item>
     <item android:state_focused="true" android:state_pressed="true">
         <shape android:shape="rectangle">
             <corners android:radius="@*android:dimen/car_button_radius"/>
@@ -47,6 +63,12 @@
             <solid android:color="@*android:color/car_grey_300"/>
         </shape>
     </item>
+    <item android:state_ux_restricted="true">
+        <shape android:shape="rectangle">
+            <corners android:radius="@*android:dimen/car_button_radius"/>
+            <solid android:color="@*android:color/car_grey_300"/>
+        </shape>
+    </item>
     <item>
         <ripple android:color="?android:attr/colorControlHighlight">
             <item>
diff --git a/core/res/res/drawable-car/car_switch_track.xml b/core/res/res/drawable-car/car_switch_track.xml
index cb0b9be..51e9f7e 100644
--- a/core/res/res/drawable-car/car_switch_track.xml
+++ b/core/res/res/drawable-car/car_switch_track.xml
@@ -41,7 +41,7 @@
       android:right="@dimen/car_switch_track_margin_size">
     <shape
         android:shape="rectangle"
-        android:tint="@color/switch_track_material">
+        android:tint="@color/car_switch_track">
       <corners android:radius="7dp" />
       <solid android:color="@color/white_disabled_material" />
       <size android:height="14dp" />
diff --git a/core/res/res/values/attrs_car.xml b/core/res/res/values/attrs_car.xml
new file mode 100644
index 0000000..6bfea97
--- /dev/null
+++ b/core/res/res/values/attrs_car.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Formatting note: terminate all comments with a period, to avoid breaking
+     the documentation output. To suppress comment lines from the documentation
+     output, insert an eat-comment element after the comment lines.
+-->
+
+<resources>
+     <attr name="state_ux_restricted" format="boolean"/>
+</resources>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index bb826de..f31233b 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -147,6 +147,9 @@
     <!-- WindowMetricsTest permissions -->
     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
 
+    <!-- WindowContextTest permissions -->
+    <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
+
     <!-- Allow use of PendingIntent.getIntent() -->
     <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
 
diff --git a/core/tests/coretests/src/android/app/WindowContextTest.java b/core/tests/coretests/src/android/app/WindowContextTest.java
index dcf5e02..da7304e 100644
--- a/core/tests/coretests/src/android/app/WindowContextTest.java
+++ b/core/tests/coretests/src/android/app/WindowContextTest.java
@@ -18,29 +18,38 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
+import android.content.Intent;
 import android.hardware.display.DisplayManager;
+import android.os.Binder;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 import android.view.Display;
 import android.view.IWindowManager;
 import android.view.View;
 import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.WindowType;
 import android.view.WindowManagerGlobal;
 import android.view.WindowManagerImpl;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Tests for {@link WindowContext}
  *
@@ -54,20 +63,14 @@
 @SmallTest
 @Presubmit
 public class WindowContextTest {
+    @Rule
+    public ActivityTestRule<EmptyActivity> mActivityRule =
+            new ActivityTestRule<>(EmptyActivity.class, false /* initialTouchMode */,
+                    false /* launchActivity */);
+
     private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
     private final WindowContext mWindowContext = createWindowContext();
-
-    @Test
-    public void testWindowContextRelease_doRemoveWindowToken() throws Throwable {
-        final IBinder token = mWindowContext.getWindowContextToken();
-        final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
-
-        assertTrue("Token must be registered to WMS", wms.isWindowToken(token));
-
-        mWindowContext.release();
-
-        assertFalse("Token must be unregistered to WMS", wms.isWindowToken(token));
-    }
+    private final IWindowManager mWms = WindowManagerGlobal.getWindowManagerService();
 
     @Test
     public void testCreateWindowContextWindowManagerAttachClientToken() {
@@ -83,12 +86,133 @@
         assertEquals(mWindowContext.getWindowContextToken(), params.mWindowContextToken);
     }
 
+    /**
+     * Test the {@link WindowContext} life cycle behavior to add a new window token:
+     * <ul>
+     *  <li>The window token is created before adding the first view.</li>
+     *  <li>The window token is registered after adding the first view.</li>
+     *  <li>The window token is removed after {@link WindowContext}'s release.</li>
+     * </ul>
+     */
+    @Test
+    public void testCreateWindowContextNewTokenFromClient() throws Throwable {
+        final IBinder token = mWindowContext.getWindowContextToken();
+
+        // Test that the window token is not created yet.
+        assertFalse("Token must not be registered until adding the first window",
+                mWms.isWindowToken(token));
+
+        final WindowManager.LayoutParams params =
+                new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+        final View testView = new View(mWindowContext);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        testView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+            @Override
+            public void onViewAttachedToWindow(View v) {
+                latch.countDown();
+            }
+
+            @Override
+            public void onViewDetachedFromWindow(View v) {}
+        });
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowContext.getSystemService(WindowManager.class).addView(testView, params);
+
+            assertEquals(token, params.mWindowContextToken);
+        });
+
+
+        assertTrue(latch.await(4, TimeUnit.SECONDS));
+
+
+        // Verify that the window token of the window context is created after first addView().
+        assertTrue("Token must exist after adding the first view.",
+                mWms.isWindowToken(token));
+
+        mWindowContext.release();
+
+        // After the window context's release, the window token is also removed.
+        assertFalse("Token must be removed after release.", mWms.isWindowToken(token));
+    }
+
+    /**
+     * Verifies the behavior when window context attaches an {@link Activity} by override
+     * {@link WindowManager.LayoutParams#token}.
+     *
+     * The window context token should be overridden to
+     * {@link android.view.WindowManager.LayoutParams} and the {@link Activity}'s token must
+     * not be removed regardless of the release of window context.
+     */
+    @Test
+    public void testCreateWindowContext_AttachActivity_TokenNotRemovedAfterRelease()
+            throws Throwable {
+        mActivityRule.launchActivity(new Intent());
+        final Activity activity = mActivityRule.getActivity();
+        final WindowManager.LayoutParams params = activity.getWindow().getAttributes();
+
+        final WindowContext windowContext = createWindowContext(params.type);
+        final IBinder token = windowContext.getWindowContextToken();
+
+        final View testView = new View(windowContext);
+
+        mInstrumentation.runOnMainSync(() -> {
+            windowContext.getSystemService(WindowManager.class).addView(testView, params);
+
+            assertEquals(token, params.mWindowContextToken);
+        });
+        windowContext.release();
+
+        // Even if the window context is released, the activity should still exist.
+        assertTrue("Token must exist even if the window context is released.",
+                mWms.isWindowToken(activity.getActivityToken()));
+    }
+
+    /**
+     * Verifies the behavior when window context attaches an existing token by override
+     * {@link WindowManager.LayoutParams#token}.
+     *
+     * The window context token should be overridden to
+     * {@link android.view.WindowManager.LayoutParams} and the {@link Activity}'s token must not be
+     * removed regardless of release of window context.
+     */
+    @Test
+    public void testCreateWindowContext_AttachWindowToken_TokenNotRemovedAfterRelease()
+            throws Throwable {
+        final WindowContext windowContext = createWindowContext(TYPE_INPUT_METHOD);
+        final IBinder token = windowContext.getWindowContextToken();
+
+        final IBinder existingToken = new Binder();
+        mWms.addWindowToken(existingToken, TYPE_INPUT_METHOD, windowContext.getDisplayId());
+
+        final WindowManager.LayoutParams params =
+                new WindowManager.LayoutParams(TYPE_INPUT_METHOD);
+        params.token = existingToken;
+        final View testView = new View(windowContext);
+
+        mInstrumentation.runOnMainSync(() -> {
+            windowContext.getSystemService(WindowManager.class).addView(testView, params);
+
+            assertEquals(token, params.mWindowContextToken);
+        });
+        windowContext.release();
+
+        // Even if the window context is released, the existing token should still exist.
+        assertTrue("Token must exist even if the window context is released.",
+                mWms.isWindowToken(existingToken));
+
+        mWms.removeWindowToken(existingToken, DEFAULT_DISPLAY);
+    }
+
     private WindowContext createWindowContext() {
+        return createWindowContext(TYPE_APPLICATION_OVERLAY);
+    }
+
+    private WindowContext createWindowContext(@WindowType int type) {
         final Context instContext = mInstrumentation.getTargetContext();
         final Display display = instContext.getSystemService(DisplayManager.class)
                 .getDisplay(DEFAULT_DISPLAY);
-        final Context context = instContext.createDisplayContext(display);
-        return new WindowContext(context, TYPE_APPLICATION_OVERLAY,
-                null /* options */);
+        return (WindowContext) instContext.createWindowContext(display, type,  null /* options */);
     }
 }
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTests.java
deleted file mode 100644
index 5a3ea35..0000000
--- a/core/tests/mockingcoretests/src/android/view/DisplayTests.java
+++ /dev/null
@@ -1,527 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_90;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManagerGlobal;
-import android.util.DisplayMetrics;
-import android.view.DisplayAdjustments.FixedRotationAdjustments;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-import org.mockito.quality.Strictness;
-
-import java.util.function.Consumer;
-
-/**
- * Tests for {@link Display}.
- *
- * <p>Build/Install/Run:
- *
- * atest FrameworksMockingCoreTests:android.view.DisplayTests
- */
-@RunWith(AndroidJUnit4.class)
-public class DisplayTests {
-
-    private static final int APP_WIDTH = 272;
-    private static final int APP_HEIGHT = 700;
-    // Tablet size device, ROTATION_0 corresponds to portrait.
-    private static final int LOGICAL_WIDTH = 700;
-    private static final int LOGICAL_HEIGHT = 1800;
-
-    // Bounds of the app when the device is in portrait mode.
-    private static Rect sAppBoundsPortrait = buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT);
-    private static Rect sAppBoundsLandscape = buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH);
-
-    private StaticMockitoSession mMockitoSession;
-
-    private DisplayManagerGlobal mDisplayManagerGlobal;
-    private Context mApplicationContext;
-    private DisplayInfo mDisplayInfo = new DisplayInfo();
-
-    @Before
-    public void setupTests() {
-        mMockitoSession = mockitoSession()
-                .mockStatic(DisplayManagerGlobal.class)
-                .strictness(Strictness.LENIENT)
-                .startMocking();
-
-        // Ensure no adjustments are set before each test.
-        mApplicationContext = ApplicationProvider.getApplicationContext();
-        DisplayAdjustments displayAdjustments =
-                mApplicationContext.getResources().getDisplayAdjustments();
-        displayAdjustments.setFixedRotationAdjustments(null);
-        mApplicationContext.getResources().overrideDisplayAdjustments(null);
-        mApplicationContext.getResources().getConfiguration().windowConfiguration.setAppBounds(
-                null);
-        mApplicationContext.getResources().getConfiguration().windowConfiguration.setMaxBounds(
-                null);
-        mDisplayInfo.rotation = ROTATION_0;
-
-        mDisplayManagerGlobal = mock(DisplayManagerGlobal.class);
-        doReturn(mDisplayInfo).when(mDisplayManagerGlobal).getDisplayInfo(anyInt());
-    }
-
-    @After
-    public void teardownTests() {
-        if (mMockitoSession != null) {
-            mMockitoSession.finishMocking();
-        }
-        Mockito.framework().clearInlineMocks();
-    }
-
-    @Test
-    public void testConstructor_defaultDisplayAdjustments_matchesDisplayInfo() {
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        assertThat(display.getDisplayAdjustments()).isEqualTo(
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        DisplayInfo actualDisplayInfo = new DisplayInfo();
-        display.getDisplayInfo(actualDisplayInfo);
-        verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
-    }
-
-    @Test
-    public void testConstructor_defaultResources_matchesDisplayInfo() {
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        assertThat(display.getDisplayAdjustments()).isEqualTo(
-                mApplicationContext.getResources().getDisplayAdjustments());
-        DisplayInfo actualDisplayInfo = new DisplayInfo();
-        display.getDisplayInfo(actualDisplayInfo);
-        verifyDisplayInfo(actualDisplayInfo, mDisplayInfo);
-    }
-
-    @Test
-    public void testGetRotation_defaultDisplayAdjustments_rotationNotAdjusted() {
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        assertThat(display.getRotation()).isEqualTo(ROTATION_0);
-    }
-
-    @Test
-    public void testGetRotation_displayAdjustmentsWithoutOverride_rotationNotAdjusted() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, but no override is set.
-        DisplayAdjustments displayAdjustments = DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-        final FixedRotationAdjustments fixedRotationAdjustments =
-                new FixedRotationAdjustments(ROTATION_90, APP_WIDTH, APP_HEIGHT,
-                        DisplayCutout.NO_CUTOUT);
-        displayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
-        // GIVEN display is constructed with display adjustments.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                displayAdjustments);
-        // THEN rotation is not adjusted since no override was set.
-        assertThat(display.getRotation()).isEqualTo(ROTATION_0);
-    }
-
-    @Test
-    public void testGetRotation_resourcesWithoutOverride_rotationNotAdjusted() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, but no override is set.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN rotation is not adjusted since no override is set.
-        assertThat(display.getRotation()).isEqualTo(ROTATION_0);
-    }
-
-    @Test
-    public void testGetRotation_resourcesWithOverrideDisplayAdjustments_rotationAdjusted() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, and an override is set.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN rotation is adjusted since an override is set.
-        assertThat(display.getRotation()).isEqualTo(ROTATION_90);
-    }
-
-    @Test
-    public void testGetRealSize_defaultResourcesPortrait_matchesLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches display orientation.
-        verifyRealSizeIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealSize_defaultResourcesLandscape_matchesRotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches display orientation.
-        verifyRealSizeIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealSize_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        // THEN real size matches display orientation.
-        verifyRealSizeIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealSize_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        // THEN real size matches display orientation.
-        verifyRealSizeIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches display orientation.
-        verifyRealSizeIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches display orientation.
-        verifyRealSizeIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, and an override is set.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches app orientation.
-        verifyRealSizeIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated, and an override is set.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches app orientation.
-        verifyRealSizeIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesPortraitSandboxed_matchesSandboxBounds() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsPortrait);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches app bounds.
-        verifyRealSizeMatchesApp(display, sAppBoundsPortrait);
-    }
-
-    @Test
-    public void testGetRealSize_resourcesLandscapeSandboxed_matchesSandboxBounds() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsLandscape);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real size matches app bounds.
-        verifyRealSizeMatchesApp(display, sAppBoundsLandscape);
-    }
-
-    @Test
-    public void testGetRealMetrics_defaultResourcesPortrait_matchesLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_defaultResourcesLandscape_matchesRotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_defaultDisplayAdjustmentsPortrait_matchesLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_defaultDisplayAdjustmentsLandscape_matchesLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesPortraitWithFixedRotation_notRotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesWithLandscapeFixedRotation_notRotatedLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches display orientation.
-        verifyRealMetricsIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesWithPortraitOverrideRotation_rotatedLogicalSize() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated with an override.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_0);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches app orientation.
-        verifyRealMetricsIsPortrait(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesWithLandscapeOverrideRotation_rotatedLogicalSize() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN fixed rotation adjustments are rotated.
-        setOverrideFixedRotationAdjustments(mApplicationContext.getResources(), ROTATION_90);
-        // GIVEN display is constructed with default resources.
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches app orientation.
-        verifyRealMetricsIsLandscape(display);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesPortraitSandboxed_matchesSandboxBounds() {
-        // GIVEN display is not rotated.
-        setDisplayInfoPortrait(mDisplayInfo);
-        // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsPortrait);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches app bounds.
-        verifyRealMetricsMatchesApp(display, sAppBoundsPortrait);
-    }
-
-    @Test
-    public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesSandboxBounds() {
-        // GIVEN display is rotated.
-        setDisplayInfoLandscape(mDisplayInfo);
-        // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsLandscape);
-        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
-                mApplicationContext.getResources());
-        // THEN real metrics matches app bounds.
-        verifyRealMetricsMatchesApp(display, sAppBoundsLandscape);
-    }
-
-    // Given rotated display dimensions, calculate the letterboxed app bounds.
-    private static Rect buildAppBounds(int displayWidth, int displayHeight) {
-        final int midWidth = displayWidth / 2;
-        final int left = midWidth - (APP_WIDTH / 2);
-        final int right = midWidth + (APP_WIDTH / 2);
-        final int midHeight = displayHeight / 2;
-        // Coordinate system starts at top left.
-        final int top = midHeight - (APP_HEIGHT / 2);
-        final int bottom = midHeight + (APP_HEIGHT / 2);
-        return new Rect(left, top, right, bottom);
-    }
-
-    private static void setDisplayInfoLandscape(DisplayInfo displayInfo) {
-        displayInfo.rotation = ROTATION_90;
-        // Flip width & height assignment since the device is rotated.
-        displayInfo.logicalWidth = LOGICAL_HEIGHT;
-        displayInfo.logicalHeight = LOGICAL_WIDTH;
-    }
-
-    private static void setDisplayInfoPortrait(DisplayInfo displayInfo) {
-        displayInfo.rotation = ROTATION_0;
-        displayInfo.logicalWidth = LOGICAL_WIDTH;
-        displayInfo.logicalHeight = LOGICAL_HEIGHT;
-    }
-
-    /**
-     * Set max bounds to be sandboxed to the app bounds, indicating the app is in
-     * size compat mode or letterbox.
-     */
-    private static void setMaxBoundsSandboxedToMatchAppBounds(Resources resources, Rect appBounds) {
-        resources.getConfiguration().windowConfiguration.setMaxBounds(appBounds);
-    }
-
-    /**
-     * Do not compare entire display info, since it is updated to match display the test is run on.
-     */
-    private static void verifyDisplayInfo(DisplayInfo actual, DisplayInfo expected) {
-        assertThat(actual.displayId).isEqualTo(expected.displayId);
-        assertThat(actual.rotation).isEqualTo(expected.rotation);
-        assertThat(actual.logicalWidth).isEqualTo(LOGICAL_WIDTH);
-        assertThat(actual.logicalHeight).isEqualTo(LOGICAL_HEIGHT);
-    }
-
-    private static void verifyRealSizeIsLandscape(Display display) {
-        Point size = new Point();
-        display.getRealSize(size);
-        // Flip the width and height check since the device is rotated.
-        assertThat(size).isEqualTo(new Point(LOGICAL_HEIGHT, LOGICAL_WIDTH));
-    }
-
-    private static void verifyRealMetricsIsLandscape(Display display) {
-        DisplayMetrics metrics = new DisplayMetrics();
-        display.getRealMetrics(metrics);
-        // Flip the width and height check since the device is rotated.
-        assertThat(metrics.widthPixels).isEqualTo(LOGICAL_HEIGHT);
-        assertThat(metrics.heightPixels).isEqualTo(LOGICAL_WIDTH);
-    }
-
-    private static void verifyRealSizeIsPortrait(Display display) {
-        Point size = new Point();
-        display.getRealSize(size);
-        assertThat(size).isEqualTo(new Point(LOGICAL_WIDTH, LOGICAL_HEIGHT));
-    }
-
-    private static void verifyRealMetricsIsPortrait(Display display) {
-        DisplayMetrics metrics = new DisplayMetrics();
-        display.getRealMetrics(metrics);
-        assertThat(metrics.widthPixels).isEqualTo(LOGICAL_WIDTH);
-        assertThat(metrics.heightPixels).isEqualTo(LOGICAL_HEIGHT);
-    }
-
-    private static void verifyRealSizeMatchesApp(Display display, Rect appBounds) {
-        Point size = new Point();
-        display.getRealSize(size);
-        assertThat(size).isEqualTo(new Point(appBounds.width(), appBounds.height()));
-    }
-
-    private static void verifyRealMetricsMatchesApp(Display display, Rect appBounds) {
-        DisplayMetrics metrics = new DisplayMetrics();
-        display.getRealMetrics(metrics);
-        assertThat(metrics.widthPixels).isEqualTo(appBounds.width());
-        assertThat(metrics.heightPixels).isEqualTo(appBounds.height());
-    }
-
-    private static FixedRotationAdjustments setOverrideFixedRotationAdjustments(
-            Resources resources, @Surface.Rotation int rotation) {
-        FixedRotationAdjustments fixedRotationAdjustments =
-                setFixedRotationAdjustments(resources, rotation);
-        resources.overrideDisplayAdjustments(
-                buildOverrideRotationAdjustments(fixedRotationAdjustments));
-        return fixedRotationAdjustments;
-    }
-
-    private static FixedRotationAdjustments setFixedRotationAdjustments(Resources resources,
-            @Surface.Rotation int rotation) {
-        final FixedRotationAdjustments fixedRotationAdjustments =
-                new FixedRotationAdjustments(rotation, APP_WIDTH, APP_HEIGHT,
-                        DisplayCutout.NO_CUTOUT);
-        resources.getDisplayAdjustments().setFixedRotationAdjustments(fixedRotationAdjustments);
-        return fixedRotationAdjustments;
-    }
-
-    private static Consumer<DisplayAdjustments> buildOverrideRotationAdjustments(
-            FixedRotationAdjustments fixedRotationAdjustments) {
-        return consumedDisplayAdjustments
-                -> consumedDisplayAdjustments.setFixedRotationAdjustments(fixedRotationAdjustments);
-    }
-}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ac8a296..222c9bd 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1903,6 +1903,12 @@
       "group": "WM_DEBUG_FOCUS_LIGHT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "123161180": {
+      "message": "SEVER CHILDREN",
+      "level": "INFO",
+      "group": "WM_SHOW_TRANSACTIONS",
+      "at": "com\/android\/server\/wm\/WindowSurfaceController.java"
+    },
     "140319294": {
       "message": "IME target changed within ActivityRecord",
       "level": "DEBUG",
@@ -2137,12 +2143,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "332390227": {
-      "message": "Sandbox max bounds for uid %s to bounds %s due to letterboxing? %s mismatch with parent bounds? %s size compat mode %s",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_CONFIGURATION",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "342460966": {
       "message": "DRAG %s: pos=(%d,%d)",
       "level": "INFO",
@@ -2623,12 +2623,6 @@
       "group": "WM_DEBUG_WINDOW_ORGANIZER",
       "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
     },
-    "910200295": {
-      "message": "Sandbox max bounds due to mismatched orientation with parent, to %s vs DisplayArea %s",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_CONFIGURATION",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "913494177": {
       "message": "removeAllWindowsIfPossible: removing win=%s",
       "level": "WARN",
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index a7d3f798..5b79d76 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -175,6 +175,39 @@
     public static final int Y16 = 0x20363159;
 
     /**
+     * <p>Android YUV P010 format.</p>
+     *
+     * P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane
+     * followed immediately by a Wx(H/2) CbCr plane. Each sample is
+     * represented by a 16-bit little-endian value, with the lower 6 bits set
+     * to zero.
+     *
+     * <p>This format assumes
+     * <ul>
+     * <li>an even height</li>
+     * <li>a vertical stride equal to the height</li>
+     * </ul>
+     * </p>
+     *
+     * <pre>   stride_in_bytes = stride * 2 </pre>
+     * <pre>   y_size = stride_in_bytes * height </pre>
+     * <pre>   cbcr_size = stride_in_bytes * (height / 2) </pre>
+     * <pre>   cb_offset = y_size </pre>
+     * <pre>   cr_offset = cb_offset + 2 </pre>
+     *
+     * <p>For example, the {@link android.media.Image} object can provide data
+     * in this format from a {@link android.hardware.camera2.CameraDevice}
+     * through a {@link android.media.ImageReader} object if this format is
+     * supported by {@link android.hardware.camera2.CameraDevice}.</p>
+     *
+     * @see android.media.Image
+     * @see android.media.ImageReader
+     * @see android.hardware.camera2.CameraDevice
+     *
+     */
+    public static final int YCBCR_P010 = 0x36;
+
+    /**
      * YCbCr format, used for video.
      *
      * <p>For the {@link android.hardware.camera2} API, the {@link #YUV_420_888} format is
@@ -807,6 +840,8 @@
             case RAW_DEPTH:
             case RAW_SENSOR:
                 return 16;
+            case YCBCR_P010:
+                return 20;
             case RAW_DEPTH10:
             case RAW10:
                 return 10;
@@ -839,6 +874,7 @@
             case YUV_420_888:
             case YUV_422_888:
             case YUV_444_888:
+            case YCBCR_P010:
             case FLEX_RGB_888:
             case FLEX_RGBA_8888:
             case RAW_SENSOR:
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 005a726..35e6b859 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -42,6 +42,7 @@
 import android.system.ErrnoException;
 import android.system.OsConstants;
 import android.text.FontConfig;
+import android.util.ArrayMap;
 import android.util.Base64;
 import android.util.LongSparseArray;
 import android.util.LruCache;
@@ -67,7 +68,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -147,7 +147,7 @@
      */
     @GuardedBy("SYSTEM_FONT_MAP_LOCK")
     @UnsupportedAppUsage(trackingBug = 123769347)
-    static final Map<String, Typeface> sSystemFontMap = new HashMap<>();
+    static final Map<String, Typeface> sSystemFontMap = new ArrayMap<>();
 
     // DirectByteBuffer object to hold sSystemFontMap's backing memory mapping.
     static ByteBuffer sSystemFontMapBuffer = null;
@@ -1231,7 +1231,7 @@
     /** @hide */
     @VisibleForTesting
     public static Map<String, Typeface> deserializeFontMap(ByteBuffer buffer) throws IOException {
-        Map<String, Typeface> fontMap = new HashMap<>();
+        Map<String, Typeface> fontMap = new ArrayMap<>();
         int typefacesBytesCount = buffer.getInt();
         long[] nativePtrs = nativeReadTypefaces(buffer.slice());
         if (nativePtrs == null) {
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index fea756c..9214ff1 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -486,7 +486,8 @@
             long ptr;
             int fontIdentifier;
             if (mFont == null) {
-                ptr = nBuild(builderPtr, readonlyBuffer, filePath, mWeight, italic, mTtcIndex);
+                ptr = nBuild(builderPtr, readonlyBuffer, filePath, mLocaleList, mWeight, italic,
+                        mTtcIndex);
                 long fontBufferPtr = nGetFontBufferAddress(ptr);
                 synchronized (SOURCE_ID_LOCK) {
                     long id = FONT_SOURCE_ID_MAP.get(fontBufferPtr, -1);
@@ -513,8 +514,8 @@
         @CriticalNative
         private static native void nAddAxis(long builderPtr, int tag, float value);
         private static native long nBuild(
-                long builderPtr, @NonNull ByteBuffer buffer, @NonNull String filePath, int weight,
-                boolean italic, int ttcIndex);
+                long builderPtr, @NonNull ByteBuffer buffer, @NonNull String filePath,
+                @NonNull String localeList, int weight, boolean italic, int ttcIndex);
         @CriticalNative
         private static native long nGetReleaseNativeFont();
 
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index c29c194..77f86fe 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -20,15 +20,16 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.text.FontConfig;
+import android.util.SparseIntArray;
 
 import com.android.internal.util.Preconditions;
 
 import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
 
 import libcore.util.NativeAllocationRegistry;
 
 import java.util.ArrayList;
-import java.util.HashSet;
 
 /**
  * A font family class can be used for creating Typeface.
@@ -68,7 +69,9 @@
                     nGetReleaseNativeFamily());
 
         private final ArrayList<Font> mFonts = new ArrayList<>();
-        private final HashSet<Integer> mStyleHashSet = new HashSet<>();
+        // Most FontFamily only has  regular, bold, italic, bold-italic. Thus 4 should be good for
+        // initial capacity.
+        private final SparseIntArray mStyles = new SparseIntArray(4);
 
         /**
          * Constructs a builder.
@@ -77,7 +80,7 @@
          */
         public Builder(@NonNull Font font) {
             Preconditions.checkNotNull(font, "font can not be null");
-            mStyleHashSet.add(makeStyleIdentifier(font));
+            mStyles.append(makeStyleIdentifier(font), 0);
             mFonts.add(font);
         }
 
@@ -97,9 +100,11 @@
          */
         public @NonNull Builder addFont(@NonNull Font font) {
             Preconditions.checkNotNull(font, "font can not be null");
-            if (!mStyleHashSet.add(makeStyleIdentifier(font))) {
+            int key = makeStyleIdentifier(font);
+            if (mStyles.indexOfKey(key) >= 0) {
                 throw new IllegalArgumentException(font + " has already been added");
             }
+            mStyles.append(key, 0);
             mFonts.add(font);
             return this;
         }
@@ -120,7 +125,7 @@
                 nAddFont(builderPtr, mFonts.get(i).getNativePtr());
             }
             final long ptr = nBuild(builderPtr, langTags, variant, isCustomFallback);
-            final FontFamily family = new FontFamily(mFonts, langTags, variant, ptr);
+            final FontFamily family = new FontFamily(mFonts, ptr);
             sFamilyRegistory.registerNativeAllocation(family, ptr);
             return family;
         }
@@ -139,15 +144,11 @@
     }
 
     private final ArrayList<Font> mFonts;
-    private final String mLangTags;
-    private final int mVariant;
     private final long mNativePtr;
 
     // Use Builder instead.
-    private FontFamily(@NonNull ArrayList<Font> fonts, String langTags, int variant, long ptr) {
+    private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) {
         mFonts = fonts;
-        mLangTags = langTags;
-        mVariant = variant;
         mNativePtr = ptr;
     }
 
@@ -157,7 +158,7 @@
      * @return a BCP-47 compliant language tag.
      */
     public @Nullable String getLangTags() {
-        return mLangTags;
+        return nGetLangTags(mNativePtr);
     }
 
     /**
@@ -165,7 +166,7 @@
      * @return a family variant
      */
     public int getVariant() {
-        return mVariant;
+        return nGetVariant(mNativePtr);
     }
 
     /**
@@ -191,4 +192,10 @@
     public long getNativePtr() {
         return mNativePtr;
     }
+
+    @FastNative
+    private static native String nGetLangTags(long family);
+
+    @CriticalNative
+    private static native int nGetVariant(long family);
 }
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index c166e12..904085f 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -22,6 +22,7 @@
 import android.graphics.Typeface;
 import android.text.FontConfig;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -36,8 +37,6 @@
 import java.nio.channels.FileChannel;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -76,7 +75,7 @@
             if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
                 sAvailableFonts = collectAllFonts();
             } else {
-                Set<Font> set = new HashSet<>();
+                Set<Font> set = new ArraySet<>();
                 for (FontFamily[] items : sFamilyMap.values()) {
                     for (FontFamily family : items) {
                         for (int i = 0; i < family.getSize(); ++i) {
@@ -96,7 +95,7 @@
         FontConfig fontConfig = getSystemPreinstalledFontConfig();
         Map<String, FontFamily[]> map = buildSystemFallback(fontConfig);
 
-        Set<Font> res = new HashSet<>();
+        Set<Font> res = new ArraySet<>();
         for (FontFamily[] families : map.values()) {
             for (FontFamily family : families) {
                 for (int i = 0; i < family.getSize(); ++i) {
@@ -218,7 +217,7 @@
     }
 
     private static void appendNamedFamily(@NonNull FontConfig.FontFamily xmlFamily,
-            @NonNull HashMap<String, ByteBuffer> bufferCache,
+            @NonNull ArrayMap<String, ByteBuffer> bufferCache,
             @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap) {
         final String familyName = xmlFamily.getName();
         final FontFamily family = createFontFamily(
@@ -284,8 +283,8 @@
      */
     @VisibleForTesting
     public static Map<String, FontFamily[]> buildSystemFallback(FontConfig fontConfig) {
-        final Map<String, FontFamily[]> fallbackMap = new HashMap<>();
-        final HashMap<String, ByteBuffer> bufferCache = new HashMap<>();
+        final Map<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+        final ArrayMap<String, ByteBuffer> bufferCache = new ArrayMap<>();
         final List<FontConfig.FontFamily> xmlFamilies = fontConfig.getFontFamilies();
 
         final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>();
@@ -326,7 +325,7 @@
     public static Map<String, Typeface> buildSystemTypefaces(
             FontConfig fontConfig,
             Map<String, FontFamily[]> fallbackMap) {
-        final HashMap<String, Typeface> result = new HashMap<>();
+        final ArrayMap<String, Typeface> result = new ArrayMap<>();
         Typeface.initSystemDefaultTypefaces(fallbackMap, fontConfig.getAliases(), result);
         return result;
     }
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index 7fbbb61..4612ba2 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// Sidecar
 android_library_import {
     name: "window-sidecar",
     aars: ["window-sidecar-release.aar"],
@@ -20,7 +21,7 @@
 
 java_library {
     name: "androidx.window.sidecar",
-    srcs: ["src/**/*.java"],
+    srcs: ["src/androidx/window/sidecar/**/*.java", "src/androidx/window/util/**/*.java"],
     static_libs: ["window-sidecar"],
     installable: true,
     sdk_version: "core_platform",
@@ -36,3 +37,31 @@
     src: "androidx.window.sidecar.xml",
     filename_from_src: true,
 }
+
+// Extensions
+// NOTE: This module is still under active development and must not
+// be used in production. Use 'androidx.window.sidecar' instead.
+android_library_import {
+    name: "window-extensions",
+    aars: ["window-extensions-release.aar"],
+    sdk_version: "current",
+}
+
+java_library {
+    name: "androidx.window.extensions",
+    srcs: ["src/androidx/window/extensions/**/*.java", "src/androidx/window/util/**/*.java"],
+    static_libs: ["window-extensions"],
+    installable: true,
+    sdk_version: "core_platform",
+    system_ext_specific: true,
+    libs: ["framework", "androidx.annotation_annotation",],
+    required: ["androidx.window.extensions.xml",],
+}
+
+prebuilt_etc {
+    name: "androidx.window.extensions.xml",
+    system_ext_specific: true,
+    sub_dir: "permissions",
+    src: "androidx.window.extensions.xml",
+    filename_from_src: true,
+}
diff --git a/libs/WindowManager/Jetpack/androidx.window.extensions.xml b/libs/WindowManager/Jetpack/androidx.window.extensions.xml
new file mode 100644
index 0000000..34264aa
--- /dev/null
+++ b/libs/WindowManager/Jetpack/androidx.window.extensions.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<permissions>
+    <library
+        name="androidx.window.extensions"
+        file="/system_ext/framework/androidx.window.extensions.jar"/>
+</permissions>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
new file mode 100644
index 0000000..b7a6039
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions;
+
+import android.content.Context;
+
+/**
+ * Provider class that will instantiate the library implementation. It must be included in the
+ * vendor library, and the vendor implementation must match the signature of this class.
+ */
+public class ExtensionProvider {
+    /**
+     * Provides a simple implementation of {@link ExtensionInterface} that can be replaced by
+     * an OEM by overriding this method.
+     */
+    public static ExtensionInterface getExtensionImpl(Context context) {
+        return new SampleExtensionImpl(context);
+    }
+
+    /**
+     * The support library will use this method to check API version compatibility.
+     * @return API version string in MAJOR.MINOR.PATCH-description format.
+     */
+    public static String getApiVersion() {
+        return "1.0.0-settings_sample";
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
new file mode 100644
index 0000000..0bf6965
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
+import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.window.util.BaseDisplayFeature;
+import androidx.window.util.SettingsConfigProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reference implementation of androidx.window.extensions OEM interface for use with
+ * WindowManager Jetpack.
+ *
+ * NOTE: This version is a work in progress and under active development. It MUST NOT be used in
+ * production builds since the interface can still change before reaching stable version.
+ * Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead.
+ */
+class SampleExtensionImpl extends StubExtension implements
+        SettingsConfigProvider.StateChangeCallback {
+    private static final String TAG = "SampleExtension";
+
+    private final SettingsConfigProvider mConfigProvider;
+
+    SampleExtensionImpl(Context context) {
+        mConfigProvider = new SettingsConfigProvider(context, this);
+    }
+
+    @Override
+    public void onDevicePostureChanged() {
+        updateDeviceState(new ExtensionDeviceState(mConfigProvider.getDeviceState()));
+    }
+
+    @Override
+    public void onDisplayFeaturesChanged() {
+        for (Activity activity : getActivitiesListeningForLayoutChanges()) {
+            ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
+            updateWindowLayout(activity, newLayout);
+        }
+    }
+
+    @NonNull
+    private ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
+        List<ExtensionDisplayFeature> displayFeatures = getDisplayFeatures(activity);
+        return new ExtensionWindowLayoutInfo(displayFeatures);
+    }
+
+    private List<ExtensionDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
+        List<ExtensionDisplayFeature> features = new ArrayList<>();
+        int displayId = activity.getDisplayId();
+        if (displayId != DEFAULT_DISPLAY) {
+            Log.w(TAG, "This sample doesn't support display features on secondary displays");
+            return features;
+        }
+
+        if (activity.isInMultiWindowMode()) {
+            // It is recommended not to report any display features in multi-window mode, since it
+            // won't be possible to synchronize the display feature positions with window movement.
+            return features;
+        }
+
+        List<BaseDisplayFeature> storedFeatures = mConfigProvider.getDisplayFeatures();
+        for (BaseDisplayFeature baseFeature : storedFeatures) {
+            Rect featureRect = baseFeature.getRect();
+            rotateRectToDisplayRotation(displayId, featureRect);
+            transformToWindowSpaceRect(activity, featureRect);
+            features.add(new ExtensionFoldingFeature(featureRect, baseFeature.getType(),
+                    baseFeature.getState()));
+        }
+        return features;
+    }
+
+    @Override
+    protected void onListenersChanged() {
+        if (hasListeners()) {
+            mConfigProvider.registerObserversIfNeeded();
+        } else {
+            mConfigProvider.unregisterObserversIfNeeded();
+        }
+
+        onDevicePostureChanged();
+        onDisplayFeaturesChanged();
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java
new file mode 100644
index 0000000..b0895ef
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions;
+
+import android.app.Activity;
+
+import androidx.annotation.NonNull;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Basic implementation of the {@link ExtensionInterface}. An OEM can choose to use it as the base
+ * class for their implementation.
+ */
+abstract class StubExtension implements ExtensionInterface {
+
+    private ExtensionCallback mExtensionCallback;
+    private final Set<Activity> mWindowLayoutChangeListenerActivities = new HashSet<>();
+    private boolean mDeviceStateChangeListenerRegistered;
+
+    StubExtension() {
+    }
+
+    @Override
+    public void setExtensionCallback(@NonNull ExtensionCallback extensionCallback) {
+        this.mExtensionCallback = extensionCallback;
+    }
+
+    @Override
+    public void onWindowLayoutChangeListenerAdded(@NonNull Activity activity) {
+        this.mWindowLayoutChangeListenerActivities.add(activity);
+        this.onListenersChanged();
+    }
+
+    @Override
+    public void onWindowLayoutChangeListenerRemoved(@NonNull Activity activity) {
+        this.mWindowLayoutChangeListenerActivities.remove(activity);
+        this.onListenersChanged();
+    }
+
+    @Override
+    public void onDeviceStateListenersChanged(boolean isEmpty) {
+        this.mDeviceStateChangeListenerRegistered = !isEmpty;
+        this.onListenersChanged();
+    }
+
+    void updateDeviceState(ExtensionDeviceState newState) {
+        if (this.mExtensionCallback != null) {
+            mExtensionCallback.onDeviceStateChanged(newState);
+        }
+    }
+
+    void updateWindowLayout(@NonNull Activity activity,
+            @NonNull ExtensionWindowLayoutInfo newLayout) {
+        if (this.mExtensionCallback != null) {
+            mExtensionCallback.onWindowLayoutChanged(activity, newLayout);
+        }
+    }
+
+    @NonNull
+    Set<Activity> getActivitiesListeningForLayoutChanges() {
+        return mWindowLayoutChangeListenerActivities;
+    }
+
+    protected boolean hasListeners() {
+        return !mWindowLayoutChangeListenerActivities.isEmpty()
+                || mDeviceStateChangeListenerRegistered;
+    }
+
+    protected abstract void onListenersChanged();
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
new file mode 100644
index 0000000..1094a0e
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.sidecar;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
+import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
+
+import android.app.Activity;
+import android.app.ActivityThread;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.window.util.BaseDisplayFeature;
+import androidx.window.util.SettingsConfigProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reference implementation of androidx.window.sidecar OEM interface for use with
+ * WindowManager Jetpack.
+ */
+class SampleSidecarImpl extends StubSidecar implements
+        SettingsConfigProvider.StateChangeCallback {
+    private static final String TAG = "SampleSidecar";
+
+    private final SettingsConfigProvider mConfigProvider;
+
+    SampleSidecarImpl(Context context) {
+        mConfigProvider = new SettingsConfigProvider(context, this);
+    }
+
+    @Override
+    public void onDevicePostureChanged() {
+        updateDeviceState(getDeviceState());
+    }
+
+    @Override
+    public void onDisplayFeaturesChanged() {
+        for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
+            SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
+            updateWindowLayout(windowToken, newLayout);
+        }
+    }
+
+    @NonNull
+    @Override
+    public SidecarDeviceState getDeviceState() {
+        SidecarDeviceState deviceState = new SidecarDeviceState();
+        deviceState.posture = mConfigProvider.getDeviceState();
+        return deviceState;
+    }
+
+    @NonNull
+    @Override
+    public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
+        Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
+        SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo();
+        if (activity == null) {
+            return windowLayoutInfo;
+        }
+        windowLayoutInfo.displayFeatures = getDisplayFeatures(activity);
+        return windowLayoutInfo;
+    }
+
+    private List<SidecarDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
+        List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>();
+        int displayId = activity.getDisplayId();
+        if (displayId != DEFAULT_DISPLAY) {
+            Log.w(TAG, "This sample doesn't support display features on secondary displays");
+            return features;
+        }
+
+        if (activity.isInMultiWindowMode()) {
+            // It is recommended not to report any display features in multi-window mode, since it
+            // won't be possible to synchronize the display feature positions with window movement.
+            return features;
+        }
+
+        List<BaseDisplayFeature> storedFeatures = mConfigProvider.getDisplayFeatures();
+        for (BaseDisplayFeature baseFeature : storedFeatures) {
+            SidecarDisplayFeature feature = new SidecarDisplayFeature();
+            Rect featureRect = baseFeature.getRect();
+            rotateRectToDisplayRotation(displayId, featureRect);
+            transformToWindowSpaceRect(activity, featureRect);
+            feature.setRect(featureRect);
+            feature.setType(baseFeature.getType());
+            features.add(feature);
+        }
+        return features;
+    }
+
+    @Override
+    protected void onListenersChanged() {
+        if (hasListeners()) {
+            mConfigProvider.registerObserversIfNeeded();
+        } else {
+            mConfigProvider.unregisterObserversIfNeeded();
+        }
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
deleted file mode 100644
index 5397302..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.sidecar;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static androidx.window.sidecar.SidecarHelper.getWindowDisplay;
-import static androidx.window.sidecar.SidecarHelper.isInMultiWindow;
-import static androidx.window.sidecar.SidecarHelper.rotateRectToDisplayRotation;
-import static androidx.window.sidecar.SidecarHelper.transformToWindowSpaceRect;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-class SettingsSidecarImpl extends StubSidecar {
-    private static final String TAG = "SettingsSidecar";
-
-    private static final String DEVICE_POSTURE = "device_posture";
-    private static final String DISPLAY_FEATURES = "display_features";
-
-    private static final Pattern FEATURE_PATTERN =
-            Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]");
-
-    private static final String FEATURE_TYPE_FOLD = "fold";
-    private static final String FEATURE_TYPE_HINGE = "hinge";
-
-    private Context mContext;
-    private SettingsObserver mSettingsObserver;
-
-    final class SettingsObserver extends ContentObserver {
-        private final Uri mDevicePostureUri =
-                Settings.Global.getUriFor(DEVICE_POSTURE);
-        private final Uri mDisplayFeaturesUri =
-                Settings.Global.getUriFor(DISPLAY_FEATURES);
-        private final ContentResolver mResolver = mContext.getContentResolver();
-        private boolean mRegisteredObservers;
-
-
-        private SettingsObserver() {
-            super(new Handler(Looper.getMainLooper()));
-        }
-
-        private void registerObserversIfNeeded() {
-            if (mRegisteredObservers) {
-                return;
-            }
-            mRegisteredObservers = true;
-            mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendents */,
-                    this /* ContentObserver */);
-            mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendents */,
-                    this /* ContentObserver */);
-        }
-
-        private void unregisterObserversIfNeeded() {
-            if (!mRegisteredObservers) {
-                return;
-            }
-            mRegisteredObservers = false;
-            mResolver.unregisterContentObserver(this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            if (uri == null) {
-                return;
-            }
-
-            if (mDevicePostureUri.equals(uri)) {
-                updateDevicePosture();
-                return;
-            }
-            if (mDisplayFeaturesUri.equals(uri)) {
-                updateDisplayFeatures();
-                return;
-            }
-        }
-    }
-
-    SettingsSidecarImpl(Context context) {
-        mContext = context;
-        mSettingsObserver = new SettingsObserver();
-    }
-
-    private void updateDevicePosture() {
-        updateDeviceState(getDeviceState());
-    }
-
-    /** Update display features with values read from settings. */
-    private void updateDisplayFeatures() {
-        for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
-            SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
-            updateWindowLayout(windowToken, newLayout);
-        }
-    }
-
-    @NonNull
-    @Override
-    public SidecarDeviceState getDeviceState() {
-        ContentResolver resolver = mContext.getContentResolver();
-        int posture = Settings.Global.getInt(resolver, DEVICE_POSTURE,
-                SidecarDeviceState.POSTURE_UNKNOWN);
-        SidecarDeviceState deviceState = new SidecarDeviceState();
-        deviceState.posture = posture;
-        return deviceState;
-    }
-
-    @NonNull
-    @Override
-    public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
-        List<SidecarDisplayFeature> displayFeatures = readDisplayFeatures(windowToken);
-        SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo();
-        windowLayoutInfo.displayFeatures = displayFeatures;
-        return windowLayoutInfo;
-    }
-
-    private List<SidecarDisplayFeature> readDisplayFeatures(IBinder windowToken) {
-        List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>();
-        int displayId = getWindowDisplay(windowToken);
-        if (displayId != DEFAULT_DISPLAY) {
-            Log.w(TAG, "This sample doesn't support display features on secondary displays");
-            return features;
-        }
-
-        if (isInMultiWindow(windowToken)) {
-            // It is recommended not to report any display features in multi-window mode, since it
-            // won't be possible to synchronize the display feature positions with window movement.
-            return features;
-        }
-
-        ContentResolver resolver = mContext.getContentResolver();
-        String displayFeaturesString = Settings.Global.getString(resolver, DISPLAY_FEATURES);
-        if (TextUtils.isEmpty(displayFeaturesString)) {
-            displayFeaturesString = mContext.getResources().getString(
-                    R.string.config_display_features);
-        }
-        if (TextUtils.isEmpty(displayFeaturesString)) {
-            return features;
-        }
-
-        String[] featureStrings = displayFeaturesString.split(";");
-        for (String featureString : featureStrings) {
-            Matcher featureMatcher = FEATURE_PATTERN.matcher(featureString);
-            if (!featureMatcher.matches()) {
-                Log.e(TAG, "Malformed feature description format: " + featureString);
-                continue;
-            }
-            try {
-                String featureType = featureMatcher.group(1);
-                int type;
-                switch (featureType) {
-                    case FEATURE_TYPE_FOLD:
-                        type = SidecarDisplayFeature.TYPE_FOLD;
-                        break;
-                    case FEATURE_TYPE_HINGE:
-                        type = SidecarDisplayFeature.TYPE_HINGE;
-                        break;
-                    default: {
-                        Log.e(TAG, "Malformed feature type: " + featureType);
-                        continue;
-                    }
-                }
-
-                int left = Integer.parseInt(featureMatcher.group(2));
-                int top = Integer.parseInt(featureMatcher.group(3));
-                int right = Integer.parseInt(featureMatcher.group(4));
-                int bottom = Integer.parseInt(featureMatcher.group(5));
-                Rect featureRect = new Rect(left, top, right, bottom);
-                rotateRectToDisplayRotation(featureRect, displayId);
-                transformToWindowSpaceRect(featureRect, windowToken);
-                if (isNotZero(featureRect)) {
-                    SidecarDisplayFeature feature = new SidecarDisplayFeature();
-                    feature.setRect(featureRect);
-                    feature.setType(type);
-                    features.add(feature);
-                } else {
-                    Log.w(TAG, "Failed to adjust feature to window");
-                }
-            } catch (NumberFormatException e) {
-                Log.e(TAG, "Malformed feature description: " + featureString);
-            }
-        }
-        return features;
-    }
-
-    private static boolean isNotZero(Rect rect) {
-        return rect.height() > 0 || rect.width() > 0;
-    }
-
-    @Override
-    protected void onListenersChanged() {
-        if (mSettingsObserver == null) {
-            return;
-        }
-
-        if (hasListeners()) {
-            mSettingsObserver.registerObserversIfNeeded();
-        } else {
-            mSettingsObserver.unregisterObserversIfNeeded();
-        }
-    }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
index 0b4915ed..e6f8388 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
@@ -28,7 +28,7 @@
      * an OEM by overriding this method.
      */
     public static SidecarInterface getSidecarImpl(Context context) {
-        return new SettingsSidecarImpl(context);
+        return new SampleSidecarImpl(context);
     }
 
     /**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java
new file mode 100644
index 0000000..b74a2a4
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.util;
+
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+
+/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
+public class BaseDisplayFeature {
+    private final int mType;
+    private final int mState;
+    @NonNull
+    public final Rect mRect;
+
+    public BaseDisplayFeature(int type, int state, @NonNull Rect rect) {
+        this.mType = type;
+        this.mState = state;
+        if (rect.width() == 0 && rect.height() == 0) {
+            throw new IllegalArgumentException(
+                    "Display feature rectangle cannot have zero width and height simultaneously.");
+        }
+        this.mRect = rect;
+    }
+
+    public int getType() {
+        return mType;
+    }
+
+    public int getState() {
+        return mState;
+    }
+
+    @NonNull
+    public Rect getRect() {
+        return mRect;
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
similarity index 62%
rename from libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
rename to libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
index e5b6cff..2a593f1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,30 +14,36 @@
  * limitations under the License.
  */
 
-package androidx.window.sidecar;
+package androidx.window.util;
 
-import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 
 import android.app.Activity;
-import android.app.ActivityThread;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManagerGlobal;
-import android.os.IBinder;
 import android.view.DisplayInfo;
 import android.view.Surface;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-class SidecarHelper {
+/**
+ * Util class for both Sidecar and Extensions.
+ */
+public final class ExtensionHelper {
+
+    private ExtensionHelper() {
+        // Util class, no instances should be created.
+    }
+
     /**
-     * Rotate the input rectangle specified in default display orientation to the current display
+     * Rotates the input rectangle specified in default display orientation to the current display
      * rotation.
      */
-    static void rotateRectToDisplayRotation(Rect inOutRect, int displayId) {
+    public static void rotateRectToDisplayRotation(int displayId, Rect inOutRect) {
         DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance();
         DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId);
         int rotation = displayInfo.rotation;
@@ -52,7 +58,7 @@
     }
 
     /**
-     * Rotate the input rectangle within parent bounds for a given delta.
+     * Rotates the input rectangle within parent bounds for a given delta.
      */
     private static void rotateBounds(Rect inOutRect, int parentWidth, int parentHeight,
             @Surface.Rotation int delta) {
@@ -79,9 +85,9 @@
         }
     }
 
-    /** Transform rectangle from absolute coordinate space to the window coordinate space. */
-    static void transformToWindowSpaceRect(Rect inOutRect, IBinder windowToken) {
-        Rect windowRect = getWindowBounds(windowToken);
+    /** Transforms rectangle from absolute coordinate space to the window coordinate space. */
+    public static void transformToWindowSpaceRect(Activity activity, Rect inOutRect) {
+        Rect windowRect = getWindowBounds(activity);
         if (windowRect == null) {
             inOutRect.setEmpty();
             return;
@@ -95,32 +101,17 @@
     }
 
     /**
-     * Get the current window bounds in absolute coordinates.
-     * NOTE: Only works with Activity windows.
+     * Gets the current window bounds in absolute coordinates.
      */
     @Nullable
-    private static Rect getWindowBounds(IBinder windowToken) {
-        Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
-        return activity != null
-                ? activity.getWindowManager().getCurrentWindowMetrics().getBounds()
-                : null;
+    private static Rect getWindowBounds(@NonNull Activity activity) {
+        return activity.getWindowManager().getCurrentWindowMetrics().getBounds();
     }
 
     /**
-     * Check if this window is an Activity window that is in multi-window mode.
+     * Checks if both dimensions of the given rect are zero at the same time.
      */
-    static boolean isInMultiWindow(IBinder windowToken) {
-        Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
-        return activity != null && activity.isInMultiWindowMode();
-    }
-
-    /**
-     * Get the id of the parent display for the window.
-     * NOTE: Only works with Activity windows.
-     */
-    static int getWindowDisplay(IBinder windowToken) {
-        Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
-        return activity != null
-                ? activity.getWindowManager().getDefaultDisplay().getDisplayId() : INVALID_DISPLAY;
+    public static boolean isZero(@NonNull Rect rect) {
+        return rect.height() == 0 && rect.width() == 0;
     }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java
new file mode 100644
index 0000000..6dd190c
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.util;
+
+import static androidx.window.util.ExtensionHelper.isZero;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Device and display feature state provider that uses Settings as the source.
+ */
+public final class SettingsConfigProvider extends ContentObserver {
+    private static final String TAG = "SettingsConfigProvider";
+    private static final String DEVICE_POSTURE = "device_posture";
+    private static final String DISPLAY_FEATURES = "display_features";
+
+    private static final Pattern FEATURE_PATTERN =
+            Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]");
+
+    private static final String FEATURE_TYPE_FOLD = "fold";
+    private static final String FEATURE_TYPE_HINGE = "hinge";
+
+    private final Uri mDevicePostureUri =
+            Settings.Global.getUriFor(DEVICE_POSTURE);
+    private final Uri mDisplayFeaturesUri =
+            Settings.Global.getUriFor(DISPLAY_FEATURES);
+    private final Context mContext;
+    private final ContentResolver mResolver;
+    private final StateChangeCallback mCallback;
+    private boolean mRegisteredObservers;
+
+    public SettingsConfigProvider(@NonNull Context context, @NonNull StateChangeCallback callback) {
+        super(new Handler(Looper.getMainLooper()));
+        mContext = context;
+        mResolver = context.getContentResolver();
+        mCallback = callback;
+    }
+
+    /**
+     * Registers the content observers for Settings keys that store device state and display feature
+     * configurations.
+     */
+    public void registerObserversIfNeeded() {
+        if (mRegisteredObservers) {
+            return;
+        }
+        mRegisteredObservers = true;
+        mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendants */,
+                this /* ContentObserver */);
+        mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendants */,
+                this /* ContentObserver */);
+    }
+
+    /**
+     * Unregisters the content observers that are tracking the state changes.
+     * @see #registerObserversIfNeeded()
+     */
+    public void unregisterObserversIfNeeded() {
+        if (!mRegisteredObservers) {
+            return;
+        }
+        mRegisteredObservers = false;
+        mResolver.unregisterContentObserver(this);
+    }
+
+    /**
+     * Gets the device posture int stored in Settings.
+     */
+    public int getDeviceState() {
+        return Settings.Global.getInt(mResolver, DEVICE_POSTURE,
+                0 /* POSTURE_UNKNOWN */);
+    }
+
+    /**
+     * Gets the list of all display feature configs stored in Settings. Uses a custom
+     * {@link BaseDisplayFeature} class to report the config to be translated for actual
+     * containers in Sidecar or Extensions.
+     */
+    public List<BaseDisplayFeature> getDisplayFeatures() {
+        List<BaseDisplayFeature> features = new ArrayList<>();
+        String displayFeaturesString = Settings.Global.getString(mResolver, DISPLAY_FEATURES);
+        if (TextUtils.isEmpty(displayFeaturesString)) {
+            displayFeaturesString = mContext.getResources().getString(
+                    R.string.config_display_features);
+        }
+        if (TextUtils.isEmpty(displayFeaturesString)) {
+            return features;
+        }
+        String[] featureStrings =  displayFeaturesString.split(";");
+
+        int deviceState = getDeviceState();
+
+        for (String featureString : featureStrings) {
+            Matcher featureMatcher = FEATURE_PATTERN.matcher(featureString);
+            if (!featureMatcher.matches()) {
+                Log.e(TAG, "Malformed feature description format: " + featureString);
+                continue;
+            }
+            try {
+                String featureType = featureMatcher.group(1);
+                int type;
+                switch (featureType) {
+                    case FEATURE_TYPE_FOLD:
+                        type = 1 /* TYPE_FOLD */;
+                        break;
+                    case FEATURE_TYPE_HINGE:
+                        type = 2 /* TYPE_HINGE */;
+                        break;
+                    default: {
+                        Log.e(TAG, "Malformed feature type: " + featureType);
+                        continue;
+                    }
+                }
+
+                int left = Integer.parseInt(featureMatcher.group(2));
+                int top = Integer.parseInt(featureMatcher.group(3));
+                int right = Integer.parseInt(featureMatcher.group(4));
+                int bottom = Integer.parseInt(featureMatcher.group(5));
+                Rect featureRect = new Rect(left, top, right, bottom);
+                if (!isZero(featureRect)) {
+                    BaseDisplayFeature feature = new BaseDisplayFeature(type, deviceState,
+                            featureRect);
+                    features.add(feature);
+                } else {
+                    Log.w(TAG, "Read empty feature");
+                }
+            } catch (NumberFormatException e) {
+                Log.e(TAG, "Malformed feature description: " + featureString);
+            }
+        }
+        return features;
+    }
+
+    @Override
+    public void onChange(boolean selfChange, Uri uri) {
+        if (uri == null) {
+            return;
+        }
+
+        if (mDevicePostureUri.equals(uri)) {
+            mCallback.onDevicePostureChanged();
+            mCallback.onDisplayFeaturesChanged();
+            return;
+        }
+        if (mDisplayFeaturesUri.equals(uri)) {
+            mCallback.onDisplayFeaturesChanged();
+        }
+    }
+
+    /**
+     * Callback that notifies about device or display feature state changes.
+     */
+    public interface StateChangeCallback {
+        /**
+         * Notifies about the device state update.
+         */
+        void onDevicePostureChanged();
+
+        /**
+         * Notifies about the display feature config update.
+         */
+        void onDisplayFeaturesChanged();
+    }
+}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
new file mode 100644
index 0000000..7b306b0
--- /dev/null
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
index 7ea4689..255e4d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.animation
 
-import android.os.Looper
 import android.util.ArrayMap
 import android.util.Log
 import android.view.View
@@ -847,7 +846,7 @@
      * pass to [spring].
      */
     data class SpringConfig internal constructor(
-        internal var stiffness: Float,
+        var stiffness: Float,
         internal var dampingRatio: Float,
         internal var startVelocity: Float = 0f,
         internal var finalPosition: Float = UNSET
@@ -879,8 +878,8 @@
      */
     data class FlingConfig internal constructor(
         internal var friction: Float,
-        internal var min: Float,
-        internal var max: Float,
+        var min: Float,
+        var max: Float,
         internal var startVelocity: Float
     ) {
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 39510e5..4e19e06 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1478,6 +1478,9 @@
      * Update bubble order and pointer position.
      */
     public void updateBubbleOrder(List<Bubble> bubbles) {
+        if (isExpansionAnimating()) {
+            return;
+        }
         final Runnable reorder = () -> {
             for (int i = 0; i < bubbles.size(); i++) {
                 Bubble bubble = bubbles.get(i);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 8fb358a..b91ba34 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -64,6 +64,7 @@
     private static final String TAG = "PipResizeGestureHandler";
     private static final int PINCH_RESIZE_SNAP_DURATION = 250;
     private static final int PINCH_RESIZE_MAX_ANGLE_ROTATION = 45;
+    private static final float PINCH_RESIZE_AUTO_MAX_RATIO = 0.9f;
 
     private final Context mContext;
     private final PipBoundsAlgorithm mPipBoundsAlgorithm;
@@ -539,6 +540,11 @@
             // position correctly. Drag-resize does not need to move, so just finalize resize.
             if (mUsingPinchToZoom) {
                 final Rect startBounds = new Rect(mLastResizeBounds);
+                // If user resize is pretty close to max size, just auto resize to max.
+                if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x
+                        || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) {
+                    mLastResizeBounds.set(0, 0, mMaxSize.x, mMaxSize.y);
+                }
                 mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds,
                         mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds()));
                 mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index dca2732..c0ab20d 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -15,7 +15,10 @@
 android_test {
     name: "WMShellUnitTests",
 
-    srcs: ["**/*.java"],
+    srcs: [
+        "**/*.java",
+        "**/*.kt",
+    ],
 
     static_libs: [
         "WindowManager-Shell",
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
index 4bd9bed..17ed396 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/animation/PhysicsAnimatorTest.kt
@@ -26,7 +26,7 @@
 import androidx.dynamicanimation.animation.FloatPropertyCompat
 import androidx.dynamicanimation.animation.SpringForce
 import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
+import com.android.wm.shell.ShellTestCase
 import com.android.wm.shell.animation.PhysicsAnimator.EndListener
 import com.android.wm.shell.animation.PhysicsAnimator.UpdateListener
 import com.android.wm.shell.animation.PhysicsAnimatorTestUtils.clearAnimationUpdateFrames
@@ -54,8 +54,7 @@
 @TestableLooper.RunWithLooper
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
-@Ignore("Blocking presubmits - investigating in b/158697054")
-class PhysicsAnimatorTest : SysuiTestCase() {
+class PhysicsAnimatorTest : ShellTestCase() {
     private lateinit var viewGroup: ViewGroup
     private lateinit var testView: View
     private lateinit var testView2: View
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
index 4fab9a5..dd1a6a5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/storage/BubbleVolatileRepositoryTest.kt
@@ -20,12 +20,12 @@
 import android.os.UserHandle
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import com.android.systemui.util.mockito.eq
 import com.android.wm.shell.ShellTestCase
 import junit.framework.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
index fe53641..9f1ee6c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/magnetictarget/MagnetizedObjectTest.kt
@@ -21,8 +21,8 @@
 import android.view.View
 import androidx.dynamicanimation.animation.FloatPropertyCompat
 import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.animation.PhysicsAnimatorTestUtils
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.animation.PhysicsAnimatorTestUtils
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -43,7 +43,7 @@
 @TestableLooper.RunWithLooper
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
-class MagnetizedObjectTest : SysuiTestCase() {
+class MagnetizedObjectTest : ShellTestCase() {
     /** Incrementing value for fake MotionEvent timestamps. */
     private var time = 0L
 
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index b944310..b769d40 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -79,7 +79,8 @@
 
 // Regular JNI
 static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jobject buffer,
-        jstring filePath, jint weight, jboolean italic, jint ttcIndex) {
+                                jstring filePath, jstring langTags, jint weight, jboolean italic,
+                                jint ttcIndex) {
     NPE_CHECK_RETURN_ZERO(env, buffer);
     std::unique_ptr<NativeFontBuilder> builder(toBuilder(builderPtr));
     const void* fontPtr = env->GetDirectBufferAddress(buffer);
@@ -94,6 +95,7 @@
         return 0;
     }
     ScopedUtfChars fontPath(env, filePath);
+    ScopedUtfChars langTagStr(env, langTags);
     jobject fontRef = MakeGlobalRefOrDie(env, buffer);
     sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
             release_global_ref, reinterpret_cast<void*>(fontRef)));
@@ -105,8 +107,13 @@
                           "Failed to create internal object. maybe invalid font data.");
         return 0;
     }
-    std::shared_ptr<minikin::Font> font = minikin::Font::Builder(minikinFont).setWeight(weight)
-                    .setSlant(static_cast<minikin::FontStyle::Slant>(italic)).build();
+    uint32_t localeListId = minikin::registerLocaleList(langTagStr.c_str());
+    std::shared_ptr<minikin::Font> font =
+            minikin::Font::Builder(minikinFont)
+                    .setWeight(weight)
+                    .setSlant(static_cast<minikin::FontStyle::Slant>(italic))
+                    .setLocaleListId(localeListId)
+                    .build();
     return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
 }
 
@@ -302,11 +309,12 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 static const JNINativeMethod gFontBuilderMethods[] = {
-    { "nInitBuilder", "()J", (void*) Font_Builder_initBuilder },
-    { "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis },
-    { "nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;IZI)J", (void*) Font_Builder_build },
-    { "nClone", "(JJIZI)J", (void*) Font_Builder_clone },
-    { "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont },
+        {"nInitBuilder", "()J", (void*)Font_Builder_initBuilder},
+        {"nAddAxis", "(JIF)V", (void*)Font_Builder_addAxis},
+        {"nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;Ljava/lang/String;IZI)J",
+         (void*)Font_Builder_build},
+        {"nClone", "(JJIZI)J", (void*)Font_Builder_clone},
+        {"nGetReleaseNativeFont", "()J", (void*)Font_Builder_getReleaseNativeFont},
 };
 
 static const JNINativeMethod gFontMethods[] = {
diff --git a/libs/hwui/jni/fonts/FontFamily.cpp b/libs/hwui/jni/fonts/FontFamily.cpp
index 37e5276..a07723f 100644
--- a/libs/hwui/jni/fonts/FontFamily.cpp
+++ b/libs/hwui/jni/fonts/FontFamily.cpp
@@ -83,6 +83,23 @@
     return reinterpret_cast<jlong>(releaseFontFamily);
 }
 
+// FastNative
+static jstring FontFamily_getLangTags(JNIEnv* env, jobject, jlong familyPtr) {
+    FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
+    uint32_t localeListId = family->family->localeListId();
+    if (localeListId == 0) {
+        return nullptr;
+    }
+    std::string langTags = minikin::getLocaleString(localeListId);
+    return env->NewStringUTF(langTags.c_str());
+}
+
+// CriticalNative
+static jint FontFamily_getVariant(jlong familyPtr) {
+    FontFamilyWrapper* family = reinterpret_cast<FontFamilyWrapper*>(familyPtr);
+    return static_cast<jint>(family->family->variant());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static const JNINativeMethod gFontFamilyBuilderMethods[] = {
@@ -93,9 +110,16 @@
     { "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc },
 };
 
+static const JNINativeMethod gFontFamilyMethods[] = {
+        {"nGetLangTags", "(J)Ljava/lang/String;", (void*)FontFamily_getLangTags},
+        {"nGetVariant", "(J)I", (void*)FontFamily_getVariant},
+};
+
 int register_android_graphics_fonts_FontFamily(JNIEnv* env) {
     return RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily$Builder",
-            gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods));
+                                gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods)) +
+           RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily", gFontFamilyMethods,
+                                NELEM(gFontFamilyMethods));
 }
 
 }
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index d248f61..7837d7e 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -45,6 +45,7 @@
             case ImageFormat.YV12:
             case ImageFormat.YUV_420_888:
             case ImageFormat.NV21:
+            case ImageFormat.YCBCR_P010:
                 return 3;
             case ImageFormat.NV16:
                 return 2;
@@ -225,6 +226,7 @@
             case ImageFormat.RAW_SENSOR:
             case ImageFormat.RAW_PRIVATE: // round estimate, real size is unknown
             case ImageFormat.DEPTH16:
+            case ImageFormat.YCBCR_P010:
                 estimatedBytePerPixel = 2.0;
                 break;
             case PixelFormat.RGB_888:
@@ -244,6 +246,7 @@
 
     private static Size getEffectivePlaneSizeForImage(Image image, int planeIdx) {
         switch (image.getFormat()) {
+            case ImageFormat.YCBCR_P010:
             case ImageFormat.YV12:
             case ImageFormat.YUV_420_888:
             case ImageFormat.NV21:
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index b6c47fca..ecd9cc1 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -69,6 +69,7 @@
         case HAL_PIXEL_FORMAT_RAW_OPAQUE:
         case HAL_PIXEL_FORMAT_BLOB:
         case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+        case HAL_PIXEL_FORMAT_YCBCR_P010:
             return false;
 
         case HAL_PIXEL_FORMAT_YV12:
@@ -261,6 +262,32 @@
             pStride = 1;
             rStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16);
             break;
+        case HAL_PIXEL_FORMAT_YCBCR_P010:
+            if (buffer->height % 2 != 0) {
+                ALOGE("YCBCR_P010: height (%d) should be a multiple of 2", buffer->height);
+                return BAD_VALUE;
+            }
+
+            if (buffer->width <= 0) {
+                ALOGE("YCBCR_P010: width (%d) should be a > 0", buffer->width);
+                return BAD_VALUE;
+            }
+
+            if (buffer->height <= 0) {
+                ALOGE("YCBCR_P010: height (%d) should be a > 0", buffer->height);
+                return BAD_VALUE;
+            }
+
+            ySize = (buffer->stride * 2) * buffer->height;
+            cSize = ySize / 2;
+            pStride = (idx == 0) ? 2 : 4;
+            cb = buffer->data + ySize;
+            cr = cb + 2;
+
+            pData = (idx == 0) ?  buffer->data : (idx == 1) ?  cb : cr;
+            dataSize = (idx == 0) ? ySize : cSize;
+            rStride = buffer->stride * 2;
+            break;
         case HAL_PIXEL_FORMAT_Y8:
             // Single plane, 8bpp.
             LOG_ALWAYS_FATAL_IF(idx != 0, "Wrong index: %d", idx);
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 9ec84d9..eee9f1e 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -3786,7 +3786,7 @@
         jniThrowRuntimeException(env, "Failed to GetByteArrayElements");
         return -1;
     }
-    int realReadSize = filterClient->read(reinterpret_cast<uint8_t*>(dst) + offset, size);
+    int realReadSize = filterClient->read(reinterpret_cast<int8_t*>(dst) + offset, size);
     env->ReleaseByteArrayElements(buffer, dst, 0);
     return (jint) realReadSize;
 }
diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp
index 1a2f8c0..748d458 100644
--- a/media/jni/tuner/DemuxClient.cpp
+++ b/media/jni/tuner/DemuxClient.cpp
@@ -88,12 +88,19 @@
 }
 
 sp<TimeFilterClient> DemuxClient::openTimeFilter() {
-    // TODO: pending aidl interface
+    if (mTunerDemux != NULL) {
+        shared_ptr<ITunerTimeFilter> tunerTimeFilter;
+        Status s = mTunerDemux->openTimeFilter(&tunerTimeFilter);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return NULL;
+        }
+        return new TimeFilterClient(tunerTimeFilter);
+    }
 
     if (mDemux != NULL) {
         sp<ITimeFilter> hidlTimeFilter = openHidlTimeFilter();
         if (hidlTimeFilter != NULL) {
-            sp<TimeFilterClient> timeFilterClient = new TimeFilterClient();
+            sp<TimeFilterClient> timeFilterClient = new TimeFilterClient(NULL);
             timeFilterClient->setHidlTimeFilter(hidlTimeFilter);
             return timeFilterClient;
         }
@@ -103,7 +110,14 @@
 }
 
 int DemuxClient::getAvSyncHwId(sp<FilterClient> filterClient) {
-    // pending aidl interface
+    if (mTunerDemux != NULL) {
+        int hwId;
+        Status s = mTunerDemux->getAvSyncHwId(filterClient->getAidlFilter(), &hwId);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return INVALID_AV_SYNC_HW_ID;
+        }
+        return hwId;
+    }
 
     if (mDemux != NULL) {
         uint32_t avSyncHwId;
@@ -119,11 +133,18 @@
         }
     }
 
-    return -1;
+    return INVALID_AV_SYNC_HW_ID;
 }
 
 long DemuxClient::getAvSyncTime(int avSyncHwId) {
-    // pending aidl interface
+    if (mTunerDemux != NULL) {
+        int64_t time;
+        Status s = mTunerDemux->getAvSyncTime(avSyncHwId, &time);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return INVALID_AV_SYNC_TIME;
+        }
+        return time;
+    }
 
     if (mDemux != NULL) {
         uint64_t time;
@@ -138,7 +159,7 @@
         }
     }
 
-    return -1;
+    return INVALID_AV_SYNC_TIME;
 }
 
 sp<DvrClient> DemuxClient::openDvr(DvrType dvbType, int bufferSize, sp<DvrClientCallback> cb) {
@@ -167,7 +188,10 @@
 }
 
 Result DemuxClient::connectCiCam(int ciCamId) {
-    // pending aidl interface
+    if (mTunerDemux != NULL) {
+        Status s = mTunerDemux->connectCiCam(ciCamId);
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mDemux != NULL) {
         return mDemux->connectCiCam(static_cast<uint32_t>(ciCamId));
@@ -177,7 +201,10 @@
 }
 
 Result DemuxClient::disconnectCiCam() {
-    // pending aidl interface
+    if (mTunerDemux != NULL) {
+        Status s = mTunerDemux->disconnectCiCam();
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mDemux != NULL) {
         return mDemux->disconnectCiCam();
diff --git a/media/jni/tuner/DemuxClient.h b/media/jni/tuner/DemuxClient.h
index 463944a..31eb35a 100644
--- a/media/jni/tuner/DemuxClient.h
+++ b/media/jni/tuner/DemuxClient.h
@@ -31,7 +31,9 @@
 
 using Status = ::ndk::ScopedAStatus;
 using ::aidl::android::media::tv::tuner::ITunerDemux;
+using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
 
+using ::android::hardware::tv::tuner::V1_0::IDemux;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
 using ::android::hardware::tv::tuner::V1_0::DvrType;
 using ::android::hardware::tv::tuner::V1_0::IDemux;
@@ -39,6 +41,9 @@
 
 using namespace std;
 
+const int64_t INVALID_AV_SYNC_TIME = -1;
+const int INVALID_AV_SYNC_HW_ID = -1;
+
 namespace android {
 
 struct DemuxClient : public RefBase {
diff --git a/media/jni/tuner/FilterClient.cpp b/media/jni/tuner/FilterClient.cpp
index ca9eb46..8b4ca37 100644
--- a/media/jni/tuner/FilterClient.cpp
+++ b/media/jni/tuner/FilterClient.cpp
@@ -18,6 +18,7 @@
 
 #include <aidlcommonsupport/NativeHandle.h>
 #include <android-base/logging.h>
+#include <fmq/ConvertMQDescriptors.h>
 #include <utils/Log.h>
 
 #include "FilterClient.h"
@@ -68,18 +69,12 @@
     mFilter_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(mFilter);
 }
 
-int FilterClient::read(uint8_t* buffer, int size) {
-    // TODO: pending aidl interface
-
-    if (mFilter != NULL) {
-        Result res = getFilterMq();
-        if (res != Result::SUCCESS) {
-            return -1;
-        }
-        return copyData(buffer, size);
+int FilterClient::read(int8_t* buffer, int size) {
+    Result res = getFilterMq();
+    if (res != Result::SUCCESS) {
+        return -1;
     }
-
-    return -1;
+    return copyData(buffer, size);
 }
 
 SharedHandleInfo FilterClient::getAvSharedHandleInfo() {
@@ -106,7 +101,10 @@
 }
 
 Result FilterClient::configureMonitorEvent(int monitorEventType) {
-    // TODO: pending aidl interface
+    if (mTunerFilter != NULL) {
+        Status s = mTunerFilter->configureMonitorEvent(monitorEventType);
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mFilter_1_1 != NULL) {
         return mFilter_1_1->configureMonitorEvent(monitorEventType);
@@ -116,7 +114,10 @@
 }
 
 Result FilterClient::configureIpFilterContextId(int cid) {
-    // TODO: pending aidl interface
+    if (mTunerFilter != NULL) {
+        Status s = mTunerFilter->configureIpFilterContextId(cid);
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mFilter_1_1 != NULL) {
         return mFilter_1_1->configureIpCid(cid);
@@ -126,7 +127,19 @@
 }
 
 Result FilterClient::configureAvStreamType(AvStreamType avStreamType) {
-    // TODO: pending aidl interface
+    if (mTunerFilter != NULL) {
+        int type;
+        switch (avStreamType.getDiscriminator()) {
+            case AvStreamType::hidl_discriminator::audio:
+                type = (int)avStreamType.audio();
+                break;
+            case AvStreamType::hidl_discriminator::video:
+                type = (int)avStreamType.video();
+                break;
+        }
+        Status s = mTunerFilter->configureAvStreamType(type);
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mFilter_1_1 != NULL) {
         return mFilter_1_1->configureAvStreamType(avStreamType);
@@ -228,7 +241,10 @@
 }
 
 Result FilterClient::setDataSource(sp<FilterClient> filterClient){
-    // TODO: pending aidl interface
+    if (mTunerFilter != NULL) {
+        Status s = mTunerFilter->setDataSource(filterClient->getAidlFilter());
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mFilter != NULL) {
         sp<IFilter> sourceFilter = filterClient->getHalFilter();
@@ -891,29 +907,43 @@
 }
 
 Result FilterClient::getFilterMq() {
-    if (mFilter == NULL) {
-        return Result::INVALID_STATE;
-    }
-
     if (mFilterMQ != NULL) {
         return Result::SUCCESS;
     }
 
-    Result getQueueDescResult = Result::UNKNOWN_ERROR;
-    MQDescriptorSync<uint8_t> filterMQDesc;
-    mFilter->getQueueDesc(
-            [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
-                filterMQDesc = desc;
-                getQueueDescResult = r;
-            });
-    if (getQueueDescResult == Result::SUCCESS) {
-        mFilterMQ = std::make_unique<MQ>(filterMQDesc, true);
-        EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
+    AidlMQDesc aidlMqDesc;
+    Result res = Result::UNAVAILABLE;
+
+    if (mTunerFilter != NULL) {
+        Status s = mTunerFilter->getQueueDesc(&aidlMqDesc);
+        res = ClientHelper::getServiceSpecificErrorCode(s);
+        if (res == Result::SUCCESS) {
+            mFilterMQ = new (nothrow) AidlMQ(aidlMqDesc);
+            EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
+        }
+        return res;
     }
-    return getQueueDescResult;
+
+    if (mFilter != NULL) {
+        MQDescriptorSync<uint8_t> filterMQDesc;
+        mFilter->getQueueDesc(
+                [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
+                    filterMQDesc = desc;
+                    res = r;
+                });
+        if (res == Result::SUCCESS) {
+            AidlMQDesc aidlMQDesc;
+            unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, SynchronizedReadWrite>(
+                    filterMQDesc,  &aidlMQDesc);
+            mFilterMQ = new (nothrow) AidlMessageQueue(aidlMQDesc);
+            EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag);
+        }
+    }
+
+    return res;
 }
 
-int FilterClient::copyData(uint8_t* buffer, int size) {
+int FilterClient::copyData(int8_t* buffer, int size) {
     if (mFilter == NULL || mFilterMQ == NULL || mFilterMQEventFlag == NULL) {
         return -1;
     }
diff --git a/media/jni/tuner/FilterClient.h b/media/jni/tuner/FilterClient.h
index 21919ac..bbabc28 100644
--- a/media/jni/tuner/FilterClient.h
+++ b/media/jni/tuner/FilterClient.h
@@ -25,12 +25,14 @@
 #include <android/hardware/tv/tuner/1.1/IFilter.h>
 #include <android/hardware/tv/tuner/1.1/IFilterCallback.h>
 #include <android/hardware/tv/tuner/1.1/types.h>
+#include <fmq/AidlMessageQueue.h>
 #include <fmq/MessageQueue.h>
 
 #include "ClientHelper.h"
 #include "FilterClientCallback.h"
 
 using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
 using ::aidl::android::media::tv::tuner::BnTunerFilterCallback;
 using ::aidl::android::media::tv::tuner::ITunerFilter;
 using ::aidl::android::media::tv::tuner::TunerDemuxIpAddress;
@@ -69,10 +71,13 @@
 
 using namespace std;
 
-using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
-
 namespace android {
 
+using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using MQDesc = MQDescriptorSync<uint8_t>;
+using AidlMQ = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
+using AidlMQDesc = MQDescriptor<int8_t, SynchronizedReadWrite>;
+
 struct SharedHandleInfo {
     native_handle_t* sharedHandle;
     uint64_t size;
@@ -139,7 +144,7 @@
      *
      * @return the actual reading size. -1 if failed to read.
      */
-    int read(uint8_t* buffer, int size);
+    int read(int8_t* buffer, int size);
 
     /**
      * Get the a/v shared memory handle information
@@ -234,7 +239,7 @@
     void getAidlIpAddress(DemuxIpAddress ipAddr,
             TunerDemuxIpAddress& srcIpAddress, TunerDemuxIpAddress& dstIpAddress);
     Result getFilterMq();
-    int copyData(uint8_t* buffer, int size);
+    int copyData(int8_t* buffer, int size);
     void checkIsMediaFilter(DemuxFilterType type);
     void handleAvShareMemory();
     void closeAvSharedMemory();
@@ -259,7 +264,7 @@
      */
     sp<::android::hardware::tv::tuner::V1_1::IFilter> mFilter_1_1;
 
-    unique_ptr<MQ> mFilterMQ;
+    AidlMQ* mFilterMQ;
     EventFlag* mFilterMQEventFlag;
 
     sp<FilterClientCallback> mCallback;
diff --git a/media/jni/tuner/TimeFilterClient.cpp b/media/jni/tuner/TimeFilterClient.cpp
index 27ea6e5..432238d 100644
--- a/media/jni/tuner/TimeFilterClient.cpp
+++ b/media/jni/tuner/TimeFilterClient.cpp
@@ -19,6 +19,7 @@
 #include <android-base/logging.h>
 #include <utils/Log.h>
 
+#include "ClientHelper.h"
 #include "TimeFilterClient.h"
 
 using ::android::hardware::tv::tuner::V1_0::Result;
@@ -28,13 +29,12 @@
 
 /////////////// TimeFilterClient ///////////////////////
 
-// TODO: pending aidl interface
-TimeFilterClient::TimeFilterClient() {
-    //mTunerTimeFilter = tunerTimeFilter;
+TimeFilterClient::TimeFilterClient(shared_ptr<ITunerTimeFilter> tunerTimeFilter) {
+    mTunerTimeFilter = tunerTimeFilter;
 }
 
 TimeFilterClient::~TimeFilterClient() {
-    //mTunerTimeFilter = NULL;
+    mTunerTimeFilter = NULL;
     mTimeFilter = NULL;
 }
 
@@ -44,7 +44,10 @@
 }
 
 Result TimeFilterClient::setTimeStamp(long timeStamp) {
-    // TODO: pending aidl interface
+    if (mTunerTimeFilter != NULL) {
+        Status s = mTunerTimeFilter->setTimeStamp(timeStamp);
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mTimeFilter != NULL) {
         return mTimeFilter->setTimeStamp(timeStamp);
@@ -54,7 +57,10 @@
 }
 
 Result TimeFilterClient::clearTimeStamp() {
-    // TODO: pending aidl interface
+    if (mTunerTimeFilter != NULL) {
+        Status s = mTunerTimeFilter->clearTimeStamp();
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mTimeFilter != NULL) {
         return mTimeFilter->clearTimeStamp();
@@ -64,7 +70,14 @@
 }
 
 long TimeFilterClient::getTimeStamp() {
-    // TODO: pending aidl interface
+    if (mTunerTimeFilter != NULL) {
+        int64_t timeStamp;
+        Status s = mTunerTimeFilter->getTimeStamp(&timeStamp);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+        }
+        return timeStamp;
+    }
 
     if (mTimeFilter != NULL) {
         Result res;
@@ -84,27 +97,37 @@
 }
 
 long TimeFilterClient::getSourceTime() {
-    // TODO: pending aidl interface
+    if (mTunerTimeFilter != NULL) {
+        int64_t sourceTime;
+        Status s = mTunerTimeFilter->getTimeStamp(&sourceTime);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
+        }
+        return sourceTime;
+    }
 
     if (mTimeFilter != NULL) {
         Result res;
-        long timestamp;
+        long sourceTime;
         mTimeFilter->getSourceTime(
                 [&](Result r, uint64_t t) {
                     res = r;
-                    timestamp = t;
+                    sourceTime = t;
                 });
         if (res != Result::SUCCESS) {
             return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
         }
-        return timestamp;
+        return sourceTime;
     }
 
     return (long)Constant64Bit::INVALID_PRESENTATION_TIME_STAMP;
 }
 
 Result TimeFilterClient::close() {
-    // TODO: pending aidl interface
+    if (mTunerTimeFilter != NULL) {
+        Status s = mTunerTimeFilter->close();
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
 
     if (mTimeFilter != NULL) {
         return mTimeFilter->close();
diff --git a/media/jni/tuner/TimeFilterClient.h b/media/jni/tuner/TimeFilterClient.h
index 9a9d172..56ddd68 100644
--- a/media/jni/tuner/TimeFilterClient.h
+++ b/media/jni/tuner/TimeFilterClient.h
@@ -17,12 +17,13 @@
 #ifndef _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_
 #define _ANDROID_MEDIA_TV_TIME_FILTER_CLIENT_H_
 
-//#include <aidl/android/media/tv/tuner/ITunerTimeFilter.h>
+#include <aidl/android/media/tv/tuner/ITunerTimeFilter.h>
 #include <android/hardware/tv/tuner/1.0/ITimeFilter.h>
 #include <android/hardware/tv/tuner/1.1/types.h>
 
-//using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
+using ::aidl::android::media::tv::tuner::ITunerTimeFilter;
 
+using Status = ::ndk::ScopedAStatus;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 using ::android::hardware::hidl_vec;
@@ -36,8 +37,7 @@
 struct TimeFilterClient : public RefBase {
 
 public:
-    // TODO: add TunerTimeFilter as parameter.
-    TimeFilterClient();
+    TimeFilterClient(shared_ptr<ITunerTimeFilter> tunerTimeFilter);
     ~TimeFilterClient();
 
     // TODO: remove after migration to Tuner Service is done.
@@ -73,8 +73,7 @@
      * An AIDL Tuner TimeFilter Singleton assigned at the first time the Tuner Client
      * opens an TimeFilter. Default null when time filter is not opened.
      */
-    // TODO: pending on aidl interface
-    //shared_ptr<ITunerTimeFilter> mTunerTimeFilter;
+    shared_ptr<ITunerTimeFilter> mTunerTimeFilter;
 
     /**
      * A TimeFilter HAL interface that is ready before migrating to the TunerTimeFilter.
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index 14393a1..a604490d 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -200,7 +200,14 @@
 }
 
 shared_ptr<DemuxCapabilities> TunerClient::getDemuxCaps() {
-    // pending aidl interface
+    if (mTunerService != NULL) {
+        TunerDemuxCapabilities aidlCaps;
+        Status s = mTunerService->getDemuxCaps(&aidlCaps);
+        if (ClientHelper::getServiceSpecificErrorCode(s) != Result::SUCCESS) {
+            return NULL;
+        }
+        return make_shared<DemuxCapabilities>(getHidlDemuxCaps(aidlCaps));
+    }
 
     if (mTuner != NULL) {
         Result res;
@@ -459,6 +466,26 @@
     return descrambler;
 }
 
+DemuxCapabilities TunerClient::getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps) {
+    DemuxCapabilities caps{
+        .numDemux = (uint32_t)aidlCaps.numDemux,
+        .numRecord = (uint32_t)aidlCaps.numRecord,
+        .numPlayback = (uint32_t)aidlCaps.numPlayback,
+        .numTsFilter = (uint32_t)aidlCaps.numTsFilter,
+        .numSectionFilter = (uint32_t)aidlCaps.numSectionFilter,
+        .numAudioFilter = (uint32_t)aidlCaps.numAudioFilter,
+        .numVideoFilter = (uint32_t)aidlCaps.numVideoFilter,
+        .numPesFilter = (uint32_t)aidlCaps.numPesFilter,
+        .numPcrFilter = (uint32_t)aidlCaps.numPcrFilter,
+        .numBytesInSectionFilter = (uint32_t)aidlCaps.numBytesInSectionFilter,
+        .filterCaps = (uint32_t)aidlCaps.filterCaps,
+        .bTimeFilter = aidlCaps.bTimeFilter,
+    };
+    caps.linkCaps.resize(aidlCaps.linkCaps.size());
+    copy(aidlCaps.linkCaps.begin(), aidlCaps.linkCaps.end(), caps.linkCaps.begin());
+    return caps;
+}
+
 FrontendInfo TunerClient::FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo) {
     FrontendInfo hidlFrontendInfo {
         .type = static_cast<FrontendType>(aidlFrontendInfo.type),
diff --git a/media/jni/tuner/TunerClient.h b/media/jni/tuner/TunerClient.h
index 6ce6661..acd018e 100644
--- a/media/jni/tuner/TunerClient.h
+++ b/media/jni/tuner/TunerClient.h
@@ -32,6 +32,7 @@
 
 using Status = ::ndk::ScopedAStatus;
 
+using ::aidl::android::media::tv::tuner::TunerDemuxCapabilities;
 using ::aidl::android::media::tv::tuner::ITunerService;
 using ::aidl::android::media::tv::tuner::TunerFrontendInfo;
 using ::aidl::android::media::tv::tunerresourcemanager::ITunerResourceManager;
@@ -145,6 +146,7 @@
     sp<ILnb> openHidlLnbByName(string name, LnbId& lnbId);
     sp<IDescrambler> openHidlDescrambler();
     vector<int> getLnbHandles();
+    DemuxCapabilities getHidlDemuxCaps(TunerDemuxCapabilities& aidlCaps);
     FrontendInfo FrontendInfoAidlToHidl(TunerFrontendInfo aidlFrontendInfo);
     void updateTunerResources();
     void updateFrontendResources();
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 2716e09..fa3213d 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -3231,32 +3231,6 @@
         }
     }
 
-    /** {@hide} - returns the factory serial number */
-    @UnsupportedAppUsage
-    @RequiresPermission(anyOf = {
-            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
-            android.Manifest.permission.NETWORK_FACTORY})
-    public int registerNetworkFactory(Messenger messenger, String name) {
-        try {
-            return mService.registerNetworkFactory(messenger, name);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /** {@hide} */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    @RequiresPermission(anyOf = {
-            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
-            android.Manifest.permission.NETWORK_FACTORY})
-    public void unregisterNetworkFactory(Messenger messenger) {
-        try {
-            mService.unregisterNetworkFactory(messenger);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
     /**
      * Registers the specified {@link NetworkProvider}.
      * Each listener must only be registered once. The listener can be unregistered with
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index 1b4d2e4..db8b7ed 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -156,9 +156,6 @@
 
     boolean requestBandwidthUpdate(in Network network);
 
-    int registerNetworkFactory(in Messenger messenger, in String name);
-    void unregisterNetworkFactory(in Messenger messenger);
-
     int registerNetworkProvider(in Messenger messenger, in String name);
     void unregisterNetworkProvider(in Messenger messenger);
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 44864a6..a6e2af9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -42,6 +42,7 @@
 import android.provider.settings.validators.SecureSettingsValidators;
 import android.provider.settings.validators.SystemSettingsValidators;
 import android.provider.settings.validators.Validator;
+import android.telephony.SubscriptionManager;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.BackupUtils;
@@ -95,10 +96,11 @@
     private static final String KEY_NETWORK_POLICIES = "network_policies";
     private static final String KEY_WIFI_NEW_CONFIG = "wifi_new_config";
     private static final String KEY_DEVICE_SPECIFIC_CONFIG = "device_specific_config";
+    private static final String KEY_SIM_SPECIFIC_SETTINGS = "sim_specific_settings";
 
     // Versioning of the state file.  Increment this version
     // number any time the set of state items is altered.
-    private static final int STATE_VERSION = 8;
+    private static final int STATE_VERSION = 9;
 
     // Versioning of the Network Policies backup payload.
     private static final int NETWORK_POLICIES_BACKUP_VERSION = 1;
@@ -106,19 +108,20 @@
 
     // Slots in the checksum array.  Never insert new items in the middle
     // of this array; new slots must be appended.
-    private static final int STATE_SYSTEM           = 0;
-    private static final int STATE_SECURE           = 1;
-    private static final int STATE_LOCALE           = 2;
-    private static final int STATE_WIFI_SUPPLICANT  = 3;
-    private static final int STATE_WIFI_CONFIG      = 4;
-    private static final int STATE_GLOBAL           = 5;
-    private static final int STATE_LOCK_SETTINGS    = 6;
-    private static final int STATE_SOFTAP_CONFIG    = 7;
-    private static final int STATE_NETWORK_POLICIES = 8;
-    private static final int STATE_WIFI_NEW_CONFIG  = 9;
-    private static final int STATE_DEVICE_CONFIG    = 10;
+    private static final int STATE_SYSTEM                = 0;
+    private static final int STATE_SECURE                = 1;
+    private static final int STATE_LOCALE                = 2;
+    private static final int STATE_WIFI_SUPPLICANT       = 3;
+    private static final int STATE_WIFI_CONFIG           = 4;
+    private static final int STATE_GLOBAL                = 5;
+    private static final int STATE_LOCK_SETTINGS         = 6;
+    private static final int STATE_SOFTAP_CONFIG         = 7;
+    private static final int STATE_NETWORK_POLICIES      = 8;
+    private static final int STATE_WIFI_NEW_CONFIG       = 9;
+    private static final int STATE_DEVICE_CONFIG         = 10;
+    private static final int STATE_SIM_SPECIFIC_SETTINGS = 11;
 
-    private static final int STATE_SIZE             = 11; // The current number of state items
+    private static final int STATE_SIZE                  = 12; // The current number of state items
 
     // Number of entries in the checksum array at various version numbers
     private static final int STATE_SIZES[] = {
@@ -130,7 +133,8 @@
             8,              // version 5 added STATE_SOFTAP_CONFIG
             9,              // version 6 added STATE_NETWORK_POLICIES
             10,             // version 7 added STATE_WIFI_NEW_CONFIG
-            STATE_SIZE      // version 8 added STATE_DEVICE_CONFIG
+            11,             // version 8 added STATE_DEVICE_CONFIG
+            STATE_SIZE      // version 9 added STATE_SIM_SPECIFIC_SETTINGS
     };
 
     private static final int FULL_BACKUP_ADDED_GLOBAL = 2;  // added the "global" entry
@@ -218,6 +222,7 @@
         byte[] netPoliciesData = getNetworkPolicies();
         byte[] wifiFullConfigData = getNewWifiConfigData();
         byte[] deviceSpecificInformation = getDeviceSpecificConfiguration();
+        byte[] simSpecificSettingsData = getSimSpecificSettingsData();
 
         long[] stateChecksums = readOldChecksums(oldState);
 
@@ -246,6 +251,9 @@
         stateChecksums[STATE_DEVICE_CONFIG] =
                 writeIfChanged(stateChecksums[STATE_DEVICE_CONFIG], KEY_DEVICE_SPECIFIC_CONFIG,
                         deviceSpecificInformation, data);
+        stateChecksums[STATE_SIM_SPECIFIC_SETTINGS] =
+                writeIfChanged(stateChecksums[STATE_SIM_SPECIFIC_SETTINGS],
+                        KEY_SIM_SPECIFIC_SETTINGS, simSpecificSettingsData, data);
 
         writeNewChecksums(stateChecksums, newState);
     }
@@ -386,6 +394,12 @@
                             preservedSettings);
                     break;
 
+                case KEY_SIM_SPECIFIC_SETTINGS:
+                    byte[] restoredSimSpecificSettings = new byte[size];
+                    data.readEntityData(restoredSimSpecificSettings, 0, size);
+                    restoreSimSpecificSettings(restoredSimSpecificSettings);
+                    break;
+
                 default :
                     data.skipEntityData();
 
@@ -1189,6 +1203,20 @@
         return true;
     }
 
+    private byte[] getSimSpecificSettingsData() {
+        SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
+        byte[] simSpecificData = subManager.getAllSimSpecificSettingsForBackup();
+        Log.i(TAG, "sim specific data of length + " + simSpecificData.length
+                + " successfully retrieved");
+
+        return simSpecificData;
+    }
+
+    private void restoreSimSpecificSettings(byte[] data) {
+        SubscriptionManager subManager = SubscriptionManager.from(getBaseContext());
+        subManager.restoreAllSimSpecificSettingsFromBackup(data);
+    }
+
     private void updateWindowManagerIfNeeded(Integer previousDensity) {
         int newDensity;
         try {
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index 6dff2e3..a6321fe 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -19,4 +19,7 @@
 
     <!-- Max number of columns for quick controls area -->
     <integer name="controls_max_columns">2</integer>
+
+    <!-- Whether to use the split 2-column notification shade -->
+    <bool name="config_use_split_notification_shade">true</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 44eeba1..bb04c3b 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -565,4 +565,7 @@
 
     <!-- Whether wallet view is shown in landscape / seascape orientations -->
     <bool name="global_actions_show_landscape_wallet_view">false</bool>
+
+    <!-- Whether to use the split 2-column notification shade -->
+    <bool name="config_use_split_notification_shade">false</bool>
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 3584c82..e2ca349 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -72,9 +72,13 @@
         return ActivityOptions.makeRemoteTransition(remoteTransition.getTransition());
     }
 
+    /**
+     * Returns ActivityOptions for overriding task transition animation.
+     */
     public static ActivityOptions makeCustomAnimation(Context context, int enterResId,
             int exitResId, final Runnable callback, final Handler callbackHandler) {
-        return ActivityOptions.makeCustomAnimation(context, enterResId, exitResId, callbackHandler,
+        return ActivityOptions.makeCustomTaskAnimation(context, enterResId, exitResId,
+                callbackHandler,
                 new ActivityOptions.OnAnimationStartedListener() {
                     @Override
                     public void onAnimationStarted() {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
index a56c6a1..e6477f1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java
@@ -18,9 +18,11 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.view.WindowManager.TransitionOldType;
 
 import android.os.RemoteException;
 import android.util.Log;
@@ -65,13 +67,17 @@
             final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
         return new IRemoteAnimationRunner.Stub() {
             @Override
-            public void onAnimationStart(RemoteAnimationTarget[] apps,
+            public void onAnimationStart(@TransitionOldType int transit,
+                    RemoteAnimationTarget[] apps,
                     RemoteAnimationTarget[] wallpapers,
+                    RemoteAnimationTarget[] nonApps,
                     final IRemoteAnimationFinishedCallback finishedCallback) {
                 final RemoteAnimationTargetCompat[] appsCompat =
                         RemoteAnimationTargetCompat.wrap(apps);
                 final RemoteAnimationTargetCompat[] wallpapersCompat =
                         RemoteAnimationTargetCompat.wrap(wallpapers);
+                final RemoteAnimationTargetCompat[] nonAppsCompat =
+                        RemoteAnimationTargetCompat.wrap(nonApps);
                 final Runnable animationFinishedCallback = new Runnable() {
                     @Override
                     public void run() {
@@ -83,8 +89,8 @@
                         }
                     }
                 };
-                remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat,
-                        animationFinishedCallback);
+                remoteAnimationAdapter.onAnimationStart(transit, appsCompat, wallpapersCompat,
+                        nonAppsCompat, animationFinishedCallback);
             }
 
             @Override
@@ -104,6 +110,9 @@
                         RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */);
                 final RemoteAnimationTargetCompat[] wallpapersCompat =
                         RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */);
+                // TODO(bc-unlock): Build wrapped object for non-apps target.
+                final RemoteAnimationTargetCompat[] nonAppsCompat =
+                        new RemoteAnimationTargetCompat[0];
                 final Runnable animationFinishedCallback = new Runnable() {
                     @Override
                     public void run() {
@@ -147,7 +156,10 @@
                     }
                 }
                 t.apply();
-                remoteAnimationAdapter.onAnimationStart(appsCompat, wallpapersCompat,
+                // TODO(bc-unlcok): Pass correct transit type.
+                remoteAnimationAdapter.onAnimationStart(
+                        TRANSIT_OLD_NONE,
+                        appsCompat, wallpapersCompat, nonAppsCompat,
                         animationFinishedCallback);
             }
         };
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
index 33372f6..0076292 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java
@@ -16,8 +16,11 @@
 
 package com.android.systemui.shared.system;
 
+import android.view.WindowManager;
+
 public interface RemoteAnimationRunnerCompat {
-    void onAnimationStart(RemoteAnimationTargetCompat[] apps,
-            RemoteAnimationTargetCompat[] wallpapers, Runnable finishedCallback);
+    void onAnimationStart(@WindowManager.TransitionOldType int transit,
+            RemoteAnimationTargetCompat[] apps, RemoteAnimationTargetCompat[] wallpapers,
+            RemoteAnimationTargetCompat[] nonApps, Runnable finishedCallback);
     void onAnimationCancelled();
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 85e9ca0..276036c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -74,16 +74,7 @@
 
         @Override
         public void onDisplayChanged(int displayId) {
-            if (displayId == DEFAULT_DISPLAY) return;
-            final Presentation presentation = mPresentations.get(displayId);
-            if (presentation != null && mShowing) {
-                hidePresentation(displayId);
-                // update DisplayInfo.
-                final Display display = mDisplayService.getDisplay(displayId);
-                if (display != null) {
-                    showPresentation(display);
-                }
-            }
+
         }
 
         @Override
@@ -305,15 +296,16 @@
         }
 
         @Override
+        public void onDisplayChanged() {
+            updateBounds();
+            getWindow().getDecorView().requestLayout();
+        }
+
+        @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
 
-            final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
-                    .getBounds();
-            mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
-            mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
-            mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
-            mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
+            updateBounds();
 
             setContentView(LayoutInflater.from(mContext)
                     .inflate(R.layout.keyguard_presentation, null));
@@ -338,5 +330,14 @@
 
             mKeyguardClockSwitchController.init();
         }
+
+        private void updateBounds() {
+            final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
+                    .getBounds();
+            mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
+            mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
+            mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
+            mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 1b033e9..17f7ccf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -17,7 +17,13 @@
 package com.android.systemui.keyguard;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE;
+import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
 
+import android.app.ActivityTaskManager;
 import android.app.Service;
 import android.content.Intent;
 import android.os.Binder;
@@ -26,8 +32,17 @@
 import android.os.IBinder;
 import android.os.PowerManager;
 import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.util.Log;
+import android.util.Slog;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
+import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
 import android.view.WindowManagerPolicyConstants;
 
 import com.android.internal.policy.IKeyguardDismissCallback;
@@ -43,6 +58,21 @@
     static final String TAG = "KeyguardService";
     static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
 
+    /**
+     * Run Keyguard animation as remote animation in System UI instead of local animation in
+     * the server process.
+     *
+     * Note: Must be consistent with WindowManagerService.
+     */
+    private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
+            "persist.wm.enable_remote_keyguard_animation";
+
+    /**
+     * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+     */
+    private static boolean sEnableRemoteKeyguardAnimation =
+            SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
+
     private final KeyguardViewMediator mKeyguardViewMediator;
     private final KeyguardLifecyclesDispatcher mKeyguardLifecyclesDispatcher;
 
@@ -52,6 +82,21 @@
         super();
         mKeyguardViewMediator = keyguardViewMediator;
         mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
+
+        if (sEnableRemoteKeyguardAnimation) {
+            RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+            final RemoteAnimationAdapter exitAnimationAdapter =
+                    new RemoteAnimationAdapter(mExitAnimationRunner, 0, 0);
+            definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY, exitAnimationAdapter);
+            definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+                    exitAnimationAdapter);
+            final RemoteAnimationAdapter occludeAnimationAdapter =
+                    new RemoteAnimationAdapter(mOccludeAnimationRunner, 0, 0);
+            definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE, occludeAnimationAdapter);
+            definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE, occludeAnimationAdapter);
+            ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay(
+                    DEFAULT_DISPLAY, definition);
+        }
     }
 
     @Override
@@ -76,6 +121,48 @@
         }
     }
 
+    private final IRemoteAnimationRunner.Stub mExitAnimationRunner =
+            new IRemoteAnimationRunner.Stub() {
+        @Override // Binder interface
+        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                RemoteAnimationTarget[] apps,
+                RemoteAnimationTarget[] wallpapers,
+                RemoteAnimationTarget[] nonApps,
+                IRemoteAnimationFinishedCallback finishedCallback) {
+            Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation");
+            checkPermission();
+            mKeyguardViewMediator.startKeyguardExitAnimation(transit, apps, wallpapers,
+                    null /* nonApps */, finishedCallback);
+            Trace.endSection();
+        }
+
+        @Override // Binder interface
+        public void onAnimationCancelled() {
+        }
+    };
+
+    private final IRemoteAnimationRunner.Stub mOccludeAnimationRunner =
+            new IRemoteAnimationRunner.Stub() {
+        @Override // Binder interface
+        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                       RemoteAnimationTarget[] apps,
+                       RemoteAnimationTarget[] wallpapers,
+                        RemoteAnimationTarget[] nonApps,
+                        IRemoteAnimationFinishedCallback finishedCallback) {
+            // TODO(bc-unlock): Calls KeyguardViewMediator#setOccluded to update the state and
+            // run animation.
+            try {
+                finishedCallback.onAnimationFinished();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "RemoteException");
+            }
+        }
+
+        @Override // Binder interface
+        public void onAnimationCancelled() {
+        }
+    };
+
     private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
 
         @Override // Binder interface
@@ -225,6 +312,11 @@
             mKeyguardViewMediator.onBootCompleted();
         }
 
+        /**
+         * @deprecated When remote animation is enabled, this won't be called anymore. Use
+         * {@code IRemoteAnimationRunner#onAnimationStart} instead.
+         */
+        @Deprecated
         @Override
         public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
             Trace.beginSection("KeyguardService.mBinder#startKeyguardExitAnimation");
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e732669..5a918d4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -27,6 +27,9 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.AlarmManager;
@@ -65,8 +68,13 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseIntArray;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.RemoteAnimationTarget;
+import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowManager;
 import android.view.WindowManagerPolicyConstants;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
@@ -85,6 +93,7 @@
 import com.android.keyguard.KeyguardViewController;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.Dumpable;
+import com.android.systemui.Interpolators;
 import com.android.systemui.SystemUI;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.classifier.FalsingCollector;
@@ -1318,6 +1327,7 @@
             if (mHiding && isOccluded) {
                 // We're in the process of going away but WindowManager wants to show a
                 // SHOW_WHEN_LOCKED activity instead.
+                // TODO(bc-unlock): Migrate to remote animation.
                 startKeyguardExitAnimation(0, 0);
             }
 
@@ -1703,7 +1713,9 @@
                     Trace.beginSection(
                             "KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
                     StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
-                    handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
+                    handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration,
+                            params.mApps, params.mWallpapers, params.mNonApps,
+                            params.mFinishedCallback);
                     mFalsingCollector.onSuccessfulUnlock();
                     Trace.endSection();
                     break;
@@ -1990,15 +2002,19 @@
             if (mShowing && !mOccluded) {
                 mKeyguardGoingAwayRunnable.run();
             } else {
+                // TODO(bc-unlock): Fill parameters
                 handleStartKeyguardExitAnimation(
                         SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(),
-                        mHideAnimation.getDuration());
+                        mHideAnimation.getDuration(), null /* apps */,  null /* wallpapers */,
+                        null /* nonApps */, null /* finishedCallback */);
             }
         }
         Trace.endSection();
     }
 
-    private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+    private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration,
+            RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+            RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
         Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation");
         if (DEBUG) Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime
                 + " fadeoutDuration=" + fadeoutDuration);
@@ -2031,6 +2047,49 @@
             mWakeAndUnlocking = false;
             mDismissCallbackRegistry.notifyDismissSucceeded();
             mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
+
+            // TODO(bc-animation): When remote animation is enabled for keyguard exit animation,
+            // apps, wallpapers and finishedCallback are set to non-null. nonApps is not yet
+            // supported, so it's always null.
+            mContext.getMainExecutor().execute(() -> {
+                if (finishedCallback == null) {
+                    return;
+                }
+
+                // TODO(bc-unlock): Sample animation, just to apply alpha animation on the app.
+                final SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier(
+                        mKeyguardViewControllerLazy.get().getViewRootImpl().getView());
+                final RemoteAnimationTarget primary = apps[0];
+                ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+                anim.setDuration(400 /* duration */);
+                anim.setInterpolator(Interpolators.LINEAR);
+                anim.addUpdateListener((ValueAnimator animation) -> {
+                    SurfaceParams params = new SurfaceParams.Builder(primary.leash)
+                            .withAlpha(animation.getAnimatedFraction())
+                            .build();
+                    applier.scheduleApply(params);
+                });
+                anim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        try {
+                            finishedCallback.onAnimationFinished();
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "RemoteException");
+                        }
+                    }
+
+                    @Override
+                    public void onAnimationCancel(Animator animation) {
+                        try {
+                            finishedCallback.onAnimationFinished();
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "RemoteException");
+                        }
+                    }
+                });
+                anim.start();
+            });
             resetKeyguardDonePendingLocked();
             mHideAnimationRun = false;
             adjustStatusBarLocked();
@@ -2223,10 +2282,55 @@
         return mKeyguardViewControllerLazy.get();
     }
 
+    /**
+     * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+     * the wallpaper and keyguard flag, and WindowManager has started running keyguard exit
+     * animation.
+     *
+     * @param startTime the start time of the animation in uptime milliseconds. Deprecated.
+     * @param fadeoutDuration the duration of the exit animation, in milliseconds Deprecated.
+     * @deprecated Will be migrate to remote animation soon.
+     */
+    @Deprecated
     public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
+        startKeyguardExitAnimation(0, startTime, fadeoutDuration, null, null, null, null);
+    }
+
+    /**
+     * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+     * the wallpaper and keyguard flag, and System UI should start running keyguard exit animation.
+     *
+     * @param apps The list of apps to animate.
+     * @param wallpapers The list of wallpapers to animate.
+     * @param nonApps The list of non-app windows such as Bubbles to animate.
+     * @param finishedCallback The callback to invoke when the animation is finished.
+     */
+    public void startKeyguardExitAnimation(@WindowManager.TransitionOldType int transit,
+            RemoteAnimationTarget[] apps,
+            RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+            IRemoteAnimationFinishedCallback finishedCallback) {
+        startKeyguardExitAnimation(transit, 0, 0, apps, wallpapers, nonApps, finishedCallback);
+    }
+
+    /**
+     * Notifies to System UI that the activity behind has now been drawn and it's safe to remove
+     * the wallpaper and keyguard flag, and start running keyguard exit animation.
+     *
+     * @param startTime the start time of the animation in uptime milliseconds. Deprecated.
+     * @param fadeoutDuration the duration of the exit animation, in milliseconds Deprecated.
+     * @param apps The list of apps to animate.
+     * @param wallpapers The list of wallpapers to animate.
+     * @param nonApps The list of non-app windows such as Bubbles to animate.
+     * @param finishedCallback The callback to invoke when the animation is finished.
+     */
+    private void startKeyguardExitAnimation(@WindowManager.TransitionOldType int transit,
+            long startTime, long fadeoutDuration,
+            RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+            RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) {
         Trace.beginSection("KeyguardViewMediator#startKeyguardExitAnimation");
         Message msg = mHandler.obtainMessage(START_KEYGUARD_EXIT_ANIM,
-                new StartKeyguardExitAnimParams(startTime, fadeoutDuration));
+                new StartKeyguardExitAnimParams(transit, startTime, fadeoutDuration, apps,
+                        wallpapers, nonApps, finishedCallback));
         mHandler.sendMessage(msg);
         Trace.endSection();
     }
@@ -2300,12 +2404,26 @@
 
     private static class StartKeyguardExitAnimParams {
 
+        @WindowManager.TransitionOldType int mTransit;
         long startTime;
         long fadeoutDuration;
+        RemoteAnimationTarget[] mApps;
+        RemoteAnimationTarget[] mWallpapers;
+        RemoteAnimationTarget[] mNonApps;
+        IRemoteAnimationFinishedCallback mFinishedCallback;
 
-        private StartKeyguardExitAnimParams(long startTime, long fadeoutDuration) {
+        private StartKeyguardExitAnimParams(@WindowManager.TransitionOldType int transit,
+                long startTime, long fadeoutDuration,
+                RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+                RemoteAnimationTarget[] nonApps,
+                IRemoteAnimationFinishedCallback finishedCallback) {
+            this.mTransit = transit;
             this.startTime = startTime;
             this.fadeoutDuration = fadeoutDuration;
+            this.mApps = apps;
+            this.mWallpapers = wallpapers;
+            this.mNonApps = nonApps;
+            this.mFinishedCallback = finishedCallback;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
index a4e9189..580cbcf 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java
@@ -24,10 +24,8 @@
 import android.app.people.IPeopleManager;
 import android.app.people.PeopleSpaceTile;
 import android.appwidget.AppWidgetManager;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
@@ -36,12 +34,9 @@
 import android.util.Log;
 import android.view.ViewGroup;
 
-import androidx.preference.PreferenceManager;
-
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.systemui.R;
-import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
 
 import java.util.List;
 
@@ -128,26 +123,17 @@
 
     /** Stores the user selected configuration for {@code mAppWidgetId}. */
     private void storeWidgetConfiguration(PeopleSpaceTile tile) {
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
-        SharedPreferences.Editor editor = sp.edit();
         if (PeopleSpaceUtils.DEBUG) {
             Log.d(TAG, "Put " + tile.getUserName() + "'s shortcut ID: "
                     + tile.getId() + " for widget ID: "
                     + mAppWidgetId);
         }
-        // Ensure updates to app widget can be retrieved from both appWidget Id and tile ID.
-        editor.putString(String.valueOf(mAppWidgetId), tile.getId());
-        editor.putInt(tile.getId(), mAppWidgetId);
-        editor.apply();
-        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
-        Bundle options = new Bundle();
-        options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, tile);
-        appWidgetManager.updateAppWidgetOptions(mAppWidgetId, options);
-        int[] widgetIds = appWidgetManager.getAppWidgetIds(
-                new ComponentName(mContext, PeopleSpaceWidgetProvider.class));
+
+        PeopleSpaceUtils.setStorageForTile(mContext, tile, mAppWidgetId);
+        int[] widgetIds = new int[mAppWidgetId];
         // TODO: Populate new widget with existing conversation notification, if there is any.
         PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager,
-                mNotificationManager);
+                mPeopleManager);
         finishActivity();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
index 91e7968..994dc6d 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java
@@ -18,6 +18,7 @@
 
 import static android.app.Notification.EXTRA_MESSAGES;
 
+import android.annotation.Nullable;
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.PendingIntent;
@@ -70,11 +71,13 @@
 import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
@@ -89,6 +92,13 @@
     private static final int MIN_HOUR = 1;
     private static final int ONE_DAY = 1;
     public static final String OPTIONS_PEOPLE_SPACE_TILE = "options_people_space_tile";
+    public static final String PACKAGE_NAME = "package_name";
+    public static final String USER_ID = "user_id";
+    public static final String SHORTCUT_ID = "shortcut_id";
+
+    public static final String EMPTY_STRING = "";
+    public static final int INVALID_WIDGET_ID = -1;
+    public static final int INVALID_USER_ID = -1;
 
     private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
     private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
@@ -171,70 +181,174 @@
      * notification being posted or removed.
      */
     public static void updateSingleConversationWidgets(Context context, int[] appWidgetIds,
-            AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
-        IPeopleManager peopleManager = IPeopleManager.Stub.asInterface(
-                ServiceManager.getService(Context.PEOPLE_SERVICE));
-        LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+            AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
         Map<Integer, PeopleSpaceTile> widgetIdToTile = new HashMap<>();
-        try {
-            List<PeopleSpaceTile> tiles =
-                    PeopleSpaceUtils.getTiles(context, notificationManager,
-                            peopleManager, launcherApps);
-            for (int appWidgetId : appWidgetIds) {
-                String shortcutId = sp.getString(String.valueOf(appWidgetId), null);
-                if (DEBUG) {
-                    Log.d(TAG, "Widget ID: " + appWidgetId + " Shortcut ID: " + shortcutId);
-                }
-
-                Optional<PeopleSpaceTile> entry = tiles.stream().filter(
-                        e -> e.getId().equals(shortcutId)).findFirst();
-
-                if (!entry.isPresent() || shortcutId == null) {
-                    if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
-                    //TODO: Delete app widget id when crash is fixed (b/175486868)
-                    continue;
-                }
-                // Augment current tile based on stored fields.
-                PeopleSpaceTile tile = augmentTileFromStorage(entry.get(), appWidgetManager,
-                        appWidgetId);
-
-                RemoteViews views = createRemoteViews(context, tile, appWidgetId);
-
-                // Tell the AppWidgetManager to perform an update on the current app widget.
-                appWidgetManager.updateAppWidget(appWidgetId, views);
-
-                widgetIdToTile.put(appWidgetId, tile);
+        for (int appWidgetId : appWidgetIds) {
+            PeopleSpaceTile tile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
+                    appWidgetId);
+            if (tile == null) {
+                if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
+                //TODO: Delete app widget id when crash is fixed (b/172932636)
+                continue;
             }
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to retrieve conversations to set tiles: " + e);
+
+            if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName());
+            RemoteViews views = createRemoteViews(context, tile, appWidgetId);
+
+            // Tell the AppWidgetManager to perform an update on the current app widget.
+            appWidgetManager.updateAppWidget(appWidgetId, views);
+
+            widgetIdToTile.put(appWidgetId, tile);
         }
         getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds);
     }
 
-    /** Augment {@link PeopleSpaceTile} with fields from stored tile. */
-    @VisibleForTesting
-    static PeopleSpaceTile augmentTileFromStorage(PeopleSpaceTile tile,
-            AppWidgetManager appWidgetManager, int appWidgetId) {
-        Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
-        PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
-        if (storedTile == null) {
-            return tile;
+    @Nullable
+    private static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager,
+            AppWidgetManager appWidgetManager,
+            Context context, int appWidgetId) {
+        try {
+            // Migrate storage for existing users.
+            SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
+                    Context.MODE_PRIVATE);
+            String pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
+            int userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+            String shortcutId = widgetSp.getString(SHORTCUT_ID, EMPTY_STRING);
+            if (pkg.isEmpty() || shortcutId.isEmpty() || userId == INVALID_WIDGET_ID) {
+                SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+                shortcutId = sp.getString(String.valueOf(appWidgetId), null);
+                if (shortcutId == null) {
+                    Log.e(TAG, "Cannot restore widget");
+                    return null;
+                }
+                migrateExistingUsersToNewStorage(context, shortcutId, appWidgetId);
+                pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
+                userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
+            }
+
+            // Check if tile is cached.
+            Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
+            PeopleSpaceTile tile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+            if (tile != null) {
+                return tile;
+            }
+
+            // If tile is null, we need to retrieve from persisted storage.
+            if (DEBUG) Log.d(TAG, "Retrieving from storage after reboots");
+            LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
+            ConversationChannel channel = peopleManager.getConversation(pkg, userId, shortcutId);
+            if (channel == null) {
+                Log.d(TAG, "Could not retrieve conversation from storage");
+                return null;
+            }
+            return new PeopleSpaceTile.Builder(channel, launcherApps).build();
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to retrieve conversation for tile: " + e);
+            return null;
         }
-        return tile.toBuilder()
-                .setBirthdayText(storedTile.getBirthdayText())
-                .setNotificationKey(storedTile.getNotificationKey())
-                .setNotificationContent(storedTile.getNotificationContent())
-                .setNotificationDataUri(storedTile.getNotificationDataUri())
-                .build();
     }
 
-    /** If incoming notification changed tile, store the changes in the tile options. */
-    public static void storeNotificationChange(StatusBarNotification sbn,
+    /** Best-effort attempts to migrate existing users to the new storage format. */
+    // TODO: Remove after sufficient time. Temporary migration storage for existing users.
+    private static void migrateExistingUsersToNewStorage(Context context, String shortcutId,
+            int appWidgetId) {
+        try {
+            List<PeopleSpaceTile> tiles =
+                    PeopleSpaceUtils.getTiles(context, INotificationManager.Stub.asInterface(
+                            ServiceManager.getService(Context.NOTIFICATION_SERVICE)),
+                            IPeopleManager.Stub.asInterface(
+                                    ServiceManager.getService(Context.PEOPLE_SERVICE)),
+                            context.getSystemService(LauncherApps.class));
+            Optional<PeopleSpaceTile> entry = tiles.stream().filter(
+                    e -> e.getId().equals(shortcutId)).findFirst();
+            if (entry.isPresent()) {
+                if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName());
+                setStorageForTile(context, entry.get(), appWidgetId);
+            } else {
+                Log.e(TAG, "Could not migrate user");
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "Could not query conversations");
+        }
+    }
+
+    /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */
+    public static void setStorageForTile(Context context, PeopleSpaceTile tile, int appWidgetId) {
+        // Write relevant persisted storage.
+        SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
+                Context.MODE_PRIVATE);
+        SharedPreferences.Editor widgetEditor = widgetSp.edit();
+        widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, tile.getPackageName());
+        widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, tile.getId());
+        int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier();
+        widgetEditor.putInt(PeopleSpaceUtils.USER_ID, userId);
+        widgetEditor.apply();
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+        SharedPreferences.Editor editor = sp.edit();
+        editor.putString(String.valueOf(appWidgetId), tile.getId());
+        String key = PeopleSpaceUtils.getKey(tile.getId(), tile.getPackageName(), userId);
+        // Don't overwrite existing widgets with the same key.
+        Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+        storedWidgetIds.add(String.valueOf(appWidgetId));
+        editor.putStringSet(key, storedWidgetIds);
+        editor.apply();
+
+        // Write cached storage.
+        updateAppWidgetOptionsAndView(AppWidgetManager.getInstance(context), context, appWidgetId,
+                tile);
+    }
+
+    /** Removes stored data when tile is deleted. */
+    public static void removeStorageForTile(Context context, int widgetId) {
+        SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(widgetId),
+                Context.MODE_PRIVATE);
+        String packageName = widgetSp.getString(PACKAGE_NAME, null);
+        String shortcutId = widgetSp.getString(SHORTCUT_ID, null);
+        int userId = widgetSp.getInt(USER_ID, -1);
+
+        // Delete widgetId mapping to key.
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
+        SharedPreferences.Editor editor = sp.edit();
+        String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
+        Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+        storedWidgetIds.remove(String.valueOf(widgetId));
+        editor.putStringSet(key, storedWidgetIds);
+        editor.remove(String.valueOf(widgetId));
+        editor.apply();
+
+        // Delete all data specifically mapped to widgetId.
+        SharedPreferences.Editor widgetEditor = widgetSp.edit();
+        widgetEditor.remove(PACKAGE_NAME);
+        widgetEditor.remove(USER_ID);
+        widgetEditor.remove(SHORTCUT_ID);
+        widgetEditor.apply();
+    }
+
+    /**
+     * Returns whether the data mapped to app widget specified by {@code appWidgetId} matches the
+     * requested update data.
+     */
+    public static boolean isCorrectAppWidget(Context context, int appWidgetId, String shortcutId,
+            String packageName, int userId) {
+        SharedPreferences sp = context.getSharedPreferences(String.valueOf(appWidgetId),
+                Context.MODE_PRIVATE);
+        String storedPackage = sp.getString(PACKAGE_NAME, EMPTY_STRING);
+        int storedUserId = sp.getInt(USER_ID, INVALID_USER_ID);
+        String storedShortcutId = sp.getString(SHORTCUT_ID, EMPTY_STRING);
+        return storedPackage.equals(packageName) && storedShortcutId.equals(shortcutId)
+                && storedUserId == userId;
+    }
+
+    /**
+     * If incoming notification changed tile, store the changes in the tile options.
+     */
+    public static void updateWidgetWithNotificationChanged(IPeopleManager peopleManager,
+            Context context,
+            StatusBarNotification sbn,
             NotificationAction notificationAction, AppWidgetManager appWidgetManager,
             int appWidgetId) {
-        Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
-        PeopleSpaceTile storedTile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        PeopleSpaceTile storedTile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
+                appWidgetId);
         if (storedTile == null) {
             if (DEBUG) Log.d(TAG, "Could not find stored tile to add notification to");
             return;
@@ -263,7 +377,7 @@
                     .setNotificationDataUri(null)
                     .build();
         }
-        updateAppWidgetOptions(appWidgetManager, appWidgetId, storedTile);
+        updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, storedTile);
     }
 
     private static void updateAppWidgetOptions(AppWidgetManager appWidgetManager, int appWidgetId,
@@ -677,4 +791,23 @@
         }
         return lookupKeysWithBirthdaysToday;
     }
+
+    /**
+     * Returns the uniquely identifying key for the conversation.
+     *
+     * <p>{@code userId} will always be a number, so we put user ID as the
+     * delimiter between the app-provided strings of shortcut ID and package name.
+     *
+     * <p>There aren't restrictions on shortcut ID characters, but there are restrictions requiring
+     * a {@code packageName} to always start with a letter. This restriction means we are
+     * guaranteed to avoid cases like "a/b/0/0/package.name" having two potential keys, as the first
+     * case is impossible given the package name restrictions:
+     * <ul>
+     *     <li>"a/b" + "/" + 0 + "/" + "0/packageName"</li>
+     *     <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li>
+     * </ul>
+     */
+    public static String getKey(String shortcutId, String packageName, int userId) {
+        return shortcutId + "/" + userId + "/" + packageName;
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
index ad6046b..bee54e4 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.people.widget;
 
-import android.app.INotificationManager;
 import android.app.NotificationChannel;
+import android.app.people.IPeopleManager;
 import android.appwidget.AppWidgetManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -29,6 +29,7 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
+import android.widget.RemoteViews;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.appwidget.IAppWidgetService;
@@ -37,7 +38,8 @@
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
 
-import java.util.Objects;
+import java.util.HashSet;
+import java.util.Set;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -51,7 +53,7 @@
     private final Context mContext;
     private IAppWidgetService mAppWidgetService;
     private AppWidgetManager mAppWidgetManager;
-    private INotificationManager mNotificationManager;
+    private IPeopleManager mPeopleManager;
 
     @Inject
     public PeopleSpaceWidgetManager(Context context, IAppWidgetService appWidgetService) {
@@ -59,11 +61,13 @@
         mContext = context;
         mAppWidgetService = appWidgetService;
         mAppWidgetManager = AppWidgetManager.getInstance(context);
-        mNotificationManager = INotificationManager.Stub.asInterface(
-                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        mPeopleManager = IPeopleManager.Stub.asInterface(
+                ServiceManager.getService(Context.PEOPLE_SERVICE));
     }
 
-    /** Constructor used for testing. */
+    /**
+     * Constructor used for testing.
+     */
     @VisibleForTesting
     protected PeopleSpaceWidgetManager(Context context) {
         if (DEBUG) Log.d(TAG, "constructor");
@@ -72,16 +76,20 @@
                 ServiceManager.getService(Context.APPWIDGET_SERVICE));
     }
 
-    /** AppWidgetManager setter used for testing. */
+    /**
+     * AppWidgetManager setter used for testing.
+     */
     @VisibleForTesting
     protected void setAppWidgetManager(IAppWidgetService appWidgetService,
-            AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
+            AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
         mAppWidgetService = appWidgetService;
         mAppWidgetManager = appWidgetManager;
-        mNotificationManager = notificationManager;
+        mPeopleManager = peopleManager;
     }
 
-    /** Updates People Space widgets. */
+    /**
+     * Updates People Space widgets.
+     */
     public void updateWidgets() {
         try {
             if (DEBUG) Log.d(TAG, "updateWidgets called");
@@ -99,7 +107,7 @@
 
             if (showSingleConversation) {
                 PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds,
-                        mAppWidgetManager, mNotificationManager);
+                        mAppWidgetManager, mPeopleManager);
             } else {
                 mAppWidgetService
                         .notifyAppWidgetViewDataChanged(mContext.getOpPackageName(), widgetIds,
@@ -114,30 +122,38 @@
      * Check if any existing People tiles match the incoming notification change, and store the
      * change in the tile if so.
      */
-    public void storeNotificationChange(StatusBarNotification sbn,
+    public void updateWidgetWithNotificationChanged(StatusBarNotification sbn,
             PeopleSpaceUtils.NotificationAction notificationAction) {
-        if (DEBUG) Log.d(TAG, "storeNotificationChange called");
+        RemoteViews views = new RemoteViews(
+                mContext.getPackageName(), R.layout.people_space_small_avatar_tile);
+        if (DEBUG) Log.d(TAG, "updateWidgetWithNotificationChanged called");
         boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
         if (!showSingleConversation) {
             return;
         }
         try {
+            String sbnShortcutId = sbn.getShortcutId();
+            if (sbnShortcutId == null) {
+                return;
+            }
             int[] widgetIds = mAppWidgetService.getAppWidgetIds(
                     new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
             );
             if (widgetIds.length == 0) {
                 return;
             }
-
             SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
-            for (int widgetId : widgetIds) {
-                String shortcutId = sp.getString(String.valueOf(widgetId), null);
-                if (!Objects.equals(sbn.getShortcutId(), shortcutId)) {
-                    continue;
-                }
+            int userId = UserHandle.getUserHandleForUid(sbn.getUid()).getIdentifier();
+            String key = PeopleSpaceUtils.getKey(sbnShortcutId, sbn.getPackageName(), userId);
+            Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+            if (storedWidgetIds.isEmpty()) {
+                return;
+            }
+            for (String widgetIdString : storedWidgetIds) {
+                int widgetId = Integer.parseInt(widgetIdString);
                 if (DEBUG) Log.d(TAG, "Storing notification change, key:" + sbn.getKey());
-                PeopleSpaceUtils.storeNotificationChange(
+                PeopleSpaceUtils.updateWidgetWithNotificationChanged(mPeopleManager, mContext,
                         sbn, notificationAction, mAppWidgetManager, widgetId);
             }
         } catch (Exception e) {
@@ -159,8 +175,7 @@
         public void onNotificationPosted(
                 StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) {
             if (DEBUG) Log.d(TAG, "onNotificationPosted");
-            storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
-            updateWidgets();
+            updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.POSTED);
         }
 
         @Override
@@ -169,8 +184,7 @@
                 NotificationListenerService.RankingMap rankingMap
         ) {
             if (DEBUG) Log.d(TAG, "onNotificationRemoved");
-            storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
-            updateWidgets();
+            updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
         }
 
         @Override
@@ -179,8 +193,7 @@
                 NotificationListenerService.RankingMap rankingMap,
                 int reason) {
             if (DEBUG) Log.d(TAG, "onNotificationRemoved with reason " + reason);
-            storeNotificationChange(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
-            updateWidgets();
+            updateWidgetWithNotificationChanged(sbn, PeopleSpaceUtils.NotificationAction.REMOVED);
         }
 
         @Override
@@ -207,4 +220,4 @@
         }
     };
 
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
index 7f204cc..f5577d3 100644
--- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.people.widget;
 
-import android.app.INotificationManager;
 import android.app.PendingIntent;
+import android.app.people.IPeopleManager;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProvider;
 import android.content.Context;
@@ -53,8 +53,8 @@
                 Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
         if (showSingleConversation) {
             PeopleSpaceUtils.updateSingleConversationWidgets(context, appWidgetIds,
-                    appWidgetManager, INotificationManager.Stub.asInterface(
-                            ServiceManager.getService(Context.NOTIFICATION_SERVICE)));
+                    appWidgetManager, IPeopleManager.Stub.asInterface(
+                            ServiceManager.getService(Context.PEOPLE_SERVICE)));
             return;
         }
         // Perform this loop procedure for each App Widget that belongs to this provider
@@ -91,6 +91,7 @@
         for (int widgetId : appWidgetIds) {
             if (DEBUG) Log.d(TAG, "Widget removed");
             mUiEventLogger.log(PeopleSpaceUtils.PeopleSpaceWidgetEvent.PEOPLE_SPACE_WIDGET_DELETED);
+            PeopleSpaceUtils.removeStorageForTile(context, widgetId);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 88b9c6c..d08f973 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -33,6 +33,7 @@
 import android.view.SyncRtSurfaceTransactionApplier;
 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
 import android.view.View;
+import android.view.WindowManager;
 
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.policy.ScreenDecorationsUtils;
@@ -159,8 +160,10 @@
         }
 
         @Override
-        public void onAnimationStart(RemoteAnimationTarget[] remoteAnimationTargets,
+        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                RemoteAnimationTarget[] remoteAnimationTargets,
                 RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
+                RemoteAnimationTarget[] remoteAnimationNonAppTargets,
                 IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
                     throws RemoteException {
             mMainExecutor.execute(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index a30f193..3b47594 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -95,6 +95,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.qs.QSDetailDisplayer;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -292,6 +293,7 @@
     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
     private final QSDetailDisplayer mQSDetailDisplayer;
+    private final FeatureFlags mFeatureFlags;
     private final ScrimController mScrimController;
     // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card.
     // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications
@@ -554,7 +556,9 @@
             QSDetailDisplayer qsDetailDisplayer,
             ScrimController scrimController,
             MediaDataManager mediaDataManager,
-            AmbientState ambientState) {
+            AmbientState ambientState,
+            FeatureFlags featureFlags
+    ) {
         super(view, falsingManager, dozeLog, keyguardStateController,
                 (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
                 latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager,
@@ -571,6 +575,7 @@
         mNotificationIconAreaController = notificationIconAreaController;
         mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
         mQSDetailDisplayer = qsDetailDisplayer;
+        mFeatureFlags = featureFlags;
         mView.setWillNotDraw(!DEBUG);
         mLayoutInflater = layoutInflater;
         mFalsingManager = falsingManager;
@@ -768,6 +773,26 @@
             lp.width = panelWidth;
             mNotificationStackScrollLayoutController.setLayoutParams(lp);
         }
+
+        if (shouldUseSplitNotificationShade()) {
+            // In order to change the constraints at runtime, all children of the Constraint Layout
+            // must have ids.
+            ensureAllViewsHaveIds(mNotificationContainerParent);
+        }
+    }
+
+    private boolean shouldUseSplitNotificationShade() {
+        return mFeatureFlags.isTwoColumnNotificationShadeEnabled()
+                && mResources.getBoolean(R.bool.config_use_split_notification_shade);
+    }
+
+    private static void ensureAllViewsHaveIds(ViewGroup parentView) {
+        for (int i = 0; i < parentView.getChildCount(); i++) {
+            View childView = parentView.getChildAt(i);
+            if (childView.getId() == View.NO_ID) {
+                childView.setId(View.generateViewId());
+            }
+        }
     }
 
     private void reInflateViews() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index b7d1bc6..6db21f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -362,34 +362,6 @@
     }
 
     @Test
-    public void testAugmentTileFromStorageWithNotification() {
-        PeopleSpaceTile tile =
-                new PeopleSpaceTile
-                        .Builder("id", "userName", ICON, new Intent())
-                        .build();
-        PeopleSpaceTile actual = PeopleSpaceUtils
-                .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITH_SHORTCUT);
-
-        assertThat(actual.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
-        assertThat(actual.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
-        assertThat(actual.getNotificationDataUri()).isEqualTo(URI);
-    }
-
-    @Test
-    public void testAugmentTileFromStorageWithoutNotification() {
-        PeopleSpaceTile tile =
-                new PeopleSpaceTile
-                        .Builder("id", "userName", ICON, new Intent())
-                        .build();
-        PeopleSpaceTile actual = PeopleSpaceUtils
-                .augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITHOUT_SHORTCUT);
-
-        assertThat(actual.getNotificationKey()).isEqualTo(null);
-        assertThat(actual.getNotificationKey()).isEqualTo(null);
-        assertThat(actual.getNotificationDataUri()).isEqualTo(null);
-    }
-
-    @Test
     public void testDoNotUpdateSingleConversationAppWidgetWhenNotBirthday() {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
         when(mMockCursor.moveToNext()).thenReturn(true, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 8c0afb8..ef314ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -21,6 +21,8 @@
 
 import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -31,26 +33,24 @@
 
 import static java.util.Objects.requireNonNull;
 
-import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.Person;
+import android.app.people.ConversationChannel;
+import android.app.people.IPeopleManager;
 import android.app.people.PeopleSpaceTile;
 import android.appwidget.AppWidgetManager;
+import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
-import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
-import android.widget.RemoteViews;
 
 import androidx.preference.PreferenceManager;
 import androidx.test.filters.SmallTest;
@@ -58,6 +58,7 @@
 import com.android.internal.appwidget.IAppWidgetService;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.people.PeopleSpaceUtils;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
 import com.android.systemui.statusbar.SbnBuilder;
@@ -74,26 +75,27 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
     private static final long MIN_LINGER_DURATION = 5;
 
-    private static final String TEST_PACKAGE_A = "com.test.package_a";
+    private static final String TEST_PACKAGE_A = "com.android.systemui.tests";
     private static final String TEST_PACKAGE_B = "com.test.package_b";
     private static final String TEST_CHANNEL_ID = "channel_id";
     private static final String TEST_CHANNEL_NAME = "channel_name";
     private static final String TEST_PARENT_CHANNEL_ID = "parent_channel_id";
     private static final String TEST_CONVERSATION_ID = "conversation_id";
     private static final int WIDGET_ID_WITH_SHORTCUT = 1;
+    private static final int SECOND_WIDGET_ID_WITH_SHORTCUT = 3;
     private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
     private static final String SHORTCUT_ID = "101";
     private static final String OTHER_SHORTCUT_ID = "102";
-    private static final String NOTIFICATION_KEY = "notification_key";
-    private static final String NOTIFICATION_CONTENT = "notification_content";
+    private static final String NOTIFICATION_KEY = "0|com.android.systemui.tests|0|null|0";
+    private static final String NOTIFICATION_CONTENT = "message text";
     private static final Uri URI = Uri.parse("fake_uri");
     private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
     private static final Person PERSON = new Person.Builder()
@@ -105,10 +107,14 @@
     private static final PeopleSpaceTile PERSON_TILE =
             new PeopleSpaceTile
                     .Builder(SHORTCUT_ID, "username", ICON, new Intent())
-                    .setNotificationKey(NOTIFICATION_KEY)
+                    .setPackageName(TEST_PACKAGE_A)
+                    .setUid(0)
+                    .setNotificationKey(NOTIFICATION_KEY + "1")
                     .setNotificationContent(NOTIFICATION_CONTENT)
                     .setNotificationDataUri(URI)
                     .build();
+    private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
+            SHORTCUT_ID).setLongLabel("name").build();
 
     private PeopleSpaceWidgetManager mManager;
 
@@ -119,175 +125,101 @@
     @Mock
     private AppWidgetManager mAppWidgetManager;
     @Mock
-    private INotificationManager mINotificationManager;
+    private IPeopleManager mIPeopleManager;
 
     @Captor
     private ArgumentCaptor<NotificationHandler> mListenerCaptor;
+    @Captor
+    private ArgumentCaptor<Bundle> mBundleArgumentCaptor;
 
     private final NoManSimulator mNoMan = new NoManSimulator();
     private final FakeSystemClock mClock = new FakeSystemClock();
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mManager =
                 new PeopleSpaceWidgetManager(mContext);
-        mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mINotificationManager);
+        mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mIPeopleManager);
         mManager.attach(mListenerService);
 
         verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
         NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue());
         mNoMan.addListener(serviceListener);
+        // Default to single People tile widgets.
         Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 2);
+                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
 
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
-        SharedPreferences.Editor editor = sp.edit();
-        editor.putString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), SHORTCUT_ID);
-        editor.apply();
+        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+
         Bundle options = new Bundle();
-        options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
-
+        options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
         when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
                 .thenReturn(options);
         when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
                 .thenReturn(new Bundle());
+        when(mIPeopleManager.getConversation(TEST_PACKAGE_A, 0, SHORTCUT_ID)).thenReturn(
+                getConversationWithShortcutId(SHORTCUT_ID));
     }
 
     @Test
-    public void testDoNotNotifyAppWidgetIfNoWidgets() throws RemoteException {
+    public void testDoNotUpdateAppWidgetIfNoWidgets() throws Exception {
         int[] widgetIdsArray = {};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        NotifEvent notif1 = mNoMan.postNotif(
-                new NotificationEntryBuilder()
-                        .setId(0)
-                        .setPkg(TEST_PACKAGE_A));
+        StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(sbn)
+                .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
     }
 
     @Test
-    public void testDoNotNotifySingleConversationAppWidgetIfNoWidgets() throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
+    public void testDoNotUpdateAppWidgetIfNoShortcutInfo() throws Exception {
         int[] widgetIdsArray = {};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        NotifEvent notif1 = mNoMan.postNotif(
-                new NotificationEntryBuilder()
-                        .setId(0)
-                        .setPkg(TEST_PACKAGE_A));
+        Notification notificationWithoutShortcut = new Notification.Builder(mContext)
+                .setContentTitle("TEST_TITLE")
+                .setContentText("TEST_TEXT")
+                .setStyle(new Notification.MessagingStyle(PERSON)
+                        .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
+                )
+                .build();
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(new SbnBuilder()
+                        .setNotification(notificationWithoutShortcut)
+                        .setPkg(TEST_PACKAGE_A)
+                        .build())
+                .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), any(RemoteViews.class));
-        verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
     }
 
     @Test
-    public void testNotifyAppWidgetIfNotificationPosted() throws RemoteException {
-        int[] widgetIdsArray = {1};
+    public void testDoNotUpdateAppWidgetIfNoPackage() throws Exception {
+        int[] widgetIdsArray = {};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        NotifEvent notif1 = mNoMan.postNotif(
-                new NotificationEntryBuilder()
-                        .setId(0)
-                        .setPkg(TEST_PACKAGE_A));
+        StatusBarNotification sbnWithoutPackageName = new SbnBuilder()
+                .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+                .build();
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(sbnWithoutPackageName)
+                .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mIAppWidgetService, times(1))
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
-        verify(mIAppWidgetService, never()).updateAppWidgetIds(any(), any(),
-                any(RemoteViews.class));
-    }
-
-    @Test
-    public void testNotifySingleConversationAppWidgetOnceIfNotificationPosted()
-            throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
-        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
-        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_A)
-                .setId(1));
-
-        verify(mIAppWidgetService, never())
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
-        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
-                any(RemoteViews.class));
-        verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT),
-                any(RemoteViews.class));
-    }
-
-    @Test
-    public void testNotifySingleConversationAppWidgetTwiceIfTwoNotificationsPosted()
-            throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
-        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
-        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_A)
-                .setId(1));
-        mClock.advanceTime(4);
-        NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_B)
-                .setId(2));
-
-        verify(mIAppWidgetService, never())
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
-        verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
-                any(RemoteViews.class));
-        verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT),
-                any(RemoteViews.class));
-    }
-
-    @Test
-    public void testNotifyAppWidgetTwiceIfTwoNotificationsPosted() throws RemoteException {
-        int[] widgetIdsArray = {1, 2};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
-        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_A)
-                .setId(1));
-        mClock.advanceTime(4);
-        NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_B)
-                .setId(2));
-
-        verify(mIAppWidgetService, times(2))
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
         verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
-                any(RemoteViews.class));
+                any());
     }
 
     @Test
-    public void testNotifyAppWidgetTwiceIfNotificationPostedAndRemoved() throws RemoteException {
-        int[] widgetIdsArray = {1, 2};
-        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
-
-        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setPkg(TEST_PACKAGE_A)
-                .setId(1));
-        mClock.advanceTime(4);
-        NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
-
-        verify(mIAppWidgetService, times(2))
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
-        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
-                any(RemoteViews.class));
-    }
-
-    @Test
-    public void testDoNotNotifyAppWidgetIfNonConversationChannelModified() throws RemoteException {
+    public void testDoNotUpdateAppWidgetIfNonConversationChannelModified() throws Exception {
         int[] widgetIdsArray = {1};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
@@ -298,13 +230,12 @@
                 UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
         verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
-                any(RemoteViews.class));
+                any());
     }
 
     @Test
-    public void testNotifyAppWidgetIfConversationChannelModified() throws RemoteException {
+    public void testUpdateAppWidgetIfConversationChannelModified() throws Exception {
         int[] widgetIdsArray = {1};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
@@ -316,45 +247,57 @@
                 UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mIAppWidgetService, times(1))
-                .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
-        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
-                any(RemoteViews.class));
+        verify(mAppWidgetManager, times(1)).updateAppWidget(anyInt(),
+                any());
     }
 
     @Test
-    public void testDoNotUpdateNotificationPostedIfNoExistingTile() throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
+    public void testDoNotUpdateNotificationPostedIfDifferentShortcutId() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbn)
-                .setPkg(TEST_PACKAGE_A)
                 .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
         verify(mAppWidgetManager, never())
-                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+                .updateAppWidgetOptions(anyInt(), any());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
     }
 
     @Test
-    public void testDoNotUpdateNotificationRemovedIfNoExistingTile() throws RemoteException {
+    public void testDoNotUpdateNotificationPostedIfDifferentPackageName() throws Exception {
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
+                .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+                .setPkg(TEST_PACKAGE_B)
+                .build();
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(sbnWithDifferentPackageName)
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, never())
+                .updateAppWidgetOptions(anyInt(), any());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
+    }
+
+    @Test
+    public void testDoNotUpdateNotificationRemovedIfDifferentShortcutId() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbn = createConversationNotification(OTHER_SHORTCUT_ID);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbn)
-                .setPkg(TEST_PACKAGE_A)
                 .setId(1));
         mClock.advanceTime(4);
         NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
@@ -362,99 +305,215 @@
 
         verify(mAppWidgetManager, never())
                 .updateAppWidgetOptions(anyInt(), any());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
     }
 
     @Test
-    public void testUpdateNotificationPostedIfExistingTile() throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
+    public void testDoNotUpdateNotificationRemovedIfDifferentPackageName() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID);
+        StatusBarNotification sbnWithDifferentPackageName = new SbnBuilder()
+                .setNotification(createMessagingStyleNotification(SHORTCUT_ID))
+                .setPkg(TEST_PACKAGE_B)
+                .build();
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
-                .setSbn(sbn)
-                .setPkg(TEST_PACKAGE_A)
+                .setSbn(sbnWithDifferentPackageName)
+                .setId(1));
+        mClock.advanceTime(4);
+        NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, never())
+                .updateAppWidgetOptions(anyInt(), any());
+        verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
+                any());
+    }
+
+    @Test
+    public void testUpdateNotificationPostedIfExistingTile() throws Exception {
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
+        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(createConversationNotification(SHORTCUT_ID))
                 .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
         verify(mAppWidgetManager, times(1))
-                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        mBundleArgumentCaptor.capture());
+        Bundle bundle = mBundleArgumentCaptor.getValue();
+        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        assertThat(tile.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
+        assertThat(tile.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+    }
+
+    @Test
+    public void testUpdateNotificationPostedOnTwoExistingTiles() throws Exception {
+        Bundle options = new Bundle();
+        options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+        when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+                .thenReturn(options);
+        // Set the same Person associated on another People Tile widget ID.
+        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
+                SECOND_WIDGET_ID_WITH_SHORTCUT};
+        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(createConversationNotification(SHORTCUT_ID))
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        any());
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                        any());
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                any());
+    }
+
+    @Test
+    public void testUpdateNotificationOnExistingTileAfterRemovingTileForSamePerson()
+            throws Exception {
+        Bundle options = new Bundle();
+        options.putParcelable(PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
+        when(mAppWidgetManager.getAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT)))
+                .thenReturn(options);
+        // Set the same Person associated on another People Tile widget ID.
+        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, WIDGET_ID_WITH_SHORTCUT);
+        setStorageForTile(SHORTCUT_ID, TEST_PACKAGE_A, SECOND_WIDGET_ID_WITH_SHORTCUT);
+
+        int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT,
+                SECOND_WIDGET_ID_WITH_SHORTCUT};
+        when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+        PeopleSpaceUtils.removeStorageForTile(mContext, SECOND_WIDGET_ID_WITH_SHORTCUT);
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setSbn(createConversationNotification(SHORTCUT_ID))
+                .setId(1));
+        mClock.advanceTime(MIN_LINGER_DURATION);
+
+        verify(mAppWidgetManager, times(1))
+                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                        any());
+        verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
+        verify(mAppWidgetManager, never())
+                .updateAppWidgetOptions(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                        any());
+        verify(mAppWidgetManager, never()).updateAppWidget(eq(SECOND_WIDGET_ID_WITH_SHORTCUT),
+                any());
     }
 
     @Test
     public void testDoNotUpdateNotificationPostedWithoutMessagesIfExistingTile()
-            throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
+            throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
-        Notification notification = new Notification.Builder(mContext)
+        Notification notificationWithoutMessagingStyle = new Notification.Builder(mContext)
                 .setContentTitle("TEST_TITLE")
                 .setContentText("TEST_TEXT")
                 .setShortcutId(SHORTCUT_ID)
                 .build();
         StatusBarNotification sbn = new SbnBuilder()
-                .setNotification(notification)
+                .setNotification(notificationWithoutMessagingStyle)
                 .build();
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbn)
-                .setPkg(TEST_PACKAGE_A)
                 .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
 
         verify(mAppWidgetManager, never())
                 .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+        verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
+                any());
     }
 
     @Test
-    public void testUpdateNotificationRemovedIfExistingTile() throws RemoteException {
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
-        when(mINotificationManager.getConversations(false)).thenReturn(
-                new ParceledListSlice(getConversationWithShortcutId()));
+    public void testUpdateNotificationRemovedIfExistingTile() throws Exception {
         int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         StatusBarNotification sbn = createConversationNotification(SHORTCUT_ID);
         NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
                 .setSbn(sbn)
-                .setPkg(TEST_PACKAGE_A)
                 .setId(1));
         mClock.advanceTime(MIN_LINGER_DURATION);
         NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
         mClock.advanceTime(MIN_LINGER_DURATION);
 
-        verify(mAppWidgetManager, times(2))
-                .updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT), any());
+        verify(mAppWidgetManager, times(2)).updateAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT),
+                mBundleArgumentCaptor.capture());
+        Bundle bundle = mBundleArgumentCaptor.getValue();
+        PeopleSpaceTile tile = bundle.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
+        assertThat(tile.getNotificationKey()).isEqualTo(null);
+        assertThat(tile.getNotificationContent()).isEqualTo(null);
+        assertThat(tile.getNotificationDataUri()).isEqualTo(null);
+        verify(mAppWidgetManager, times(2)).updateAppWidget(anyInt(),
+                any());
     }
 
-    /** Returns a list of a single conversation associated with {@code SHORTCUT_ID}. */
-    private List<ConversationChannelWrapper> getConversationWithShortcutId() {
-        List<ConversationChannelWrapper> convos = new ArrayList<>();
-        ConversationChannelWrapper convo1 = new ConversationChannelWrapper();
-        convo1.setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID).setLongLabel(
-                "name").build());
-        convos.add(convo1);
-        return convos;
+    /**
+     * Returns a single conversation associated with {@code shortcutId}.
+     */
+    private ConversationChannel getConversationWithShortcutId(String shortcutId) throws Exception {
+        ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
+                "name").build();
+        ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
+                0L, false);
+        return convo;
     }
 
-    private StatusBarNotification createConversationNotification(String shortcutId) {
-        Notification notification = new Notification.Builder(mContext)
+    private Notification createMessagingStyleNotification(String shortcutId) {
+        return new Notification.Builder(mContext)
                 .setContentTitle("TEST_TITLE")
                 .setContentText("TEST_TEXT")
                 .setShortcutId(shortcutId)
                 .setStyle(new Notification.MessagingStyle(PERSON)
-                        .addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
+                        .addMessage(
+                                new Notification.MessagingStyle.Message(NOTIFICATION_CONTENT, 10,
+                                        PERSON))
                 )
                 .build();
+    }
+
+    private StatusBarNotification createConversationNotification(String shortcutId) {
+        Notification notification = createMessagingStyleNotification(shortcutId);
         return new SbnBuilder()
                 .setNotification(notification)
+                .setPkg(TEST_PACKAGE_A)
                 .build();
     }
+
+    private void setStorageForTile(String shortcutId, String packageName, int widgetId) {
+        SharedPreferences widgetSp = mContext.getSharedPreferences(
+                String.valueOf(widgetId),
+                Context.MODE_PRIVATE);
+        SharedPreferences.Editor widgetEditor = widgetSp.edit();
+        widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, packageName);
+        widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, shortcutId);
+        widgetEditor.putInt(PeopleSpaceUtils.USER_ID, 0);
+        widgetEditor.apply();
+
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+        SharedPreferences.Editor editor = sp.edit();
+        editor.putString(String.valueOf(widgetId), shortcutId);
+        String key = PeopleSpaceUtils.getKey(shortcutId, packageName, 0);
+        Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
+        storedWidgetIds.add(String.valueOf(widgetId));
+        editor.putStringSet(key, storedWidgetIds);
+        editor.apply();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 8d4470b..fa78ca6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -68,6 +68,7 @@
 import com.android.systemui.media.MediaHierarchyManager;
 import com.android.systemui.qs.QSDetailDisplayer;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationShelfController;
@@ -204,6 +205,10 @@
     @Mock
     private MediaDataManager mMediaDataManager;
     @Mock
+    private FeatureFlags mFeatureFlags;
+    @Mock
+    private NotificationsQuickSettingsContainer mNotificationContainerParent;
+    @Mock
     private AmbientState mAmbientState;
     private NotificationPanelViewController mNotificationPanelViewController;
     private View.AccessibilityDelegate mAccessibiltyDelegate;
@@ -219,6 +224,8 @@
         when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
         mDisplayMetrics.density = 100;
         when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true);
+        when(mResources.getDimensionPixelSize(R.dimen.qs_panel_width)).thenReturn(400);
+        when(mResources.getDimensionPixelSize(R.dimen.notification_panel_width)).thenReturn(400);
         when(mView.getContext()).thenReturn(getContext());
         when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
         when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch);
@@ -237,6 +244,8 @@
         when(mView.findViewById(R.id.keyguard_status_view))
                 .thenReturn(mock(KeyguardStatusView.class));
         when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
+        when(mView.findViewById(R.id.notification_container_parent))
+                .thenReturn(mNotificationContainerParent);
         FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
                 mDisplayMetrics);
 
@@ -264,6 +273,11 @@
                 .thenReturn(mKeyguardClockSwitchController);
         when(mKeyguardStatusViewComponent.getKeyguardStatusViewController())
                 .thenReturn(mKeyguardStatusViewController);
+        when(mQsFrame.getLayoutParams()).thenReturn(
+                new ViewGroup.LayoutParams(600, 400));
+        when(mNotificationStackScrollLayoutController.getLayoutParams()).thenReturn(
+                new ViewGroup.LayoutParams(600, 400));
+
         mNotificationPanelViewController = new NotificationPanelViewController(mView,
                 mResources,
                 mLayoutInflater,
@@ -285,7 +299,8 @@
                 new QSDetailDisplayer(),
                 mScrimController,
                 mMediaDataManager,
-                mAmbientState);
+                mAmbientState,
+                mFeatureFlags);
         mNotificationPanelViewController.initDependencies(
                 mStatusBar,
                 mNotificationShelfController);
@@ -400,6 +415,25 @@
         verify(mStatusBarKeyguardViewManager).showBouncer(true);
     }
 
+    @Test
+    public void testAllChildrenOfNotificationContainer_haveIds() {
+        when(mNotificationContainerParent.getChildCount()).thenReturn(2);
+        when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
+        when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true);
+
+        View view1 = new View(mContext);
+        view1.setId(1);
+        when(mNotificationContainerParent.getChildAt(0)).thenReturn(view1);
+
+        View view2 = mock(View.class);
+        when(mNotificationContainerParent.getChildAt(1)).thenReturn(view2);
+
+        mNotificationPanelViewController.updateResources();
+
+        assertThat(mNotificationContainerParent.getChildAt(0).getId()).isEqualTo(1);
+        assertThat(mNotificationContainerParent.getChildAt(1).getId()).isNotEqualTo(View.NO_ID);
+    }
+
     private void onTouchEvent(MotionEvent ev) {
         mTouchHandler.onTouch(mView, ev);
     }
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index dad8bd8..9ccb0c7 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1137,7 +1137,8 @@
      */
     public abstract void requestChecksums(@NonNull String packageName, boolean includeSplits,
             @Checksum.Type int optional, @Checksum.Type int required,
-            @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
+            @Nullable List trustedInstallers,
+            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
             @NonNull Executor executor, @NonNull Handler handler);
 
     /**
diff --git a/services/core/java/com/android/server/BundleUtils.java b/services/core/java/com/android/server/BundleUtils.java
new file mode 100644
index 0000000..20ebe29
--- /dev/null
+++ b/services/core/java/com/android/server/BundleUtils.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+
+/**
+ * Utility methods for handling {@link Bundle}.
+ *
+ */
+public final class BundleUtils {
+    private BundleUtils() {
+    }
+
+    /**
+     * Returns true if {@code in} is null or empty.
+     */
+    public static boolean isEmpty(@Nullable Bundle in) {
+        return (in == null) || (in.size() == 0);
+    }
+
+    /**
+     * Creates a copy of the {@code in} Bundle.  If {@code in} is null, it'll return an empty
+     * bundle.
+     *
+     * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return
+     * {@link Bundle#EMPTY})
+     */
+    public static @NonNull Bundle clone(@Nullable Bundle in) {
+        return (in != null) ? new Bundle(in) : new Bundle();
+    }
+
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 08390b4..bf9d564 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1227,6 +1227,14 @@
 
         mDnsManager = new DnsManager(mContext, mDnsResolver);
         registerPrivateDnsSettingsCallbacks();
+
+        mNoServiceNetwork =  new NetworkAgentInfo(null,
+                new Network(NO_SERVICE_NET_ID),
+                new NetworkInfo(TYPE_NONE, 0, "", ""),
+                new LinkProperties(), new NetworkCapabilities(), 0, mContext,
+                null, new NetworkAgentConfig(), this, null,
+                null, null, 0, INVALID_UID,
+                mQosCallbackTracker);
     }
 
     private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
@@ -1376,7 +1384,7 @@
     }
 
     private NetworkState getUnfilteredActiveNetworkState(int uid) {
-        NetworkAgentInfo nai = getDefaultNetwork();
+        NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
 
         final Network[] networks = getVpnUnderlyingNetworks(uid);
         if (networks != null) {
@@ -1509,7 +1517,7 @@
             }
         }
 
-        NetworkAgentInfo nai = getDefaultNetwork();
+        NetworkAgentInfo nai = getDefaultNetworkForUid(uid);
         if (nai == null || isNetworkWithCapabilitiesBlocked(nai.networkCapabilities, uid,
                 ignoreBlocked)) {
             return null;
@@ -1648,21 +1656,28 @@
 
         HashMap<Network, NetworkCapabilities> result = new HashMap<>();
 
-        final NetworkAgentInfo nai = getDefaultNetwork();
-        NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
-        if (nc != null) {
-            result.put(
-                    nai.network,
-                    createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                            nc, mDeps.getCallingUid(), callingPackageName));
+        for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+            if (!nri.isBeingSatisfied()) {
+                continue;
+            }
+            final NetworkAgentInfo nai = nri.getSatisfier();
+            final NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
+            if (null != nc
+                    && nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
+                    && !result.containsKey(nai.network)) {
+                result.put(
+                        nai.network,
+                        createWithLocationInfoSanitizedIfNecessaryWhenParceled(
+                                nc, mDeps.getCallingUid(), callingPackageName));
+            }
         }
 
         // No need to check mLockdownEnabled. If it's true, getVpnUnderlyingNetworks returns null.
         final Network[] networks = getVpnUnderlyingNetworks(Binder.getCallingUid());
-        if (networks != null) {
-            for (Network network : networks) {
-                nc = getNetworkCapabilitiesInternal(network);
-                if (nc != null) {
+        if (null != networks) {
+            for (final Network network : networks) {
+                final NetworkCapabilities nc = getNetworkCapabilitiesInternal(network);
+                if (null != nc) {
                     result.put(
                             network,
                             createWithLocationInfoSanitizedIfNecessaryWhenParceled(
@@ -1684,9 +1699,7 @@
 
     /**
      * Return LinkProperties for the active (i.e., connected) default
-     * network interface.  It is assumed that at most one default network
-     * is active at a time. If more than one is active, it is indeterminate
-     * which will be returned.
+     * network interface for the calling uid.
      * @return the ip properties for the active network, or {@code null} if
      * none is active
      */
@@ -2129,8 +2142,8 @@
 
     private boolean isUidBlockedByRules(int uid, int uidRules, boolean isNetworkMetered,
             boolean isBackgroundRestricted) {
-        return NetworkPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
-                isNetworkMetered, isBackgroundRestricted);
+        return mPolicyManager.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
+                isBackgroundRestricted);
     }
 
     /**
@@ -2713,9 +2726,9 @@
                 pw.println(nai.requestAt(i).toString());
             }
             pw.decreaseIndent();
-            pw.println("Lingered:");
+            pw.println("Inactivity Timers:");
             pw.increaseIndent();
-            nai.dumpLingerTimers(pw);
+            nai.dumpInactivityTimers(pw);
             pw.decreaseIndent();
             pw.decreaseIndent();
         }
@@ -3310,27 +3323,27 @@
     }
 
     /**
-     * Updates the linger state from the network requests inside the NAI.
+     * Updates the inactivity state from the network requests inside the NAI.
      * @param nai the agent info to update
      * @param now the timestamp of the event causing this update
-     * @return whether the network was lingered as a result of this update
+     * @return whether the network was inactive as a result of this update
      */
-    private boolean updateLingerState(@NonNull final NetworkAgentInfo nai, final long now) {
-        // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
-        // 2. If the network was lingering and there are now requests, unlinger it.
+    private boolean updateInactivityState(@NonNull final NetworkAgentInfo nai, final long now) {
+        // 1. Update the inactivity timer. If it's changed, reschedule or cancel the alarm.
+        // 2. If the network was inactive and there are now requests, unset inactive.
         // 3. If this network is unneeded (which implies it is not lingering), and there is at least
-        //    one lingered request, start lingering.
-        nai.updateLingerTimer();
+        //    one lingered request, set inactive.
+        nai.updateInactivityTimer();
         if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
-            if (DBG) log("Unlingering " + nai.toShortString());
-            nai.unlinger();
+            if (DBG) log("Unsetting inactive " + nai.toShortString());
+            nai.unsetInactive();
             logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
-        } else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
+        } else if (unneeded(nai, UnneededFor.LINGER) && nai.getInactivityExpiry() > 0) {
             if (DBG) {
-                final int lingerTime = (int) (nai.getLingerExpiry() - now);
-                log("Lingering " + nai.toShortString() + " for " + lingerTime + "ms");
+                final int lingerTime = (int) (nai.getInactivityExpiry() - now);
+                log("Setting inactive " + nai.toShortString() + " for " + lingerTime + "ms");
             }
-            nai.linger();
+            nai.setInactive();
             logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
             return true;
         }
@@ -3344,7 +3357,6 @@
                 if (VDBG) log("NetworkFactory connected");
                 // Finish setting up the full connection
                 NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo);
-                npi.completeConnection();
                 sendAllRequestsToProvider(npi);
             } else {
                 loge("Error connecting NetworkFactory");
@@ -3446,13 +3458,16 @@
         propagateUnderlyingNetworkCapabilities(nai.network);
         // Remove all previously satisfied requests.
         for (int i = 0; i < nai.numNetworkRequests(); i++) {
-            NetworkRequest request = nai.requestAt(i);
+            final NetworkRequest request = nai.requestAt(i);
             final NetworkRequestInfo nri = mNetworkRequests.get(request);
             final NetworkAgentInfo currentNetwork = nri.getSatisfier();
             if (currentNetwork != null
                     && currentNetwork.network.getNetId() == nai.network.getNetId()) {
+                // uid rules for this network will be removed in destroyNativeNetwork(nai).
                 nri.setSatisfier(null, null);
-                sendUpdatedScoreToFactories(request, null);
+                if (request.isRequest()) {
+                    sendUpdatedScoreToFactories(request, null);
+                }
 
                 if (mDefaultRequest == nri) {
                     // TODO : make battery stats aware that since 2013 multiple interfaces may be
@@ -3466,7 +3481,7 @@
                 }
             }
         }
-        nai.clearLingerState();
+        nai.clearInactivityState();
         // TODO: mLegacyTypeTracker.remove seems redundant given there's a full rematch right after.
         //  Currently, deleting it breaks tests that check for the default network disconnecting.
         //  Find out why, fix the rematch code, and delete this.
@@ -3565,8 +3580,8 @@
             }
         }
         rematchAllNetworksAndRequests();
-        // If an active request exists, return as its score has already been sent if needed.
-        if (null != nri.getActiveRequest()) {
+        // If the nri is satisfied, return as its score has already been sent if needed.
+        if (nri.isBeingSatisfied()) {
             return;
         }
 
@@ -3709,7 +3724,7 @@
         if (mNetworkRequests.get(nri.mRequests.get(0)) == null) {
             return;
         }
-        if (nri.getSatisfier() != null) {
+        if (nri.isBeingSatisfied()) {
             return;
         }
         if (VDBG || (DBG && nri.mRequests.get(0).isRequest())) {
@@ -3808,7 +3823,7 @@
             // If there are still lingered requests on this network, don't tear it down,
             // but resume lingering instead.
             final long now = SystemClock.elapsedRealtime();
-            if (updateLingerState(nai, now)) {
+            if (updateInactivityState(nai, now)) {
                 notifyNetworkLosing(nai, now);
             }
             if (unneeded(nai, UnneededFor.TEARDOWN)) {
@@ -4900,7 +4915,8 @@
         // see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
         // the underlyingNetworks list.
         if (underlyingNetworks == null) {
-            final NetworkAgentInfo defaultNai = getDefaultNetwork();
+            final NetworkAgentInfo defaultNai = getDefaultNetworkForUid(
+                    nai.networkCapabilities.getOwnerUid());
             if (defaultNai != null) {
                 underlyingNetworks = new Network[] { defaultNai.network };
             }
@@ -4951,8 +4967,10 @@
         }
     }
 
-    private Network[] underlyingNetworksOrDefault(Network[] underlyingNetworks) {
-        final Network defaultNetwork = getNetwork(getDefaultNetwork());
+    // TODO This needs to be the default network that applies to the NAI.
+    private Network[] underlyingNetworksOrDefault(final int ownerUid,
+            Network[] underlyingNetworks) {
+        final Network defaultNetwork = getNetwork(getDefaultNetworkForUid(ownerUid));
         if (underlyingNetworks == null && defaultNetwork != null) {
             // null underlying networks means to track the default.
             underlyingNetworks = new Network[] { defaultNetwork };
@@ -4965,7 +4983,8 @@
         // TODO: support more than one level of underlying networks, either via a fixed-depth search
         // (e.g., 2 levels of underlying networks), or via loop detection, or....
         if (!nai.supportsUnderlyingNetworks()) return false;
-        final Network[] underlying = underlyingNetworksOrDefault(nai.declaredUnderlyingNetworks);
+        final Network[] underlying = underlyingNetworksOrDefault(
+                nai.networkCapabilities.getOwnerUid(), nai.declaredUnderlyingNetworks);
         return ArrayUtils.contains(underlying, network);
     }
 
@@ -5423,27 +5442,21 @@
     private static class NetworkProviderInfo {
         public final String name;
         public final Messenger messenger;
-        private final AsyncChannel mAsyncChannel;
         private final IBinder.DeathRecipient mDeathRecipient;
         public final int providerId;
 
         NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
-                int providerId, IBinder.DeathRecipient deathRecipient) {
+                int providerId, @NonNull IBinder.DeathRecipient deathRecipient) {
             this.name = name;
             this.messenger = messenger;
             this.providerId = providerId;
-            mAsyncChannel = asyncChannel;
             mDeathRecipient = deathRecipient;
 
-            if ((mAsyncChannel == null) == (mDeathRecipient == null)) {
-                throw new AssertionError("Must pass exactly one of asyncChannel or deathRecipient");
+            if (mDeathRecipient == null) {
+                throw new AssertionError("Must pass a deathRecipient");
             }
         }
 
-        boolean isLegacyNetworkFactory() {
-            return mAsyncChannel != null;
-        }
-
         void sendMessageToNetworkProvider(int what, int arg1, int arg2, Object obj) {
             try {
                 messenger.send(Message.obtain(null /* handler */, what, arg1, arg2, obj));
@@ -5454,38 +5467,19 @@
         }
 
         void requestNetwork(NetworkRequest request, int score, int servingProviderId) {
-            if (isLegacyNetworkFactory()) {
-                mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
-                        servingProviderId, request);
-            } else {
-                sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
+            sendMessageToNetworkProvider(NetworkProvider.CMD_REQUEST_NETWORK, score,
                             servingProviderId, request);
-            }
         }
 
         void cancelRequest(NetworkRequest request) {
-            if (isLegacyNetworkFactory()) {
-                mAsyncChannel.sendMessage(android.net.NetworkFactory.CMD_CANCEL_REQUEST, request);
-            } else {
-                sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
-            }
+            sendMessageToNetworkProvider(NetworkProvider.CMD_CANCEL_REQUEST, 0, 0, request);
         }
 
         void connect(Context context, Handler handler) {
-            if (isLegacyNetworkFactory()) {
-                mAsyncChannel.connect(context, handler, messenger);
-            } else {
-                try {
-                    messenger.getBinder().linkToDeath(mDeathRecipient, 0);
-                } catch (RemoteException e) {
-                    mDeathRecipient.binderDied();
-                }
-            }
-        }
-
-        void completeConnection() {
-            if (isLegacyNetworkFactory()) {
-                mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+            try {
+                messenger.getBinder().linkToDeath(mDeathRecipient, 0);
+            } catch (RemoteException e) {
+                mDeathRecipient.binderDied();
             }
         }
     }
@@ -5521,9 +5515,8 @@
             mActiveRequest = activeRequest;
         }
 
-        // The network currently satisfying this request, or null if none. Must only be touched
-        // on the handler thread. This only makes sense for network requests and not for listens,
-        // as defined by NetworkRequest#isRequest(). For listens, this is always null.
+        // The network currently satisfying this NRI. Only one request in an NRI can have a
+        // satisfier. For non-multilayer requests, only REQUEST-type requests can have a satisfier.
         @Nullable
         private NetworkAgentInfo mSatisfier;
         NetworkAgentInfo getSatisfier() {
@@ -5546,6 +5539,18 @@
         final int mUid;
         final Messenger messenger;
 
+        /**
+         * Get the list of UIDs this nri applies to.
+         */
+        @NonNull
+        private Set<UidRange> getUids() {
+            // networkCapabilities.getUids() returns a defensive copy.
+            // multilayer requests will all have the same uids so return the first one.
+            final Set<UidRange> uids = null == mRequests.get(0).networkCapabilities.getUids()
+                    ? new ArraySet<>() : mRequests.get(0).networkCapabilities.getUids();
+            return uids;
+        }
+
         NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
             mRequests = initializeRequests(r);
             ensureAllNetworkRequestsHaveType(mRequests);
@@ -5579,6 +5584,13 @@
             this(r, null);
         }
 
+        // True if this NRI is being satisfied. It also accounts for if the nri has its satisifer
+        // set to the mNoServiceNetwork in which case mActiveRequest will be null thus returning
+        // false.
+        boolean isBeingSatisfied() {
+            return (null != mSatisfier && null != mActiveRequest);
+        }
+
         boolean isMultilayerRequest() {
             return mRequests.size() > 1;
         }
@@ -5604,7 +5616,9 @@
 
         @Override
         public String toString() {
-            return "uid/pid:" + mUid + "/" + mPid + " " + mRequests
+            return "uid/pid:" + mUid + "/" + mPid + " active request Id: "
+                    + (mActiveRequest == null ? null : mActiveRequest.requestId)
+                    + " " + mRequests
                     + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
         }
     }
@@ -5960,15 +5974,6 @@
                 EVENT_RELEASE_NETWORK_REQUEST, getCallingUid(), 0, networkRequest));
     }
 
-    @Override
-    public int registerNetworkFactory(Messenger messenger, String name) {
-        enforceNetworkFactoryPermission();
-        NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger, new AsyncChannel(),
-                nextNetworkProviderId(), null /* deathRecipient */);
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
-        return npi.providerId;
-    }
-
     private void handleRegisterNetworkProvider(NetworkProviderInfo npi) {
         if (mNetworkProviderInfos.containsKey(npi.messenger)) {
             // Avoid creating duplicates. even if an app makes a direct AIDL call.
@@ -5982,10 +5987,7 @@
         if (DBG) log("Got NetworkProvider Messenger for " + npi.name);
         mNetworkProviderInfos.put(npi.messenger, npi);
         npi.connect(mContext, mTrackerHandler);
-        if (!npi.isLegacyNetworkFactory()) {
-            // Legacy NetworkFactories get their requests when their AsyncChannel connects.
-            sendAllRequestsToProvider(npi);
-        }
+        sendAllRequestsToProvider(npi);
     }
 
     @Override
@@ -6004,11 +6006,6 @@
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger));
     }
 
-    @Override
-    public void unregisterNetworkFactory(Messenger messenger) {
-        unregisterNetworkProvider(messenger);
-    }
-
     private void handleUnregisterNetworkProvider(Messenger messenger) {
         NetworkProviderInfo npi = mNetworkProviderInfos.remove(messenger);
         if (npi == null) {
@@ -6064,6 +6061,26 @@
     @NonNull
     private final ArraySet<NetworkRequestInfo> mDefaultNetworkRequests = new ArraySet<>();
 
+    private boolean isPerAppDefaultRequest(@NonNull final NetworkRequestInfo nri) {
+        return (mDefaultNetworkRequests.contains(nri) && mDefaultRequest != nri);
+    }
+
+    /**
+     * Determine if an nri is a managed default request that disallows default networking.
+     * @param nri the request to evaluate
+     * @return true if device-default networking is disallowed
+     */
+    private boolean isDefaultBlocked(@NonNull final NetworkRequestInfo nri) {
+        // Check if this nri is a managed default that supports the default network at its
+        // lowest priority request.
+        final NetworkRequest defaultNetworkRequest = mDefaultRequest.mRequests.get(0);
+        final NetworkCapabilities lowestPriorityNetCap =
+                nri.mRequests.get(nri.mRequests.size() - 1).networkCapabilities;
+        return isPerAppDefaultRequest(nri)
+                && !(defaultNetworkRequest.networkCapabilities.equalRequestableCapabilities(
+                        lowestPriorityNetCap));
+    }
+
     // Request used to optionally keep mobile data active even when higher
     // priority networks like Wi-Fi are active.
     private final NetworkRequest mDefaultMobileDataRequest;
@@ -6075,12 +6092,39 @@
     // Request used to optionally keep vehicle internal network always active
     private final NetworkRequest mDefaultVehicleRequest;
 
-    // TODO: b/178729499 update this in favor of a method taking in a UID.
+    // TODO replace with INetd.DUMMY_NET_ID when available.
+    private static final int NO_SERVICE_NET_ID = 51;
+    // Sentinel NAI used to direct apps with default networks that should have no connectivity to a
+    // network with no service. This NAI should never be matched against, nor should any public API
+    // ever return the associated network. For this reason, this NAI is not in the list of available
+    // NAIs. It is used in computeNetworkReassignment() to be set as the satisfier for non-device
+    // default requests that don't support using the device default network which will ultimately
+    // allow ConnectivityService to use this no-service network when calling makeDefaultForApps().
+    @VisibleForTesting
+    final NetworkAgentInfo mNoServiceNetwork;
+
     // The NetworkAgentInfo currently satisfying the default request, if any.
     private NetworkAgentInfo getDefaultNetwork() {
         return mDefaultRequest.mSatisfier;
     }
 
+    private NetworkAgentInfo getDefaultNetworkForUid(final int uid) {
+        for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+            // Currently, all network requests will have the same uids therefore checking the first
+            // one is sufficient. If/when uids are tracked at the nri level, this can change.
+            final Set<UidRange> uids = nri.mRequests.get(0).networkCapabilities.getUids();
+            if (null == uids) {
+                continue;
+            }
+            for (final UidRange range : uids) {
+                if (range.contains(uid)) {
+                    return nri.getSatisfier();
+                }
+            }
+        }
+        return getDefaultNetwork();
+    }
+
     @Nullable
     private Network getNetwork(@Nullable NetworkAgentInfo nai) {
         return nai != null ? nai.network : null;
@@ -6165,8 +6209,6 @@
 
         LinkProperties lp = new LinkProperties(linkProperties);
 
-        // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
-        // satisfies mDefaultRequest.
         final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
         final NetworkAgentInfo nai = new NetworkAgentInfo(na,
                 new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
@@ -6589,7 +6631,8 @@
     @VisibleForTesting
     void applyUnderlyingCapabilities(@Nullable Network[] underlyingNetworks,
             @NonNull NetworkCapabilities agentCaps, @NonNull NetworkCapabilities newNc) {
-        underlyingNetworks = underlyingNetworksOrDefault(underlyingNetworks);
+        underlyingNetworks = underlyingNetworksOrDefault(
+                agentCaps.getOwnerUid(), underlyingNetworks);
         int[] transportTypes = agentCaps.getTransportTypes();
         int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
         int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
@@ -7195,7 +7238,7 @@
 
         // If we get here it means that the last linger timeout for this network expired. So there
         // must be no other active linger timers, and we must stop lingering.
-        oldNetwork.clearLingerState();
+        oldNetwork.clearInactivityState();
 
         if (unneeded(oldNetwork, UnneededFor.TEARDOWN)) {
             // Tear the network down.
@@ -7233,21 +7276,20 @@
             log("Switching to new default network for: " + nri + " using " + newDefaultNetwork);
         }
 
-        try {
-            // TODO http://b/176191930 update netd calls in follow-up CL for multinetwork changes.
-            if (mDefaultRequest != nri) {
-                return;
-            }
-
-            if (null != newDefaultNetwork) {
-                mNetd.networkSetDefault(newDefaultNetwork.network.getNetId());
-            } else {
-                mNetd.networkClearDefault();
-            }
-        } catch (RemoteException | ServiceSpecificException e) {
-            loge("Exception setting default network :" + e);
+        // Fix up the NetworkCapabilities of any networks that have this network as underlying.
+        if (newDefaultNetwork != null) {
+            propagateUnderlyingNetworkCapabilities(newDefaultNetwork.network);
         }
 
+        // Set an app level managed default and return since further processing only applies to the
+        // default network.
+        if (mDefaultRequest != nri) {
+            makeDefaultForApps(nri, oldDefaultNetwork, newDefaultNetwork);
+            return;
+        }
+
+        makeDefaultNetwork(newDefaultNetwork);
+
         if (oldDefaultNetwork != null) {
             mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
         }
@@ -7258,10 +7300,6 @@
         updateTcpBufferSizes(null != newDefaultNetwork
                 ? newDefaultNetwork.linkProperties.getTcpBufferSizes() : null);
         notifyIfacesChangedForNetworkStats();
-        // Fix up the NetworkCapabilities of any networks that have this network as underlying.
-        if (newDefaultNetwork != null) {
-            propagateUnderlyingNetworkCapabilities(newDefaultNetwork.network);
-        }
 
         // Log 0 -> X and Y -> X default network transitions, where X is the new default.
         final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
@@ -7285,6 +7323,49 @@
                 prevNetwork, prevScore, prevLp, prevNc);
     }
 
+    private void makeDefaultForApps(@NonNull final NetworkRequestInfo nri,
+            @Nullable final NetworkAgentInfo oldDefaultNetwork,
+            @Nullable final NetworkAgentInfo newDefaultNetwork) {
+        try {
+            if (VDBG) {
+                log("Setting default network for " + nri
+                        + " using UIDs " + nri.getUids()
+                        + " with old network " + (oldDefaultNetwork != null
+                        ? oldDefaultNetwork.network().getNetId() : "null")
+                        + " and new network " + (newDefaultNetwork != null
+                        ? newDefaultNetwork.network().getNetId() : "null"));
+            }
+            if (nri.getUids().isEmpty()) {
+                throw new IllegalStateException("makeDefaultForApps called without specifying"
+                        + " any applications to set as the default." + nri);
+            }
+            if (null != newDefaultNetwork) {
+                mNetd.networkAddUidRanges(
+                        newDefaultNetwork.network.getNetId(),
+                        toUidRangeStableParcels(nri.getUids()));
+            }
+            if (null != oldDefaultNetwork) {
+                mNetd.networkRemoveUidRanges(
+                        oldDefaultNetwork.network.getNetId(),
+                        toUidRangeStableParcels(nri.getUids()));
+            }
+        } catch (RemoteException | ServiceSpecificException e) {
+            loge("Exception setting OEM network preference default network :" + e);
+        }
+    }
+
+    private void makeDefaultNetwork(@Nullable final NetworkAgentInfo newDefaultNetwork) {
+        try {
+            if (null != newDefaultNetwork) {
+                mNetd.networkSetDefault(newDefaultNetwork.network.getNetId());
+            } else {
+                mNetd.networkClearDefault();
+            }
+        } catch (RemoteException | ServiceSpecificException e) {
+            loge("Exception setting default network :" + e);
+        }
+    }
+
     private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
         // For consistency with previous behaviour, send onLost callbacks before onAvailable.
         processNewlyLostListenRequests(nai);
@@ -7406,9 +7487,9 @@
             @Nullable final NetworkAgentInfo previousSatisfier,
             @Nullable final NetworkAgentInfo newSatisfier,
             final long now) {
-        if (newSatisfier != null) {
+        if (null != newSatisfier && mNoServiceNetwork != newSatisfier) {
             if (VDBG) log("rematch for " + newSatisfier.toShortString());
-            if (previousSatisfier != null) {
+            if (null != previousSatisfier && mNoServiceNetwork != previousSatisfier) {
                 if (VDBG || DDBG) {
                     log("   accepting network in place of " + previousSatisfier.toShortString());
                 }
@@ -7422,7 +7503,7 @@
                 Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
                         + newRequest);
             }
-        } else {
+        } else if (null != previousSatisfier) {
             if (DBG) {
                 log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
                         + " request " + previousRequest.requestId);
@@ -7473,7 +7554,11 @@
                     break;
                 }
             }
-            if (bestNetwork != nri.mSatisfier) {
+            if (null == bestNetwork && isDefaultBlocked(nri)) {
+                // Remove default networking if disallowed for managed default requests.
+                bestNetwork = mNoServiceNetwork;
+            }
+            if (nri.getSatisfier() != bestNetwork) {
                 // bestNetwork may be null if no network can satisfy this request.
                 changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
                         nri, nri.mActiveRequest, bestRequest, nri.getSatisfier(), bestNetwork));
@@ -7566,7 +7651,7 @@
             // if the state has not changed : the source of truth is controlled with
             // NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which have been
             // called while rematching the individual networks above.
-            if (updateLingerState(nai, now)) {
+            if (updateInactivityState(nai, now)) {
                 lingeredNetworks.add(nai);
             }
         }
@@ -7593,7 +7678,7 @@
         // Tear down all unneeded networks.
         for (NetworkAgentInfo nai : mNetworkAgentInfos) {
             if (unneeded(nai, UnneededFor.TEARDOWN)) {
-                if (nai.getLingerExpiry() > 0) {
+                if (nai.getInactivityExpiry() > 0) {
                     // This network has active linger timers and no requests, but is not
                     // lingering. Linger it.
                     //
@@ -7601,7 +7686,7 @@
                     // and became unneeded due to another network improving its score to the
                     // point where this network will no longer be able to satisfy any requests
                     // even if it validates.
-                    if (updateLingerState(nai, now)) {
+                    if (updateInactivityState(nai, now)) {
                         notifyNetworkLosing(nai, now);
                     }
                 } else {
@@ -7878,7 +7963,7 @@
 
     // Notify the requests on this NAI that the network is now lingered.
     private void notifyNetworkLosing(@NonNull final NetworkAgentInfo nai, final long now) {
-        final int lingerTime = (int) (nai.getLingerExpiry() - now);
+        final int lingerTime = (int) (nai.getInactivityExpiry() - now);
         notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
     }
 
@@ -7977,7 +8062,7 @@
             }
             NetworkAgentInfo newDefaultAgent = null;
             if (nai.isSatisfyingRequest(mDefaultRequest.mRequests.get(0).requestId)) {
-                newDefaultAgent = getDefaultNetwork();
+                newDefaultAgent = mDefaultRequest.getSatisfier();
                 if (newDefaultAgent != null) {
                     intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO,
                             newDefaultAgent.networkInfo);
@@ -8025,9 +8110,14 @@
     private Network[] getDefaultNetworks() {
         ensureRunningOnConnectivityServiceThread();
         final ArrayList<Network> defaultNetworks = new ArrayList<>();
-        final NetworkAgentInfo defaultNetwork = getDefaultNetwork();
+        final Set<Integer> activeNetIds = new ArraySet<>();
+        for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
+            if (nri.isBeingSatisfied()) {
+                activeNetIds.add(nri.getSatisfier().network().netId);
+            }
+        }
         for (NetworkAgentInfo nai : mNetworkAgentInfos) {
-            if (nai.everConnected && (nai == defaultNetwork || nai.isVPN())) {
+            if (nai.everConnected && (activeNetIds.contains(nai.network().netId) || nai.isVPN())) {
                 defaultNetworks.add(nai.network);
             }
         }
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 916bec2..4dce59f 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -66,6 +66,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -291,8 +292,9 @@
                 @NonNull VcnContext vcnContext,
                 @NonNull ParcelUuid subscriptionGroup,
                 @NonNull VcnConfig config,
-                @NonNull TelephonySubscriptionSnapshot snapshot) {
-            return new Vcn(vcnContext, subscriptionGroup, config, snapshot);
+                @NonNull TelephonySubscriptionSnapshot snapshot,
+                @NonNull VcnSafemodeCallback safemodeCallback) {
+            return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safemodeCallback);
         }
 
         /** Gets the subId indicated by the given {@link WifiInfo}. */
@@ -438,7 +440,12 @@
         // TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active
         //                    VCN.
 
-        final Vcn newInstance = mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot);
+        final VcnSafemodeCallbackImpl safemodeCallback =
+                new VcnSafemodeCallbackImpl(subscriptionGroup);
+
+        final Vcn newInstance =
+                mDeps.newVcn(
+                        mVcnContext, subscriptionGroup, config, mLastSnapshot, safemodeCallback);
         mVcns.put(subscriptionGroup, newInstance);
 
         // Now that a new VCN has started, notify all registered listeners to refresh their
@@ -536,7 +543,7 @@
         }
     }
 
-    /** Get current configuration list for testing purposes */
+    /** Get current VCNs for testing purposes */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public Map<ParcelUuid, Vcn> getAllVcns() {
         synchronized (mLock) {
@@ -638,8 +645,8 @@
             synchronized (mLock) {
                 ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId);
 
-                // TODO(b/178140910): only mark the Network as VCN-managed if not in safe mode
-                if (mVcns.containsKey(subGroup)) {
+                Vcn vcn = mVcns.get(subGroup);
+                if (vcn != null && vcn.isActive()) {
                     isVcnManagedNetwork = true;
                 }
             }
@@ -651,4 +658,31 @@
 
         return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities);
     }
+
+    /** Callback for signalling when a Vcn has entered Safemode. */
+    public interface VcnSafemodeCallback {
+        /** Called by a Vcn to signal that it has entered Safemode. */
+        void onEnteredSafemode();
+    }
+
+    /** VcnSafemodeCallback is used by Vcns to notify VcnManagementService on entering Safemode. */
+    private class VcnSafemodeCallbackImpl implements VcnSafemodeCallback {
+        @NonNull private final ParcelUuid mSubGroup;
+
+        private VcnSafemodeCallbackImpl(@NonNull final ParcelUuid subGroup) {
+            mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup");
+        }
+
+        @Override
+        public void onEnteredSafemode() {
+            synchronized (mLock) {
+                // Ignore if this subscription group doesn't exist anymore
+                if (!mVcns.containsKey(mSubGroup)) {
+                    return;
+                }
+
+                notifyAllPolicyListenersLocked();
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a43f0c6..e7e9832 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -300,6 +300,7 @@
     private static final int MSG_STREAM_DEVICES_CHANGED = 32;
     private static final int MSG_UPDATE_VOLUME_STATES_FOR_DEVICE = 33;
     private static final int MSG_REINIT_VOLUMES = 34;
+    private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35;
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -699,8 +700,9 @@
     private long mLoweredFromNormalToVibrateTime;
 
     // Array of Uids of valid accessibility services to check if caller is one of them
-    private int[] mAccessibilityServiceUids;
     private final Object mAccessibilityServiceUidsLock = new Object();
+    @GuardedBy("mAccessibilityServiceUidsLock")
+    private int[] mAccessibilityServiceUids;
 
     // Uid of the active input method service to check if caller is the one or not.
     private int mInputMethodServiceUid = android.os.Process.INVALID_UID;
@@ -2311,11 +2313,9 @@
     /** @see AudioManager#adjustVolume(int, int) */
     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
             String callingPackage, String caller) {
-        boolean hasModifyAudioSettings =
-                mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
-                == PackageManager.PERMISSION_GRANTED;
         adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
-                caller, Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+                caller, Binder.getCallingUid(), callingHasAudioSettingsPermission(),
+                VOL_ADJUST_NORMAL);
     }
 
     public void setFastScrollSoundEffectsEnabled(boolean enabled) {
@@ -2442,13 +2442,12 @@
                     + "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage);
             return;
         }
-        final boolean hasModifyAudioSettings =
-                mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
-                        == PackageManager.PERMISSION_GRANTED;
+
         sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_ADJUST_STREAM_VOL, streamType,
                 direction/*val1*/, flags/*val2*/, callingPackage));
         adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage,
-                Binder.getCallingUid(), hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+                Binder.getCallingUid(), callingHasAudioSettingsPermission(),
+                VOL_ADJUST_NORMAL);
     }
 
     protected void adjustStreamVolume(int streamType, int direction, int flags,
@@ -2966,13 +2965,11 @@
                     + " MODIFY_AUDIO_ROUTING  callingPackage=" + callingPackage);
             return;
         }
-        final boolean hasModifyAudioSettings =
-                mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
-                        == PackageManager.PERMISSION_GRANTED;
+
         sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
                 index/*val1*/, flags/*val2*/, callingPackage));
         setStreamVolume(streamType, index, flags, callingPackage, callingPackage,
-                Binder.getCallingUid(), hasModifyAudioSettings);
+                Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
     }
 
     private boolean canChangeAccessibilityVolume() {
@@ -3639,8 +3636,7 @@
         ensureValidStreamType(streamType);
         final boolean isPrivileged =
                 Binder.getCallingUid() == Process.SYSTEM_UID
-                 || (mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
-                        == PackageManager.PERMISSION_GRANTED)
+                 || callingHasAudioSettingsPermission()
                  || (mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
                         == PackageManager.PERMISSION_GRANTED);
         return (mStreamStates[streamType].getMinIndex(isPrivileged) + 5) / 10;
@@ -4383,13 +4379,10 @@
             throw new SecurityException("Should only be called from system process");
         }
 
-        final boolean hasModifyAudioSettings =
-                mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
-                        == PackageManager.PERMISSION_GRANTED;
         // direction and stream type swap here because the public
         // adjustSuggested has a different order than the other methods.
         adjustSuggestedStreamVolume(direction, streamType, flags, packageName, packageName, uid,
-                hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+                hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
     }
 
     /** @see AudioManager#adjustStreamVolumeForUid(int, int, int, String, int, int, int) */
@@ -4407,11 +4400,9 @@
                     new StringBuilder(packageName).append(" uid:").append(uid)
                     .toString()));
         }
-        final boolean hasModifyAudioSettings =
-                mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
-                        == PackageManager.PERMISSION_GRANTED;
+
         adjustStreamVolume(streamType, direction, flags, packageName, packageName, uid,
-                hasModifyAudioSettings, VOL_ADJUST_NORMAL);
+                hasAudioSettingsPermission(uid, pid), VOL_ADJUST_NORMAL);
     }
 
     /** @see AudioManager#setStreamVolumeForUid(int, int, int, String, int, int, int) */
@@ -4423,11 +4414,8 @@
             throw new SecurityException("Should only be called from system process");
         }
 
-        final boolean hasModifyAudioSettings =
-                mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
-                        == PackageManager.PERMISSION_GRANTED;
         setStreamVolume(streamType, index, flags, packageName, packageName, uid,
-                hasModifyAudioSettings);
+                hasAudioSettingsPermission(uid, pid));
     }
 
     //==========================================================================================
@@ -5393,8 +5381,7 @@
     }
 
     boolean checkAudioSettingsPermission(String method) {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS)
-                == PackageManager.PERMISSION_GRANTED) {
+        if (callingOrSelfHasAudioSettingsPermission()) {
             return true;
         }
         String msg = "Audio Settings Permission Denial: " + method + " from pid="
@@ -5404,6 +5391,21 @@
         return false;
     }
 
+    private boolean callingOrSelfHasAudioSettingsPermission() {
+        return mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private boolean callingHasAudioSettingsPermission() {
+        return mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private boolean hasAudioSettingsPermission(int uid, int pid) {
+        return mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
     /**
      * Minimum attenuation that can be set for alarms over speaker by an application that
      * doesn't have the MODIFY_AUDIO_SETTINGS permission.
@@ -7082,6 +7084,10 @@
                 case MSG_REINIT_VOLUMES:
                     onReinitVolumes((String) msg.obj);
                     break;
+                case MSG_UPDATE_A11Y_SERVICE_UIDS:
+                    onUpdateAccessibilityServiceUids();
+                    break;
+
             }
         }
     }
@@ -8148,6 +8154,9 @@
                         + " FromRestrictions=" + mMicMuteFromRestrictions
                         + " FromApi=" + mMicMuteFromApi
                         + " from system=" + mMicMuteFromSystemCached);
+        pw.print("\n  mAssistantUid="); pw.println(mAssistantUid);
+        pw.print("  mCurrentImeUid="); pw.println(mCurrentImeUid);
+        dumpAccessibilityServiceUids(pw);
 
         dumpAudioPolicies(pw);
         mDynPolicyLogger.dump(pw);
@@ -8181,6 +8190,19 @@
         }
     }
 
+    private void dumpAccessibilityServiceUids(PrintWriter pw) {
+        synchronized (mSupportedSystemUsagesLock) {
+            if (mAccessibilityServiceUids != null && mAccessibilityServiceUids.length > 0) {
+                pw.println("  Accessibility service Uids:");
+                for (int uid : mAccessibilityServiceUids) {
+                    pw.println("  - " + uid);
+                }
+            } else {
+                pw.println("  No accessibility service Uids.");
+            }
+        }
+    }
+
     /**
      * Audio Analytics ids.
      */
@@ -8484,7 +8506,8 @@
                         mAccessibilityServiceUids = uids.toArray();
                     }
                 }
-                AudioSystem.setA11yServicesUids(mAccessibilityServiceUids);
+                sendMsg(mAudioHandler, MSG_UPDATE_A11Y_SERVICE_UIDS, SENDMSG_REPLACE,
+                        0, 0, null, 0);
             }
         }
 
@@ -8502,6 +8525,14 @@
         }
     }
 
+    private void onUpdateAccessibilityServiceUids() {
+        int[] accessibilityServiceUids;
+        synchronized (mAccessibilityServiceUidsLock) {
+            accessibilityServiceUids = mAccessibilityServiceUids;
+        }
+        AudioSystem.setA11yServicesUids(accessibilityServiceUids);
+    }
+
     //==========================================================================================
     // Audio policy management
     //==========================================================================================
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 1a4f20c7..a9a705f 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -210,23 +210,23 @@
     // network is taken down.  This usually only happens to the default network. Lingering ends with
     // either the linger timeout expiring and the network being taken down, or the network
     // satisfying a request again.
-    public static class LingerTimer implements Comparable<LingerTimer> {
+    public static class InactivityTimer implements Comparable<InactivityTimer> {
         public final int requestId;
         public final long expiryMs;
 
-        public LingerTimer(int requestId, long expiryMs) {
+        public InactivityTimer(int requestId, long expiryMs) {
             this.requestId = requestId;
             this.expiryMs = expiryMs;
         }
         public boolean equals(Object o) {
-            if (!(o instanceof LingerTimer)) return false;
-            LingerTimer other = (LingerTimer) o;
+            if (!(o instanceof InactivityTimer)) return false;
+            InactivityTimer other = (InactivityTimer) o;
             return (requestId == other.requestId) && (expiryMs == other.expiryMs);
         }
         public int hashCode() {
             return Objects.hash(requestId, expiryMs);
         }
-        public int compareTo(LingerTimer other) {
+        public int compareTo(InactivityTimer other) {
             return (expiryMs != other.expiryMs) ?
                     Long.compare(expiryMs, other.expiryMs) :
                     Integer.compare(requestId, other.requestId);
@@ -269,30 +269,31 @@
      */
     public static final int ARG_AGENT_SUCCESS = 1;
 
-    // All linger timers for this network, sorted by expiry time. A linger timer is added whenever
+    // All inactivity timers for this network, sorted by expiry time. A timer is added whenever
     // a request is moved to a network with a better score, regardless of whether the network is or
     // was lingering or not.
     // TODO: determine if we can replace this with a smaller or unsorted data structure. (e.g.,
     // SparseLongArray) combined with the timestamp of when the last timer is scheduled to fire.
-    private final SortedSet<LingerTimer> mLingerTimers = new TreeSet<>();
+    private final SortedSet<InactivityTimer> mInactivityTimers = new TreeSet<>();
 
-    // For fast lookups. Indexes into mLingerTimers by request ID.
-    private final SparseArray<LingerTimer> mLingerTimerForRequest = new SparseArray<>();
+    // For fast lookups. Indexes into mInactivityTimers by request ID.
+    private final SparseArray<InactivityTimer> mInactivityTimerForRequest = new SparseArray<>();
 
-    // Linger expiry timer. Armed whenever mLingerTimers is non-empty, regardless of whether the
-    // network is lingering or not. Always set to the expiry of the LingerTimer that expires last.
-    // When the timer fires, all linger state is cleared, and if the network has no requests, it is
-    // torn down.
-    private WakeupMessage mLingerMessage;
+    // Inactivity expiry timer. Armed whenever mInactivityTimers is non-empty, regardless of
+    // whether the network is inactive or not. Always set to the expiry of the mInactivityTimers
+    // that expires last. When the timer fires, all inactivity state is cleared, and if the network
+    // has no requests, it is torn down.
+    private WakeupMessage mInactivityMessage;
 
-    // Linger expiry. Holds the expiry time of the linger timer, or 0 if the timer is not armed.
-    private long mLingerExpiryMs;
+    // Inactivity expiry. Holds the expiry time of the inactivity timer, or 0 if the timer is not
+    // armed.
+    private long mInactivityExpiryMs;
 
-    // Whether the network is lingering or not. Must be maintained separately from the above because
+    // Whether the network is inactive or not. Must be maintained separately from the above because
     // it depends on the state of other networks and requests, which only ConnectivityService knows.
     // (Example: we don't linger a network if it would become the best for a NetworkRequest if it
     // validated).
-    private boolean mLingering;
+    private boolean mInactive;
 
     // This represents the quality of the network with no clear scale.
     private int mScore;
@@ -898,17 +899,17 @@
      * ConnectivityService when the request is moved to another network with a higher score.
      */
     public void lingerRequest(int requestId, long now, long duration) {
-        if (mLingerTimerForRequest.get(requestId) != null) {
+        if (mInactivityTimerForRequest.get(requestId) != null) {
             // Cannot happen. Once a request is lingering on a particular network, we cannot
             // re-linger it unless that network becomes the best for that request again, in which
             // case we should have unlingered it.
             Log.wtf(TAG, toShortString() + ": request " + requestId + " already lingered");
         }
         final long expiryMs = now + duration;
-        LingerTimer timer = new LingerTimer(requestId, expiryMs);
-        if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString());
-        mLingerTimers.add(timer);
-        mLingerTimerForRequest.put(requestId, timer);
+        InactivityTimer timer = new InactivityTimer(requestId, expiryMs);
+        if (VDBG) Log.d(TAG, "Adding InactivityTimer " + timer + " to " + toShortString());
+        mInactivityTimers.add(timer);
+        mInactivityTimerForRequest.put(requestId, timer);
     }
 
     /**
@@ -916,23 +917,25 @@
      * Returns true if the given requestId was lingering on this network, false otherwise.
      */
     public boolean unlingerRequest(int requestId) {
-        LingerTimer timer = mLingerTimerForRequest.get(requestId);
+        InactivityTimer timer = mInactivityTimerForRequest.get(requestId);
         if (timer != null) {
-            if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString());
-            mLingerTimers.remove(timer);
-            mLingerTimerForRequest.remove(requestId);
+            if (VDBG) {
+                Log.d(TAG, "Removing InactivityTimer " + timer + " from " + toShortString());
+            }
+            mInactivityTimers.remove(timer);
+            mInactivityTimerForRequest.remove(requestId);
             return true;
         }
         return false;
     }
 
-    public long getLingerExpiry() {
-        return mLingerExpiryMs;
+    public long getInactivityExpiry() {
+        return mInactivityExpiryMs;
     }
 
-    public void updateLingerTimer() {
-        long newExpiry = mLingerTimers.isEmpty() ? 0 : mLingerTimers.last().expiryMs;
-        if (newExpiry == mLingerExpiryMs) return;
+    public void updateInactivityTimer() {
+        long newExpiry = mInactivityTimers.isEmpty() ? 0 : mInactivityTimers.last().expiryMs;
+        if (newExpiry == mInactivityExpiryMs) return;
 
         // Even if we're going to reschedule the timer, cancel it first. This is because the
         // semantics of WakeupMessage guarantee that if cancel is called then the alarm will
@@ -940,49 +943,52 @@
         // WakeupMessage makes no such guarantees about rescheduling a message, so if mLingerMessage
         // has already been dispatched, rescheduling to some time in the future won't stop it
         // from calling its callback immediately.
-        if (mLingerMessage != null) {
-            mLingerMessage.cancel();
-            mLingerMessage = null;
+        if (mInactivityMessage != null) {
+            mInactivityMessage.cancel();
+            mInactivityMessage = null;
         }
 
         if (newExpiry > 0) {
-            mLingerMessage = new WakeupMessage(
+            mInactivityMessage = new WakeupMessage(
                     mContext, mHandler,
                     "NETWORK_LINGER_COMPLETE." + network.getNetId() /* cmdName */,
                     EVENT_NETWORK_LINGER_COMPLETE /* cmd */,
                     0 /* arg1 (unused) */, 0 /* arg2 (unused) */,
                     this /* obj (NetworkAgentInfo) */);
-            mLingerMessage.schedule(newExpiry);
+            mInactivityMessage.schedule(newExpiry);
         }
 
-        mLingerExpiryMs = newExpiry;
+        mInactivityExpiryMs = newExpiry;
     }
 
-    public void linger() {
-        mLingering = true;
+    public void setInactive() {
+        mInactive = true;
     }
 
-    public void unlinger() {
-        mLingering = false;
+    public void unsetInactive() {
+        mInactive = false;
     }
 
     public boolean isLingering() {
-        return mLingering;
+        return mInactive;
     }
 
-    public void clearLingerState() {
-        if (mLingerMessage != null) {
-            mLingerMessage.cancel();
-            mLingerMessage = null;
+    public void clearInactivityState() {
+        if (mInactivityMessage != null) {
+            mInactivityMessage.cancel();
+            mInactivityMessage = null;
         }
-        mLingerTimers.clear();
-        mLingerTimerForRequest.clear();
-        updateLingerTimer();  // Sets mLingerExpiryMs, cancels and nulls out mLingerMessage.
-        mLingering = false;
+        mInactivityTimers.clear();
+        mInactivityTimerForRequest.clear();
+        // Sets mInactivityExpiryMs, cancels and nulls out mInactivityMessage.
+        updateInactivityTimer();
+        mInactive = false;
     }
 
-    public void dumpLingerTimers(PrintWriter pw) {
-        for (LingerTimer timer : mLingerTimers) { pw.println(timer); }
+    public void dumpInactivityTimers(PrintWriter pw) {
+        for (InactivityTimer timer : mInactivityTimers) {
+            pw.println(timer);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index d956ba3..e8062ae 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -203,6 +203,7 @@
     protected final NetworkCapabilities mNetworkCapabilities;
     private final SystemServices mSystemServices;
     private final Ikev2SessionCreator mIkev2SessionCreator;
+    private final UserManager mUserManager;
 
     /**
      * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
@@ -277,6 +278,10 @@
             return LocalServices.getService(DeviceIdleInternal.class);
         }
 
+        public PendingIntent getIntentForStatusPanel(Context context) {
+            return VpnConfig.getIntentForStatusPanel(context);
+        }
+
         public void sendArgumentsToDaemon(
                 final String daemon, final LocalSocket socket, final String[] arguments,
                 final RetryScheduler retryScheduler) throws IOException, InterruptedException {
@@ -405,6 +410,7 @@
         mLooper = looper;
         mSystemServices = systemServices;
         mIkev2SessionCreator = ikev2SessionCreator;
+        mUserManager = mContext.getSystemService(UserManager.class);
 
         mPackage = VpnConfig.LEGACY_VPN;
         mOwnerUID = getAppUid(mPackage, mUserId);
@@ -1431,7 +1437,7 @@
             final long token = Binder.clearCallingIdentity();
             List<UserInfo> users;
             try {
-                users = UserManager.get(mContext).getAliveUsers();
+                users = mUserManager.getAliveUsers();
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1515,7 +1521,7 @@
      */
     public void onUserAdded(int userId) {
         // If the user is restricted tie them to the parent user's VPN
-        UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+        UserInfo user = mUserManager.getUserInfo(userId);
         if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
             synchronized(Vpn.this) {
                 final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1543,7 +1549,7 @@
      */
     public void onUserRemoved(int userId) {
         // clean up if restricted
-        UserInfo user = UserManager.get(mContext).getUserInfo(userId);
+        UserInfo user = mUserManager.getUserInfo(userId);
         if (user.isRestricted() && user.restrictedProfileParentId == mUserId) {
             synchronized(Vpn.this) {
                 final Set<UidRange> existingRanges = mNetworkCapabilities.getUids();
@@ -1768,7 +1774,7 @@
     private void prepareStatusIntent() {
         final long token = Binder.clearCallingIdentity();
         try {
-            mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext);
+            mStatusIntent = mDeps.getIntentForStatusPanel(mContext);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -1968,8 +1974,7 @@
 
     private void enforceNotRestrictedUser() {
         Binder.withCleanCallingIdentity(() -> {
-            final UserManager mgr = UserManager.get(mContext);
-            final UserInfo user = mgr.getUserInfo(mUserId);
+            final UserInfo user = mUserManager.getUserInfo(mUserId);
 
             if (user.isRestricted()) {
                 throw new SecurityException("Restricted users cannot configure VPNs");
@@ -2004,9 +2009,8 @@
      */
     public void startLegacyVpnPrivileged(VpnProfile profile, KeyStore keyStore,
             @Nullable Network underlying, @NonNull LinkProperties egress) {
-        UserManager mgr = UserManager.get(mContext);
-        UserInfo user = mgr.getUserInfo(mUserId);
-        if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
+        UserInfo user = mUserManager.getUserInfo(mUserId);
+        if (user.isRestricted() || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
                     new UserHandle(mUserId))) {
             throw new SecurityException("Restricted users cannot establish VPNs");
         }
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index afc97c7..dac94f6 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -27,6 +27,7 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.FontConfig;
+import android.util.ArrayMap;
 import android.util.Base64;
 import android.util.Slog;
 
@@ -39,7 +40,6 @@
 import java.io.IOException;
 import java.security.SecureRandom;
 import java.time.Instant;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -116,7 +116,7 @@
      * FontFileInfo}. All files in this map are validated, and have higher revision numbers than
      * corresponding font files in {@link #mPreinstalledFontDirs}.
      */
-    private final Map<String, FontFileInfo> mFontFileInfoMap = new HashMap<>();
+    private final ArrayMap<String, FontFileInfo> mFontFileInfoMap = new ArrayMap<>();
 
     UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser,
             FsverityUtil fsverityUtil) {
@@ -205,7 +205,7 @@
      */
     public void update(List<FontUpdateRequest> requests) throws SystemFontException {
         // Backup the mapping for rollback.
-        HashMap<String, FontFileInfo> backupMap = new HashMap<>(mFontFileInfoMap);
+        ArrayMap<String, FontFileInfo> backupMap = new ArrayMap<>(mFontFileInfoMap);
         long backupLastModifiedDate = mLastModifiedDate;
         boolean success = false;
         try {
@@ -464,7 +464,7 @@
     }
 
     Map<String, File> getFontFileMap() {
-        Map<String, File> map = new HashMap<>();
+        Map<String, File> map = new ArrayMap<>();
         for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
             map.put(entry.getKey(), entry.getValue().getFile());
         }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index e5b5350..d8e124a0 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -110,7 +110,6 @@
 import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -300,45 +299,6 @@
     private static final String ACTION_SHOW_INPUT_METHOD_PICKER =
             "com.android.server.inputmethod.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER";
 
-    /**
-     * Debug flag for overriding runtime {@link SystemProperties}.
-     */
-    @AnyThread
-    private static final class DebugFlag {
-        private static final Object LOCK = new Object();
-        private final String mKey;
-        private final boolean mDefaultValue;
-        @GuardedBy("LOCK")
-        private boolean mValue;
-
-        public DebugFlag(String key, boolean defaultValue) {
-            mKey = key;
-            mDefaultValue = defaultValue;
-            mValue = SystemProperties.getBoolean(key, defaultValue);
-        }
-
-        void refresh() {
-            synchronized (LOCK) {
-                mValue = SystemProperties.getBoolean(mKey, mDefaultValue);
-            }
-        }
-
-        boolean value() {
-            synchronized (LOCK) {
-                return mValue;
-            }
-        }
-    }
-
-    /**
-     * Debug flags that can be overridden using "adb shell setprop <key>"
-     * Note: These flags are cached. To refresh, run "adb shell ime refresh_debug_properties".
-     */
-    private static final class DebugFlags {
-        static final DebugFlag FLAG_OPTIMIZE_START_INPUT =
-                new DebugFlag("debug.optimize_startinput", false);
-    }
-
     @UserIdInt
     private int mLastSwitchUserId;
 
@@ -3687,12 +3647,9 @@
                     }
                     res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
                             startInputFlags, startInputReason);
-                } else if (!DebugFlags.FLAG_OPTIMIZE_START_INPUT.value()
-                        || (startInputFlags & StartInputFlags.IS_TEXT_EDITOR) != 0) {
+                } else {
                     res = startInputUncheckedLocked(cs, inputContext, missingMethods, attribute,
                             startInputFlags, startInputReason);
-                } else {
-                    res = InputBindResult.NO_EDITOR;
                 }
             } else {
                 res = InputBindResult.NULL_EDITOR_INFO;
@@ -5467,10 +5424,6 @@
         @BinderThread
         @ShellCommandResult
         private int onCommandWithSystemIdentity(@Nullable String cmd) {
-            if ("refresh_debug_properties".equals(cmd)) {
-                return refreshDebugProperties();
-            }
-
             if ("get-last-switch-user-id".equals(cmd)) {
                 return mService.getLastSwitchUserId(this);
             }
@@ -5505,13 +5458,6 @@
         }
 
         @BinderThread
-        @ShellCommandResult
-        private int refreshDebugProperties() {
-            DebugFlags.FLAG_OPTIMIZE_START_INPUT.refresh();
-            return ShellCommandResult.SUCCESS;
-        }
-
-        @BinderThread
         @Override
         public void onHelp() {
             try (PrintWriter pw = getOutPrintWriter()) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index 02a36dc..f646d5d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -220,7 +220,8 @@
      */
     @VisibleForTesting
     public Context getSettingsContext(int displayId) {
-        if (mSettingsContext == null) {
+        // TODO(b/178462039): Cover the case when IME is moved to another ImeContainer.
+        if (mSettingsContext == null || mSettingsContext.getDisplayId() != displayId) {
             final Context systemUiContext = ActivityThread.currentActivityThread()
                     .createSystemUiContext(displayId);
             final Context windowContext = systemUiContext.createWindowContext(
@@ -229,11 +230,6 @@
                     windowContext, com.android.internal.R.style.Theme_DeviceDefault_Settings);
             mSwitchingDialogToken = mSettingsContext.getWindowContextToken();
         }
-        // TODO(b/159767464): register the listener to another display again if window token is not
-        // yet created.
-        if (mSettingsContext.getDisplayId() != displayId) {
-            mWindowManagerInternal.moveWindowTokenToDisplay(mSwitchingDialogToken, displayId);
-        }
         return mSettingsContext;
     }
 
diff --git a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
similarity index 98%
rename from services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
rename to services/core/java/com/android/server/location/eventlog/LocalEventLog.java
index fe51d74..b5746bb 100644
--- a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
+++ b/services/core/java/com/android/server/location/eventlog/LocalEventLog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.utils.eventlog;
+package com.android.server.location.eventlog;
 
 import android.os.SystemClock;
 import android.util.TimeUtils;
diff --git a/services/core/java/com/android/server/location/injector/LocationEventLog.java b/services/core/java/com/android/server/location/injector/LocationEventLog.java
index b8b54b3..8d73518 100644
--- a/services/core/java/com/android/server/location/injector/LocationEventLog.java
+++ b/services/core/java/com/android/server/location/injector/LocationEventLog.java
@@ -31,7 +31,7 @@
 import android.os.Build;
 import android.os.PowerManager.LocationPowerSaveMode;
 
-import com.android.server.utils.eventlog.LocalEventLog;
+import com.android.server.location.eventlog.LocalEventLog;
 
 /** In memory event log for location events. */
 public class LocationEventLog extends LocalEventLog {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index f92f3dc..39ed7e8 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,8 +16,6 @@
 
 package com.android.server.net;
 
-import static com.android.server.net.NetworkPolicyManagerService.isUidNetworkingBlockedInternal;
-
 import android.annotation.NonNull;
 import android.net.Network;
 import android.net.NetworkTemplate;
@@ -39,28 +37,6 @@
     public abstract void resetUserState(int userId);
 
     /**
-     * Figure out if networking is blocked for a given set of conditions.
-     *
-     * This is used by ConnectivityService via passing stale copies of conditions, so it must not
-     * take any locks.
-     *
-     * @param uid The target uid.
-     * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
-     * @param isNetworkMetered True if the network is metered.
-     * @param isBackgroundRestricted True if data saver is enabled.
-     *
-     * @return true if networking is blocked for the UID under the specified conditions.
-     */
-    public static boolean isUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered,
-            boolean isBackgroundRestricted) {
-        // Log of invoking internal function is disabled because it will be called very
-        // frequently. And metrics are unlikely needed on this method because the callers are
-        // external and this method doesn't take any locks or perform expensive operations.
-        return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
-                isBackgroundRestricted, null);
-    }
-
-    /**
      * Informs that an appId has been added or removed from the temp-powersave-allowlist so that
      * that network rules for that appId can be updated.
      *
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index ca21640..b99a552 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -5402,6 +5402,17 @@
     }
 
     @Override
+    public boolean checkUidNetworkingBlocked(int uid, int uidRules,
+            boolean isNetworkMetered, boolean isBackgroundRestricted) {
+        mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
+        // Log of invoking this function is disabled because it will be called very frequently. And
+        // metrics are unlikely needed on this method because the callers are external and this
+        // method doesn't take any locks or perform expensive operations.
+        return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+                isBackgroundRestricted, null);
+    }
+
+    @Override
     public boolean isUidRestrictedOnMeteredNetworks(int uid) {
         mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
         final int uidRules;
@@ -5410,9 +5421,9 @@
             uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
             isBackgroundRestricted = mRestrictBackground;
         }
-        //TODO(b/177490332): The logic here might not be correct because it doesn't consider
-        // RULE_REJECT_METERED condition. And it could be replaced by
-        // isUidNetworkingBlockedInternal().
+        // TODO(b/177490332): The logic here might not be correct because it doesn't consider
+        //  RULE_REJECT_METERED condition. And it could be replaced by
+        //  isUidNetworkingBlockedInternal().
         return isBackgroundRestricted
                 && !hasRule(uidRules, RULE_ALLOW_METERED)
                 && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED);
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 461d519..619fc4e 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -23,8 +23,8 @@
 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
 
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index de77372..18c689f 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -21,8 +21,8 @@
 import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
 import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
 import static android.service.notification.DNDModeProto.ROOT_CONFIG;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
 
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 
 import android.app.AppOpsManager;
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 5373f99..66ea554 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -23,7 +23,6 @@
 import static android.content.pm.Checksum.TYPE_WHOLE_SHA1;
 import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
 import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
-import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
 import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION;
 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
@@ -32,15 +31,15 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
 import android.content.pm.ApkChecksum;
 import android.content.pm.Checksum;
+import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageParser;
 import android.content.pm.Signature;
 import android.content.pm.parsing.ApkLiteParseUtils;
 import android.os.Handler;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.incremental.IncrementalManager;
 import android.os.incremental.IncrementalStorage;
@@ -294,21 +293,21 @@
     /**
      * Fetch or calculate checksums for the collection of files.
      *
-     * @param filesToChecksum       split name, null for base and File to fetch checksums for
-     * @param optional              mask to fetch readily available checksums
-     * @param required              mask to forcefully calculate if not available
-     * @param installerPackageName  package name of the installer of the packages
-     * @param trustedInstallers     array of certificate to trust, two specific cases:
-     *                              null - trust anybody,
-     *                              [] - trust nobody.
-     * @param statusReceiver        to receive the resulting checksums
+     * @param filesToChecksum          split name, null for base and File to fetch checksums for
+     * @param optional                 mask to fetch readily available checksums
+     * @param required                 mask to forcefully calculate if not available
+     * @param installerPackageName     package name of the installer of the packages
+     * @param trustedInstallers        array of certificate to trust, two specific cases:
+     *                                 null - trust anybody,
+     *                                 [] - trust nobody.
+     * @param onChecksumsReadyListener to receive the resulting checksums
      */
     public static void getChecksums(List<Pair<String, File>> filesToChecksum,
             @Checksum.Type int optional,
             @Checksum.Type int required,
             @Nullable String installerPackageName,
             @Nullable Certificate[] trustedInstallers,
-            @NonNull IntentSender statusReceiver,
+            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
             @NonNull Injector injector) {
         List<Map<Integer, ApkChecksum>> result = new ArrayList<>(filesToChecksum.size());
         for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
@@ -326,14 +325,14 @@
         }
 
         long startTime = SystemClock.uptimeMillis();
-        processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector,
-                startTime);
+        processRequiredChecksums(filesToChecksum, result, required, onChecksumsReadyListener,
+                injector, startTime);
     }
 
     private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum,
             List<Map<Integer, ApkChecksum>> result,
             @Checksum.Type int required,
-            @NonNull IntentSender statusReceiver,
+            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener,
             @NonNull Injector injector,
             long startTime) {
         final boolean timeout =
@@ -350,7 +349,7 @@
                         // Not ready, come back later.
                         injector.getHandler().postDelayed(() -> {
                             processRequiredChecksums(filesToChecksum, result, required,
-                                    statusReceiver, injector, startTime);
+                                    onChecksumsReadyListener, injector, startTime);
                         }, PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS);
                         return;
                     }
@@ -363,13 +362,9 @@
             }
         }
 
-        final Intent intent = new Intent();
-        intent.putExtra(EXTRA_CHECKSUMS,
-                allChecksums.toArray(new ApkChecksum[allChecksums.size()]));
-
         try {
-            statusReceiver.sendIntent(injector.getContext(), 1, intent, null, null);
-        } catch (IntentSender.SendIntentException e) {
+            onChecksumsReadyListener.onChecksumsReady(allChecksums);
+        } catch (RemoteException e) {
             Slog.w(TAG, e);
         }
     }
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
new file mode 100644
index 0000000..a32e107
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.content.IntentFilter;
+
+import com.android.internal.annotations.Immutable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Representation of an immutable default cross-profile intent filter.
+ */
+@Immutable
+final class DefaultCrossProfileIntentFilter {
+
+    @IntDef({
+            Direction.TO_PARENT,
+            Direction.TO_PROFILE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface Direction {
+        int TO_PARENT = 0;
+        int TO_PROFILE = 1;
+    }
+
+    /** The intent filter that's used */
+    public final IntentFilter filter;
+
+    /**
+     * The flags related to the forwarding, e.g.
+     * {@link android.content.pm.PackageManager#SKIP_CURRENT_PROFILE} or
+     * {@link android.content.pm.PackageManager#ONLY_IF_NO_MATCH_FOUND}.
+     */
+    public final int flags;
+
+    /**
+     * The direction of forwarding, can be either {@link Direction#TO_PARENT} or
+     * {@link Direction#TO_PROFILE}.
+     */
+    public final @Direction int direction;
+
+    /**
+     * Whether this cross profile intent filter would allow personal data to be shared into
+     * the work profile. If this is {@code true}, this intent filter should be only added to
+     * the profile if the admin does not enable
+     * {@link android.os.UserManager#DISALLOW_SHARE_INTO_MANAGED_PROFILE}.
+     */
+    public final boolean letsPersonalDataIntoProfile;
+
+    private DefaultCrossProfileIntentFilter(IntentFilter filter, int flags,
+            @Direction int direction, boolean letsPersonalDataIntoProfile) {
+        this.filter = requireNonNull(filter);
+        this.flags = flags;
+        this.direction = direction;
+        this.letsPersonalDataIntoProfile = letsPersonalDataIntoProfile;
+    }
+
+    static final class Builder {
+        private IntentFilter mFilter = new IntentFilter();
+        private int mFlags;
+        private @Direction int mDirection;
+        private boolean mLetsPersonalDataIntoProfile;
+
+        Builder(@Direction int direction, int flags, boolean letsPersonalDataIntoProfile) {
+            mDirection = direction;
+            mFlags = flags;
+            mLetsPersonalDataIntoProfile = letsPersonalDataIntoProfile;
+        }
+
+        Builder addAction(String action) {
+            mFilter.addAction(action);
+            return this;
+        }
+
+        Builder addCategory(String category) {
+            mFilter.addCategory(category);
+            return this;
+        }
+
+        Builder addDataType(String type) {
+            try {
+                mFilter.addDataType(type);
+            } catch (IntentFilter.MalformedMimeTypeException e) {
+                // ignore
+            }
+            return this;
+        }
+
+        Builder addDataScheme(String scheme) {
+            mFilter.addDataScheme(scheme);
+            return this;
+        }
+
+        DefaultCrossProfileIntentFilter build() {
+            return new DefaultCrossProfileIntentFilter(mFilter, mFlags, mDirection,
+                    mLetsPersonalDataIntoProfile);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
new file mode 100644
index 0000000..3019439
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static android.content.pm.PackageManager.ONLY_IF_NO_MATCH_FOUND;
+import static android.content.pm.PackageManager.SKIP_CURRENT_PROFILE;
+import static android.speech.RecognizerIntent.ACTION_RECOGNIZE_SPEECH;
+
+import android.content.Intent;
+import android.hardware.usb.UsbManager;
+import android.provider.AlarmClock;
+import android.provider.MediaStore;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Utility Class for {@link DefaultCrossProfileIntentFilter}.
+ */
+public class DefaultCrossProfileIntentFiltersUtils {
+
+    private DefaultCrossProfileIntentFiltersUtils() {
+    }
+
+    // Intents from profile to parent user
+    /** Emergency call intent with mime type is always resolved by primary user. */
+    private static final DefaultCrossProfileIntentFilter
+            EMERGENCY_CALL_MIME =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_CALL_EMERGENCY)
+                    .addAction(Intent.ACTION_CALL_PRIVILEGED)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataType("vnd.android.cursor.item/phone")
+                    .addDataType("vnd.android.cursor.item/phone_v2")
+                    .addDataType("vnd.android.cursor.item/person")
+                    .addDataType("vnd.android.cursor.dir/calls")
+                    .addDataType("vnd.android.cursor.item/calls")
+                    .build();
+
+    /** Emergency call intent with data schemes is always resolved by primary user. */
+    private static final DefaultCrossProfileIntentFilter
+            EMERGENCY_CALL_DATA =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_CALL_EMERGENCY)
+                    .addAction(Intent.ACTION_CALL_PRIVILEGED)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataScheme("tel")
+                    .addDataScheme("sip")
+                    .addDataScheme("voicemail")
+                    .build();
+
+    /** Dial intent with mime type can be handled by either managed profile or its parent user. */
+    private static final DefaultCrossProfileIntentFilter DIAL_MIME =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    ONLY_IF_NO_MATCH_FOUND,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_DIAL)
+                    .addAction(Intent.ACTION_VIEW)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataType("vnd.android.cursor.item/phone")
+                    .addDataType("vnd.android.cursor.item/phone_v2")
+                    .addDataType("vnd.android.cursor.item/person")
+                    .addDataType("vnd.android.cursor.dir/calls")
+                    .addDataType("vnd.android.cursor.item/calls")
+                    .build();
+
+    /** Dial intent with data scheme can be handled by either managed profile or its parent user. */
+    private static final DefaultCrossProfileIntentFilter DIAL_DATA =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    ONLY_IF_NO_MATCH_FOUND,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_DIAL)
+                    .addAction(Intent.ACTION_VIEW)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataScheme("tel")
+                    .addDataScheme("sip")
+                    .addDataScheme("voicemail")
+                    .build();
+
+    /**
+     * Dial intent with no data scheme or type can be handled by either managed profile or its
+     * parent user.
+     */
+    private static final DefaultCrossProfileIntentFilter DIAL_RAW =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    ONLY_IF_NO_MATCH_FOUND,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_DIAL)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .build();
+
+    /** Pressing the call button can be handled by either managed profile or its parent user. */
+    private static final DefaultCrossProfileIntentFilter CALL_BUTTON =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    ONLY_IF_NO_MATCH_FOUND,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_CALL_BUTTON)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    /** SMS and MMS are exclusively handled by the primary user. */
+    private static final DefaultCrossProfileIntentFilter SMS_MMS =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_VIEW)
+                    .addAction(Intent.ACTION_SENDTO)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_BROWSABLE)
+                    .addDataScheme("sms")
+                    .addDataScheme("smsto")
+                    .addDataScheme("mms")
+                    .addDataScheme("mmsto")
+                    .build();
+
+    /** Mobile network settings is always shown in the primary user. */
+    private static final DefaultCrossProfileIntentFilter
+            MOBILE_NETWORK_SETTINGS =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(android.provider.Settings.ACTION_DATA_ROAMING_SETTINGS)
+                    .addAction(android.provider.Settings.ACTION_NETWORK_OPERATOR_SETTINGS)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    /** HOME intent is always resolved by the primary user. */
+    static final DefaultCrossProfileIntentFilter HOME =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    SKIP_CURRENT_PROFILE,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(Intent.ACTION_MAIN)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_HOME)
+                    .build();
+
+    /** Get content can be forwarded to parent user. */
+    private static final DefaultCrossProfileIntentFilter GET_CONTENT =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(Intent.ACTION_GET_CONTENT)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_OPENABLE)
+                    .addDataType("*/*")
+                    .build();
+
+    /** Open document intent can be forwarded to parent user. */
+    private static final DefaultCrossProfileIntentFilter OPEN_DOCUMENT =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(Intent.ACTION_OPEN_DOCUMENT)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addCategory(Intent.CATEGORY_OPENABLE)
+                    .addDataType("*/*")
+                    .build();
+
+    /** Pick for any data type can be forwarded to parent user. */
+    private static final DefaultCrossProfileIntentFilter ACTION_PICK_DATA =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(Intent.ACTION_PICK)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addDataType("*/*")
+                    .build();
+
+    /** Pick without data type can be forwarded to parent user. */
+    private static final DefaultCrossProfileIntentFilter ACTION_PICK_RAW =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(Intent.ACTION_PICK)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    /** Speech recognition can be performed by primary user. */
+    private static final DefaultCrossProfileIntentFilter RECOGNIZE_SPEECH =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(ACTION_RECOGNIZE_SPEECH)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    /** Media capture can be performed by primary user. */
+    private static final DefaultCrossProfileIntentFilter MEDIA_CAPTURE =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(MediaStore.ACTION_IMAGE_CAPTURE)
+                    .addAction(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)
+                    .addAction(MediaStore.ACTION_VIDEO_CAPTURE)
+                    .addAction(MediaStore.Audio.Media.RECORD_SOUND_ACTION)
+                    .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)
+                    .addAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
+                    .addAction(MediaStore.INTENT_ACTION_VIDEO_CAMERA)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    /** Alarm setting can be performed by primary user. */
+    private static final DefaultCrossProfileIntentFilter SET_ALARM =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(AlarmClock.ACTION_SET_ALARM)
+                    .addAction(AlarmClock.ACTION_SHOW_ALARMS)
+                    .addAction(AlarmClock.ACTION_SET_TIMER)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    // Intents from parent to profile user
+
+    /** ACTION_SEND can be forwarded to the managed profile on user's choice. */
+    private static final DefaultCrossProfileIntentFilter ACTION_SEND =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(Intent.ACTION_SEND)
+                    .addAction(Intent.ACTION_SEND_MULTIPLE)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addDataType("*/*")
+                    .build();
+
+    /** USB devices attached can get forwarded to the profile. */
+    private static final DefaultCrossProfileIntentFilter
+            USB_DEVICE_ATTACHED =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+                    /* flags= */0,
+                    /* letsPersonalDataIntoProfile= */ false)
+                    .addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED)
+                    .addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+
+    public static List<DefaultCrossProfileIntentFilter> getDefaultManagedProfileFilters() {
+        return Arrays.asList(
+                EMERGENCY_CALL_MIME,
+                EMERGENCY_CALL_DATA,
+                DIAL_MIME,
+                DIAL_DATA,
+                DIAL_RAW,
+                CALL_BUTTON,
+                SMS_MMS,
+                SET_ALARM,
+                MEDIA_CAPTURE,
+                RECOGNIZE_SPEECH,
+                ACTION_PICK_RAW,
+                ACTION_PICK_DATA,
+                OPEN_DOCUMENT,
+                GET_CONTENT,
+                USB_DEVICE_ATTACHED,
+                ACTION_SEND,
+                HOME,
+                MOBILE_NETWORK_SETTINGS);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f444ed6..498c314 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -177,6 +177,7 @@
 import android.content.pm.FallbackCategoryProvider;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IDexModuleRegisterCallback;
+import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.IPackageChangeObserver;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver;
@@ -5523,19 +5524,19 @@
     public void requestChecksums(@NonNull String packageName, boolean includeSplits,
             @Checksum.Type int optional,
             @Checksum.Type int required, @Nullable List trustedInstallers,
-            @NonNull IntentSender statusReceiver, int userId) {
+            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId) {
         requestChecksumsInternal(packageName, includeSplits, optional, required, trustedInstallers,
-                statusReceiver, userId, mInjector.getBackgroundExecutor(),
+                onChecksumsReadyListener, userId, mInjector.getBackgroundExecutor(),
                 mInjector.getBackgroundHandler());
     }
 
     private void requestChecksumsInternal(@NonNull String packageName, boolean includeSplits,
-            @Checksum.Type int optional,
-            @Checksum.Type int required, @Nullable List trustedInstallers,
-            @NonNull IntentSender statusReceiver, int userId, @NonNull Executor executor,
-            @NonNull Handler handler) {
+            @Checksum.Type int optional, @Checksum.Type int required,
+            @Nullable List trustedInstallers,
+            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
+            @NonNull Executor executor, @NonNull Handler handler) {
         Objects.requireNonNull(packageName);
-        Objects.requireNonNull(statusReceiver);
+        Objects.requireNonNull(onChecksumsReadyListener);
         Objects.requireNonNull(executor);
         Objects.requireNonNull(handler);
 
@@ -5571,7 +5572,7 @@
                     () -> mInjector.getIncrementalManager(),
                     () -> mPmInternal);
             ApkChecksums.getChecksums(filesToChecksum, optional, required, installerPackageName,
-                    trustedCerts, statusReceiver, injector);
+                    trustedCerts, onChecksumsReadyListener, injector);
         });
     }
 
@@ -27110,12 +27111,14 @@
             ps.setStatesOnCrashOrAnr();
         }
 
+        @Override
         public void requestChecksums(@NonNull String packageName, boolean includeSplits,
                 @Checksum.Type int optional, @Checksum.Type int required,
-                @Nullable List trustedInstallers, @NonNull IntentSender statusReceiver, int userId,
+                @Nullable List trustedInstallers,
+                @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
                 @NonNull Executor executor, @NonNull Handler handler) {
             requestChecksumsInternal(packageName, includeSplits, optional, required,
-                    trustedInstallers, statusReceiver, userId, executor, handler);
+                    trustedInstallers, onChecksumsReadyListener, userId, executor, handler);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
index ce77c91..8c5084a 100644
--- a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
+++ b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
@@ -16,22 +16,16 @@
 
 package com.android.server.pm;
 
-import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
-
 import android.app.admin.SecurityLog;
 import android.content.Context;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentSender;
 import android.content.pm.ApkChecksum;
 import android.content.pm.Checksum;
+import android.content.pm.IOnChecksumsReadyListener;
 import android.content.pm.PackageManagerInternal;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerExecutor;
-import android.os.IBinder;
-import android.os.Parcelable;
+import android.os.RemoteException;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Slog;
@@ -39,7 +33,6 @@
 import com.android.internal.os.BackgroundThread;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -109,26 +102,23 @@
         // Capturing local loggingInfo to still log even if hash was invalidated.
         try {
             pmi.requestChecksums(packageName, false, 0, CHECKSUM_TYPE, null,
-                    new IntentSender((IIntentSender) new IIntentSender.Stub() {
+                    new IOnChecksumsReadyListener.Stub() {
                         @Override
-                        public void send(int code, Intent intent, String resolvedType,
-                                IBinder allowlistToken, IIntentReceiver finishedReceiver,
-                                String requiredPermission, Bundle options) {
-                            processChecksums(loggingInfo, intent);
+                        public void onChecksumsReady(List<ApkChecksum> checksums)
+                                throws RemoteException {
+                            processChecksums(loggingInfo, checksums);
                         }
-                    }), context.getUserId(), mExecutor, this);
+                    }, context.getUserId(),
+                    mExecutor, this);
         } catch (Throwable t) {
             Slog.e(TAG, "requestChecksums() failed", t);
             enqueueProcessChecksum(loggingInfo, null);
         }
     }
 
-    void processChecksums(final LoggingInfo loggingInfo, Intent intent) {
-        Parcelable[] parcelables = intent.getParcelableArrayExtra(EXTRA_CHECKSUMS);
-        ApkChecksum[] checksums = Arrays.copyOf(parcelables, parcelables.length,
-                ApkChecksum[].class);
-
-        for (ApkChecksum checksum : checksums) {
+    void processChecksums(final LoggingInfo loggingInfo, List<ApkChecksum> checksums) {
+        for (int i = 0, size = checksums.size(); i < size; ++i) {
+            ApkChecksum checksum = checksums.get(i);
             if (checksum.getType() == CHECKSUM_TYPE) {
                 processChecksum(loggingInfo, checksum.getValue());
                 return;
diff --git a/services/core/java/com/android/server/pm/RestrictionsSet.java b/services/core/java/com/android/server/pm/RestrictionsSet.java
index ea61ca4..e5a70c3 100644
--- a/services/core/java/com/android/server/pm/RestrictionsSet.java
+++ b/services/core/java/com/android/server/pm/RestrictionsSet.java
@@ -26,6 +26,7 @@
 import android.util.TypedXmlSerializer;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.BundleUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -78,7 +79,7 @@
         if (!changed) {
             return false;
         }
-        if (!UserRestrictionsUtils.isEmpty(restrictions)) {
+        if (!BundleUtils.isEmpty(restrictions)) {
             mUserRestrictions.put(userId, restrictions);
         } else {
             mUserRestrictions.delete(userId);
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index c182d68..c17d2e4 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -298,4 +298,11 @@
      * Gets all {@link UserInfo UserInfos}.
      */
     public abstract @NonNull UserInfo[] getUserInfos();
+
+    /**
+     * Sets all default cross profile intent filters between {@code parentUserId} and
+     * {@code profileUserId}.
+     */
+    public abstract void setDefaultCrossProfileIntentFilters(
+            @UserIdInt int parentUserId, @UserIdInt int profileUserId);
 }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a807777..753f22d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -80,6 +80,7 @@
 import android.os.UserManager.EnforcingUser;
 import android.os.UserManager.QuietModeFlag;
 import android.os.storage.StorageManager;
+import android.provider.Settings;
 import android.security.GateKeeper;
 import android.service.gatekeeper.IGateKeeperService;
 import android.stats.devicepolicy.DevicePolicyEnums;
@@ -108,6 +109,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.server.BundleUtils;
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
 import com.android.server.SystemService;
@@ -1960,11 +1962,11 @@
         final Bundle global = mDevicePolicyGlobalUserRestrictions.mergeAll();
         final RestrictionsSet local = getDevicePolicyLocalRestrictionsForTargetUserLR(userId);
 
-        if (UserRestrictionsUtils.isEmpty(global) && local.isEmpty()) {
+        if (BundleUtils.isEmpty(global) && local.isEmpty()) {
             // Common case first.
             return baseRestrictions;
         }
-        final Bundle effective = UserRestrictionsUtils.clone(baseRestrictions);
+        final Bundle effective = BundleUtils.clone(baseRestrictions);
         UserRestrictionsUtils.merge(effective, global);
         UserRestrictionsUtils.merge(effective, local.mergeAll());
 
@@ -2105,7 +2107,7 @@
     @Override
     public Bundle getUserRestrictions(@UserIdInt int userId) {
         checkManageOrInteractPermissionIfCallerInOtherProfileGroup(userId, "getUserRestrictions");
-        return UserRestrictionsUtils.clone(getEffectiveUserRestrictions(userId));
+        return BundleUtils.clone(getEffectiveUserRestrictions(userId));
     }
 
     @Override
@@ -2129,7 +2131,7 @@
         synchronized (mRestrictionsLock) {
             // Note we can't modify Bundles stored in mBaseUserRestrictions directly, so create
             // a copy.
-            final Bundle newRestrictions = UserRestrictionsUtils.clone(
+            final Bundle newRestrictions = BundleUtils.clone(
                     mBaseUserRestrictions.getRestrictions(userId));
             newRestrictions.putBoolean(key, value);
 
@@ -2727,7 +2729,7 @@
         if (userVersion < 7) {
             // Previously only one user could enforce global restrictions, now it is per-user.
             synchronized (mRestrictionsLock) {
-                if (!UserRestrictionsUtils.isEmpty(oldGlobalUserRestrictions)
+                if (!BundleUtils.isEmpty(oldGlobalUserRestrictions)
                         && mDeviceOwnerUserId != UserHandle.USER_NULL) {
                     mDevicePolicyGlobalUserRestrictions.updateRestrictions(
                             mDeviceOwnerUserId, oldGlobalUserRestrictions);
@@ -3562,6 +3564,9 @@
             t.traceBegin("PM.onNewUserCreated-" + userId);
             mPm.onNewUserCreated(userId, /* convertedFromPreCreated= */ false);
             t.traceEnd();
+            applyDefaultUserSettings(userTypeDetails, userId);
+            setDefaultCrossProfileIntentFilters(userId, userTypeDetails, restrictions, parentId);
+
             if (preCreate) {
                 // Must start user (which will be stopped right away, through
                 // UserController.finishUserUnlockedCompleted) so services can properly
@@ -3597,6 +3602,78 @@
         return userInfo;
     }
 
+    private void applyDefaultUserSettings(UserTypeDetails userTypeDetails, @UserIdInt int userId) {
+        final Bundle systemSettings = userTypeDetails.getDefaultSystemSettings();
+        final Bundle secureSettings = userTypeDetails.getDefaultSecureSettings();
+        if (systemSettings.isEmpty() && secureSettings.isEmpty()) {
+            return;
+        }
+
+        final int systemSettingsSize = systemSettings.size();
+        final String[] systemSettingsArray = systemSettings.keySet().toArray(
+                new String[systemSettingsSize]);
+        for (int i = 0; i < systemSettingsSize; i++) {
+            final String setting = systemSettingsArray[i];
+            if (!Settings.System.putStringForUser(
+                    mContext.getContentResolver(), setting, systemSettings.getString(setting),
+                    userId)) {
+                Slog.e(LOG_TAG, "Failed to insert default system setting: " + setting);
+            }
+        }
+
+        final int secureSettingsSize = secureSettings.size();
+        final String[] secureSettingsArray = secureSettings.keySet().toArray(
+                new String[secureSettingsSize]);
+        for (int i = 0; i < secureSettingsSize; i++) {
+            final String setting = secureSettingsArray[i];
+            if (!Settings.Secure.putStringForUser(
+                    mContext.getContentResolver(), setting, secureSettings.getString(setting),
+                    userId)) {
+                Slog.e(LOG_TAG, "Failed to insert default secure setting: " + setting);
+            }
+        }
+    }
+
+    /**
+     * Sets all default cross profile intent filters between {@code parentUserId} and
+     * {@code profileUserId}, does nothing if {@code userType} is not a profile.
+     */
+    private void setDefaultCrossProfileIntentFilters(
+            @UserIdInt int profileUserId, UserTypeDetails profileDetails,
+            Bundle profileRestrictions, @UserIdInt int parentUserId) {
+        if (profileDetails == null || !profileDetails.isProfile()) {
+            return;
+        }
+        final List<DefaultCrossProfileIntentFilter> filters =
+                profileDetails.getDefaultCrossProfileIntentFilters();
+        if (filters.isEmpty()) {
+            return;
+        }
+
+        // Skip filters that allow data to be shared into the profile, if admin has disabled it.
+        final boolean disallowSharingIntoProfile =
+                profileRestrictions.getBoolean(
+                        UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE,
+                        /* defaultValue = */ false);
+        final int size = profileDetails.getDefaultCrossProfileIntentFilters().size();
+        for (int i = 0; i < size; i++) {
+            final DefaultCrossProfileIntentFilter filter =
+                    profileDetails.getDefaultCrossProfileIntentFilters().get(i);
+            if (disallowSharingIntoProfile && filter.letsPersonalDataIntoProfile) {
+                continue;
+            }
+            if (filter.direction == DefaultCrossProfileIntentFilter.Direction.TO_PARENT) {
+                mPm.addCrossProfileIntentFilter(
+                        filter.filter, mContext.getOpPackageName(), profileUserId, parentUserId,
+                        filter.flags);
+            } else {
+                mPm.addCrossProfileIntentFilter(
+                        filter.filter, mContext.getOpPackageName(), parentUserId, profileUserId,
+                        filter.flags);
+            }
+        }
+    }
+
     /**
      * Finds and converts a previously pre-created user into a regular user, if possible.
      *
@@ -5462,6 +5539,15 @@
                 return allInfos;
             }
         }
+
+        @Override
+        public void setDefaultCrossProfileIntentFilters(
+                @UserIdInt int parentUserId, @UserIdInt int profileUserId) {
+            final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(profileUserId);
+            final Bundle restrictions = getEffectiveUserRestrictions(profileUserId);
+            UserManagerService.this.setDefaultCrossProfileIntentFilters(
+                    profileUserId, userTypeDetails, restrictions, parentUserId);
+        }
     }
 
     /**
@@ -5726,8 +5812,8 @@
 
         // merge existing base restrictions with the new type's default restrictions
         synchronized (mRestrictionsLock) {
-            if (!UserRestrictionsUtils.isEmpty(newUserType.getDefaultRestrictions())) {
-                final Bundle newRestrictions = UserRestrictionsUtils.clone(
+            if (!BundleUtils.isEmpty(newUserType.getDefaultRestrictions())) {
+                final Bundle newRestrictions = BundleUtils.clone(
                         mBaseUserRestrictions.getRestrictions(userInfo.id));
                 UserRestrictionsUtils.merge(newRestrictions,
                         newUserType.getDefaultRestrictions());
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 51dff40..ff90043 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -44,6 +44,7 @@
 
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
+import com.android.server.BundleUtils;
 import com.android.server.LocalServices;
 
 import com.google.android.collect.Sets;
@@ -384,10 +385,6 @@
         return in != null ? in : new Bundle();
     }
 
-    public static boolean isEmpty(@Nullable Bundle in) {
-        return (in == null) || (in.size() == 0);
-    }
-
     /**
      * Returns {@code true} if given bundle is not null and contains {@code true} for a given
      * restriction.
@@ -396,17 +393,6 @@
         return in != null && in.getBoolean(restriction);
     }
 
-    /**
-     * Creates a copy of the {@code in} Bundle.  If {@code in} is null, it'll return an empty
-     * bundle.
-     *
-     * <p>The resulting {@link Bundle} is always writable. (i.e. it won't return
-     * {@link Bundle#EMPTY})
-     */
-    public static @NonNull Bundle clone(@Nullable Bundle in) {
-        return (in != null) ? new Bundle(in) : new Bundle();
-    }
-
     public static void merge(@NonNull Bundle dest, @Nullable Bundle in) {
         Objects.requireNonNull(dest);
         Preconditions.checkArgument(dest != in);
@@ -483,10 +469,10 @@
         if (a == b) {
             return true;
         }
-        if (isEmpty(a)) {
-            return isEmpty(b);
+        if (BundleUtils.isEmpty(a)) {
+            return BundleUtils.isEmpty(b);
         }
-        if (isEmpty(b)) {
+        if (BundleUtils.isEmpty(b)) {
             return false;
         }
         for (String key : a.keySet()) {
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index 5fa46b9..17ce386 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -28,8 +28,12 @@
 import android.os.UserManager;
 
 import com.android.internal.util.Preconditions;
+import com.android.server.BundleUtils;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Contains the details about a multiuser "user type", such as a
@@ -82,6 +86,24 @@
      */
     private final @Nullable Bundle mDefaultRestrictions;
 
+    /**
+     * List of {@link android.provider.Settings.System} to apply by default to newly created users
+     * of this type.
+     */
+    private final @Nullable Bundle mDefaultSystemSettings;
+
+    /**
+     * List of {@link android.provider.Settings.Secure} to apply by default to newly created users
+     * of this type.
+     */
+    private final @Nullable Bundle mDefaultSecureSettings;
+
+    /**
+     * List of {@link DefaultCrossProfileIntentFilter} to allow by default for newly created
+     * profiles.
+     */
+    private final @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters;
+
 
     // Fields for profiles only, controlling the nature of their badges.
     // All badge information should be set if {@link #hasBadge()} is true.
@@ -133,7 +155,10 @@
             int iconBadge, int badgePlain, int badgeNoBackground,
             @Nullable int[] badgeLabels, @Nullable int[] badgeColors,
             @Nullable int[] darkThemeBadgeColors,
-            @Nullable Bundle defaultRestrictions) {
+            @Nullable Bundle defaultRestrictions,
+            @Nullable Bundle defaultSystemSettings,
+            @Nullable Bundle defaultSecureSettings,
+            @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters) {
         this.mName = name;
         this.mEnabled = enabled;
         this.mMaxAllowed = maxAllowed;
@@ -141,6 +166,9 @@
         this.mBaseType = baseType;
         this.mDefaultUserInfoPropertyFlags = defaultUserInfoPropertyFlags;
         this.mDefaultRestrictions = defaultRestrictions;
+        this.mDefaultSystemSettings = defaultSystemSettings;
+        this.mDefaultSecureSettings = defaultSecureSettings;
+        this.mDefaultCrossProfileIntentFilters = defaultCrossProfileIntentFilters;
 
         this.mIconBadge = iconBadge;
         this.mBadgePlain = badgePlain;
@@ -263,9 +291,9 @@
         return (mBaseType & UserInfo.FLAG_SYSTEM) != 0;
     }
 
-    /** Returns a Bundle representing the default user restrictions. */
+    /** Returns a {@link Bundle} representing the default user restrictions. */
     @NonNull Bundle getDefaultRestrictions() {
-        return UserRestrictionsUtils.clone(mDefaultRestrictions);
+        return BundleUtils.clone(mDefaultRestrictions);
     }
 
     /** Adds the default user restrictions to the given bundle of restrictions. */
@@ -273,6 +301,24 @@
         UserRestrictionsUtils.merge(currentRestrictions, mDefaultRestrictions);
     }
 
+    /** Returns a {@link Bundle} representing the default system settings. */
+    @NonNull Bundle getDefaultSystemSettings() {
+        return BundleUtils.clone(mDefaultSystemSettings);
+    }
+
+    /** Returns a {@link Bundle} representing the default secure settings. */
+    @NonNull Bundle getDefaultSecureSettings() {
+        return BundleUtils.clone(mDefaultSecureSettings);
+    }
+
+    /** Returns a list of default cross profile intent filters. */
+    @NonNull List<DefaultCrossProfileIntentFilter> getDefaultCrossProfileIntentFilters() {
+        return mDefaultCrossProfileIntentFilters != null
+                ? new ArrayList<>(mDefaultCrossProfileIntentFilters)
+                : Collections.emptyList();
+    }
+
+
     /** Dumps details of the UserTypeDetails. Do not parse this. */
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mName: "); pw.println(mName);
@@ -325,6 +371,10 @@
         private int mMaxAllowedPerParent = UNLIMITED_NUMBER_OF_USERS;
         private int mDefaultUserInfoPropertyFlags = 0;
         private @Nullable Bundle mDefaultRestrictions = null;
+        private @Nullable Bundle mDefaultSystemSettings = null;
+        private @Nullable Bundle mDefaultSecureSettings = null;
+        private @Nullable List<DefaultCrossProfileIntentFilter> mDefaultCrossProfileIntentFilters =
+                null;
         private boolean mEnabled = true;
         private int mLabel = Resources.ID_NULL;
         private @Nullable int[] mBadgeLabels = null;
@@ -407,6 +457,22 @@
             return this;
         }
 
+        public Builder setDefaultSystemSettings(@Nullable Bundle settings) {
+            mDefaultSystemSettings = settings;
+            return this;
+        }
+
+        public Builder setDefaultSecureSettings(@Nullable Bundle settings) {
+            mDefaultSecureSettings = settings;
+            return this;
+        }
+
+        public Builder setDefaultCrossProfileIntentFilters(
+                @Nullable List<DefaultCrossProfileIntentFilter> intentFilters) {
+            mDefaultCrossProfileIntentFilters = intentFilters;
+            return this;
+        }
+
         @UserInfoFlag int getBaseType() {
             return mBaseType;
         }
@@ -425,17 +491,28 @@
                 Preconditions.checkArgument(mBadgeColors != null && mBadgeColors.length != 0,
                         "UserTypeDetails " + mName + " has badge but no badgeColors.");
             }
+            if (!isProfile()) {
+                Preconditions.checkArgument(mDefaultCrossProfileIntentFilters == null
+                                || mDefaultCrossProfileIntentFilters.isEmpty(),
+                        "UserTypeDetails %s has a non empty "
+                                + "defaultCrossProfileIntentFilters", mName);
+            }
             return new UserTypeDetails(mName, mEnabled, mMaxAllowed, mBaseType,
                     mDefaultUserInfoPropertyFlags, mLabel, mMaxAllowedPerParent,
                     mIconBadge, mBadgePlain, mBadgeNoBackground, mBadgeLabels, mBadgeColors,
                     mDarkThemeBadgeColors == null ? mBadgeColors : mDarkThemeBadgeColors,
-                    mDefaultRestrictions);
+                    mDefaultRestrictions, mDefaultSystemSettings, mDefaultSecureSettings,
+                    mDefaultCrossProfileIntentFilters);
         }
 
         private boolean hasBadge() {
             return mIconBadge != Resources.ID_NULL;
         }
 
+        private boolean isProfile() {
+            return (mBaseType & UserInfo.FLAG_PROFILE) != 0;
+        }
+
         // TODO(b/143784345): Refactor this when we clean up UserInfo.
         private boolean hasValidBaseType() {
             return mBaseType == UserInfo.FLAG_FULL
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index 1ffbf60..6aac0b2 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -133,7 +133,9 @@
                         com.android.internal.R.color.profile_badge_1_dark,
                         com.android.internal.R.color.profile_badge_2_dark,
                         com.android.internal.R.color.profile_badge_3_dark)
-                .setDefaultRestrictions(null);
+                .setDefaultRestrictions(getDefaultManagedProfileRestrictions())
+                .setDefaultSecureSettings(getDefaultManagedProfileSecureSettings())
+                .setDefaultCrossProfileIntentFilters(getDefaultManagedCrossProfileIntentFilter());
     }
 
     /**
@@ -256,12 +258,35 @@
         return restrictions;
     }
 
+    private static Bundle getDefaultManagedProfileRestrictions() {
+        final Bundle restrictions = new Bundle();
+        restrictions.putBoolean(UserManager.DISALLOW_WALLPAPER, true);
+        return restrictions;
+    }
+
+    private static Bundle getDefaultManagedProfileSecureSettings() {
+        // Only add String values to the bundle, settings are written as Strings eventually
+        final Bundle settings = new Bundle();
+        settings.putString(
+                android.provider.Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH, "1");
+        settings.putString(
+                android.provider.Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, "1");
+        return settings;
+    }
+
+    private static List<DefaultCrossProfileIntentFilter>
+            getDefaultManagedCrossProfileIntentFilter() {
+        return DefaultCrossProfileIntentFiltersUtils.getDefaultManagedProfileFilters();
+    }
+
     /**
      * Reads the given xml parser to obtain device user-type customization, and updates the given
      * map of {@link UserTypeDetails.Builder}s accordingly.
      * <p>
      * The xml file can specify the attributes according to the set... methods below.
      */
+    // TODO(b/176973369): Add parsing logic to support custom settings/filters
+    //  in config_user_types.xml
     @VisibleForTesting
     static void customizeBuilders(ArrayMap<String, UserTypeDetails.Builder> builders,
             XmlResourceParser parser) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a407e8e..bc81961 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -222,6 +222,7 @@
 import com.android.server.wm.DisplayRotation;
 import com.android.server.wm.WindowManagerInternal;
 import com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+import com.android.server.wm.WindowManagerService;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -1913,6 +1914,7 @@
                 handleStartTransitionForKeyguardLw(keyguardGoingAway, 0 /* duration */);
             }
         });
+
         mKeyguardDelegate = new KeyguardServiceDelegate(mContext,
                 new StateCallback() {
                     @Override
@@ -3136,7 +3138,7 @@
     private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway, long duration) {
         final int res = applyKeyguardOcclusionChange();
         if (res != 0) return res;
-        if (keyguardGoingAway) {
+        if (!WindowManagerService.sEnableRemoteKeyguardAnimation && keyguardGoingAway) {
             if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
             startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration);
         }
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index e9d6440..c77e266 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1158,7 +1158,7 @@
      * @param startTime the start time of the animation in uptime milliseconds
      * @param fadeoutDuration the duration of the exit animation, in milliseconds
      */
-    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
+    void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
 
     /**
      * Called when System UI has been started.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index c2a1c79..a95628f 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -29,6 +29,7 @@
 import com.android.internal.policy.IKeyguardService;
 import com.android.server.UiThread;
 import com.android.server.policy.WindowManagerPolicy.OnKeyguardExitResult;
+import com.android.server.wm.WindowManagerService;
 
 import java.io.PrintWriter;
 
@@ -398,7 +399,7 @@
     }
 
     public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
-        if (mKeyguardService != null) {
+        if (!WindowManagerService.sEnableRemoteKeyguardAnimation && mKeyguardService != null) {
             mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration);
         }
     }
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index a82f239..5ec527a 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -29,6 +29,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 
 import java.util.Collections;
@@ -37,6 +38,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Represents an single instance of a VCN.
@@ -82,10 +84,19 @@
     /** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
     private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
 
+    /**
+     * Causes this VCN to immediately enter Safemode.
+     *
+     * <p>Upon entering Safemode, the VCN will unregister its RequestListener, tear down all of its
+     * VcnGatewayConnections, and notify VcnManagementService that it is in Safemode.
+     */
+    private static final int MSG_CMD_ENTER_SAFEMODE = MSG_CMD_BASE + 1;
+
     @NonNull private final VcnContext mVcnContext;
     @NonNull private final ParcelUuid mSubscriptionGroup;
     @NonNull private final Dependencies mDeps;
     @NonNull private final VcnNetworkRequestListener mRequestListener;
+    @NonNull private final VcnSafemodeCallback mVcnSafemodeCallback;
 
     @NonNull
     private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections =
@@ -94,14 +105,33 @@
     @NonNull private VcnConfig mConfig;
     @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
 
-    private boolean mIsRunning = true;
+    /**
+     * Whether this Vcn instance is active and running.
+     *
+     * <p>The value will be {@code true} while running. It will be {@code false} if the VCN has been
+     * shut down or has entered safe mode.
+     *
+     * <p>This AtomicBoolean is required in order to ensure consistency and correctness across
+     * multiple threads. Unlike the rest of the Vcn, this is queried synchronously on Binder threads
+     * from VcnManagementService, and therefore cannot rely on guarantees of running on the VCN
+     * Looper.
+     */
+    // TODO(b/179429339): update when exiting safemode (when a new VcnConfig is provided)
+    private final AtomicBoolean mIsActive = new AtomicBoolean(true);
 
     public Vcn(
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
             @NonNull VcnConfig config,
-            @NonNull TelephonySubscriptionSnapshot snapshot) {
-        this(vcnContext, subscriptionGroup, config, snapshot, new Dependencies());
+            @NonNull TelephonySubscriptionSnapshot snapshot,
+            @NonNull VcnSafemodeCallback vcnSafemodeCallback) {
+        this(
+                vcnContext,
+                subscriptionGroup,
+                config,
+                snapshot,
+                vcnSafemodeCallback,
+                new Dependencies());
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -110,10 +140,13 @@
             @NonNull ParcelUuid subscriptionGroup,
             @NonNull VcnConfig config,
             @NonNull TelephonySubscriptionSnapshot snapshot,
+            @NonNull VcnSafemodeCallback vcnSafemodeCallback,
             @NonNull Dependencies deps) {
         super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
         mVcnContext = vcnContext;
         mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
+        mVcnSafemodeCallback =
+                Objects.requireNonNull(vcnSafemodeCallback, "Missing vcnSafemodeCallback");
         mDeps = Objects.requireNonNull(deps, "Missing deps");
         mRequestListener = new VcnNetworkRequestListener();
 
@@ -143,6 +176,11 @@
         sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN));
     }
 
+    /** Synchronously checks whether this Vcn is active. */
+    public boolean isActive() {
+        return mIsActive.get();
+    }
+
     /** Get current Gateways for testing purposes */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public Set<VcnGatewayConnection> getVcnGatewayConnections() {
@@ -160,7 +198,7 @@
 
     @Override
     public void handleMessage(@NonNull Message msg) {
-        if (!mIsRunning) {
+        if (!isActive()) {
             return;
         }
 
@@ -177,6 +215,9 @@
             case MSG_CMD_TEARDOWN:
                 handleTeardown();
                 break;
+            case MSG_CMD_ENTER_SAFEMODE:
+                handleEnterSafemode();
+                break;
             default:
                 Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what);
         }
@@ -198,7 +239,13 @@
             gatewayConnection.teardownAsynchronously();
         }
 
-        mIsRunning = false;
+        mIsActive.set(false);
+    }
+
+    private void handleEnterSafemode() {
+        handleTeardown();
+
+        mVcnSafemodeCallback.onEnteredSafemode();
     }
 
     private void handleNetworkRequested(
@@ -233,7 +280,8 @@
                                 mVcnContext,
                                 mSubscriptionGroup,
                                 mLastSnapshot,
-                                gatewayConnectionConfig);
+                                gatewayConnectionConfig,
+                                new VcnGatewayStatusCallbackImpl());
                 mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
             }
         }
@@ -242,7 +290,7 @@
     private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
         mLastSnapshot = snapshot;
 
-        if (mIsRunning) {
+        if (isActive()) {
             for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
                 gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
             }
@@ -271,6 +319,20 @@
         return 52;
     }
 
+    /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */
+    @VisibleForTesting(visibility = Visibility.PACKAGE)
+    public interface VcnGatewayStatusCallback {
+        /** Called by a VcnGatewayConnection to indicate that it has entered Safemode. */
+        void onEnteredSafemode();
+    }
+
+    private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback {
+        @Override
+        public void onEnteredSafemode() {
+            sendMessage(obtainMessage(MSG_CMD_ENTER_SAFEMODE));
+        }
+    }
+
     /** External dependencies used by Vcn, for injection in tests */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     public static class Dependencies {
@@ -279,9 +341,14 @@
                 VcnContext vcnContext,
                 ParcelUuid subscriptionGroup,
                 TelephonySubscriptionSnapshot snapshot,
-                VcnGatewayConnectionConfig connectionConfig) {
+                VcnGatewayConnectionConfig connectionConfig,
+                VcnGatewayStatusCallback gatewayStatusCallback) {
             return new VcnGatewayConnection(
-                    vcnContext, subscriptionGroup, snapshot, connectionConfig);
+                    vcnContext,
+                    subscriptionGroup,
+                    snapshot,
+                    connectionConfig,
+                    gatewayStatusCallback);
         }
     }
 }
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 853bb43..9ecdf1b 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -61,7 +61,6 @@
 import android.util.ArraySet;
 import android.util.Slog;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
 import com.android.internal.util.State;
@@ -69,6 +68,7 @@
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
 import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
 
 import java.io.IOException;
 import java.net.Inet4Address;
@@ -403,15 +403,13 @@
     @NonNull
     final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
 
-    @NonNull private final Object mLock = new Object();
-
-    @GuardedBy("mLock")
     @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
 
     @NonNull private final VcnContext mVcnContext;
     @NonNull private final ParcelUuid mSubscriptionGroup;
     @NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
     @NonNull private final VcnGatewayConnectionConfig mConnectionConfig;
+    @NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback;
     @NonNull private final Dependencies mDeps;
 
     @NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback;
@@ -487,8 +485,15 @@
             @NonNull VcnContext vcnContext,
             @NonNull ParcelUuid subscriptionGroup,
             @NonNull TelephonySubscriptionSnapshot snapshot,
-            @NonNull VcnGatewayConnectionConfig connectionConfig) {
-        this(vcnContext, subscriptionGroup, snapshot, connectionConfig, new Dependencies());
+            @NonNull VcnGatewayConnectionConfig connectionConfig,
+            @NonNull VcnGatewayStatusCallback gatewayStatusCallback) {
+        this(
+                vcnContext,
+                subscriptionGroup,
+                snapshot,
+                connectionConfig,
+                gatewayStatusCallback,
+                new Dependencies());
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
@@ -497,16 +502,17 @@
             @NonNull ParcelUuid subscriptionGroup,
             @NonNull TelephonySubscriptionSnapshot snapshot,
             @NonNull VcnGatewayConnectionConfig connectionConfig,
+            @NonNull VcnGatewayStatusCallback gatewayStatusCallback,
             @NonNull Dependencies deps) {
         super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
         mVcnContext = vcnContext;
         mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
         mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
+        mGatewayStatusCallback =
+                Objects.requireNonNull(gatewayStatusCallback, "Missing gatewayStatusCallback");
         mDeps = Objects.requireNonNull(deps, "Missing deps");
 
-        synchronized (mLock) {
-            mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
-        }
+        mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
 
         mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback();
 
@@ -577,13 +583,10 @@
      */
     public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
         Objects.requireNonNull(snapshot, "Missing snapshot");
+        mVcnContext.ensureRunningOnLooperThread();
 
-        // Vcn is the only user of this method and runs on the same Thread, but lock around
-        // mLastSnapshot to be technically correct.
-        synchronized (mLock) {
-            mLastSnapshot = snapshot;
-            mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
-        }
+        mLastSnapshot = snapshot;
+        mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
 
         sendMessage(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL);
     }
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index ce951b8..771b712 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -975,7 +975,8 @@
             final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
             if (r != null && r.isState(Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) {
                 r.mDisplayContent.mAppTransition.overridePendingAppTransition(
-                        packageName, enterAnim, exitAnim, null, null);
+                        packageName, enterAnim, exitAnim, null, null,
+                        r.mOverrideTaskTransition);
             }
         }
         Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 36c5037..262505f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
 import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
@@ -82,7 +81,6 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
 import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.res.Configuration.EMPTY;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -203,7 +201,6 @@
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.ACTIVITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -273,7 +270,6 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
-import android.permission.PermissionManager;
 import android.service.dreams.DreamActivity;
 import android.service.dreams.DreamManagerInternal;
 import android.service.voice.IVoiceInteractionSession;
@@ -682,6 +678,7 @@
     boolean mRequestForceTransition;
 
     boolean mEnteringAnimation;
+    boolean mOverrideTaskTransition;
 
     boolean mAppStopped;
     // A hint to override the window specified rotation animation, or -1 to use the window specified
@@ -1627,6 +1624,8 @@
             if (rotationAnimation >= 0) {
                 mRotationAnimationHint = rotationAnimation;
             }
+
+            mOverrideTaskTransition = options.getOverrideTaskTransition();
         }
 
         ColorDisplayService.ColorDisplayServiceInternal cds = LocalServices.getService(
@@ -3997,7 +3996,8 @@
                         pendingOptions.getCustomEnterResId(),
                         pendingOptions.getCustomExitResId(),
                         pendingOptions.getAnimationStartedListener(),
-                        pendingOptions.getAnimationFinishedListener());
+                        pendingOptions.getAnimationFinishedListener(),
+                        pendingOptions.getOverrideTaskTransition());
                 break;
             case ANIM_CLIP_REVEAL:
                 displayContent.mAppTransition.overridePendingAppTransitionClipReveal(
@@ -6775,20 +6775,6 @@
         // layout traversals.
         mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
         getResolvedOverrideConfiguration().seq = mConfigurationSeq;
-
-        // Sandbox max bounds by setting it to the app bounds, if activity is letterboxed or in
-        // size compat mode.
-        if (providesMaxBounds()) {
-            if (DEBUG_CONFIGURATION) {
-                ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds for uid %s to bounds %s "
-                        + "due to letterboxing? %s mismatch with parent bounds? %s size compat "
-                        + "mode %s", getUid(),
-                        resolvedConfig.windowConfiguration.getBounds(), mLetterbox != null,
-                        !matchParentBounds(), inSizeCompatMode());
-            }
-            resolvedConfig.windowConfiguration
-                    .setMaxBounds(resolvedConfig.windowConfiguration.getBounds());
-        }
     }
 
     /**
@@ -6972,19 +6958,6 @@
         return super.getBounds();
     }
 
-    @Override
-    public boolean providesMaxBounds() {
-        // System and SystemUI should always be able to access the physical display bounds,
-        // so do not provide it with the overridden maximum bounds.
-        // TODO(b/179179513) check WindowState#mOwnerCanAddInternalSystemWindow instead
-        if (getUid() == SYSTEM_UID || PermissionManager.checkPermission(INTERNAL_SYSTEM_WINDOW,
-                getPid(), info.applicationInfo.uid) == PERMISSION_GRANTED) {
-            return false;
-        }
-        // Max bounds should be sandboxed when this is letterboxed or in size compat mode.
-        return mLetterbox != null || !matchParentBounds() || inSizeCompatMode();
-    }
-
     @VisibleForTesting
     @Override
     Rect getAnimationBounds(int appRootTaskClipMode) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index d846c3a..79f8229 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1814,8 +1814,7 @@
     }
 
     private Task computeTargetTask() {
-        if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
-                && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
+        if (mInTask == null && !mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
             // A new task should be created instead of using existing one.
             return null;
         } else if (mSourceRecord != null) {
@@ -2505,7 +2504,9 @@
         // If bring to front is requested, and no result is requested and we have not been given
         // an explicit task to launch in to, and we can find a task that was started with this
         // same component, then instead of launching bring that one to the front.
-        putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
+        putIntoExistingTask &= !isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)
+                ? (mInTask == null && mStartActivity.resultTo == null)
+                : (mInTask == null);
         ActivityRecord intentActivity = null;
         if (putIntoExistingTask) {
             if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 90070c8..5b685b4 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -90,6 +90,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
+import static com.android.server.wm.WindowManagerInternal.KeyguardExitAnimationStartListener;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
 
@@ -257,6 +258,7 @@
     private long mLastClipRevealTransitionDuration = DEFAULT_APP_TRANSITION_DURATION;
 
     private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
+    private KeyguardExitAnimationStartListener mKeyguardExitAnimationStartListener;
     private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
 
     private int mLastClipRevealMaxTranslation;
@@ -266,6 +268,7 @@
     private final boolean mLowRamRecentsEnabled;
 
     private final int mDefaultWindowAnimationStyleResId;
+    private boolean mOverrideTaskTransition;
 
     private RemoteAnimationController mRemoteAnimationController;
 
@@ -445,7 +448,7 @@
                 AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
 
         if (mRemoteAnimationController != null) {
-            mRemoteAnimationController.goodToGo();
+            mRemoteAnimationController.goodToGo(transit);
         }
         return redoLayout;
     }
@@ -508,6 +511,11 @@
         mListeners.remove(listener);
     }
 
+    void registerKeygaurdExitAnimationStartListener(
+            KeyguardExitAnimationStartListener listener) {
+        mKeyguardExitAnimationStartListener = listener;
+    }
+
     public void notifyAppTransitionFinishedLocked(IBinder token) {
         for (int i = 0; i < mListeners.size(); i++) {
             mListeners.get(i).onAppTransitionFinishedLocked(token);
@@ -971,7 +979,8 @@
             @Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction,
             boolean freeform, WindowContainer container) {
 
-        if (mNextAppTransitionOverrideRequested && container.canCustomizeAppTransition()) {
+        if (mNextAppTransitionOverrideRequested
+                && (container.canCustomizeAppTransition() || mOverrideTaskTransition)) {
             mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
         }
 
@@ -1175,7 +1184,8 @@
     }
 
     void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
-            IRemoteCallback startedCallback, IRemoteCallback endedCallback) {
+            IRemoteCallback startedCallback, IRemoteCallback endedCallback,
+            boolean overrideTaskTransaction) {
         if (canOverridePendingAppTransition()) {
             clear();
             mNextAppTransitionOverrideRequested = true;
@@ -1185,6 +1195,7 @@
             postAnimationCallback();
             mNextAppTransitionCallback = startedCallback;
             mAnimationFinishedCallback = endedCallback;
+            mOverrideTaskTransition = overrideTaskTransaction;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 582aeb3..4575cf7 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -102,6 +102,7 @@
     private final DisplayContent mDisplayContent;
     private final WallpaperController mWallpaperControllerLocked;
     private RemoteAnimationDefinition mRemoteAnimationDefinition = null;
+    private static final int KEYGUARD_GOING_AWAY_ANIMATION_DURATION = 400;
 
     private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>();
 
@@ -437,10 +438,14 @@
                 return adapter;
             }
         }
-        if (mRemoteAnimationDefinition == null) {
-            return null;
+        if (mRemoteAnimationDefinition != null) {
+            final RemoteAnimationAdapter adapter = mRemoteAnimationDefinition.getAdapter(
+                    transit, activityTypes);
+            if (adapter != null) {
+                return adapter;
+            }
         }
-        return mRemoteAnimationDefinition.getAdapter(transit, activityTypes);
+        return null;
     }
 
     /**
@@ -709,6 +714,13 @@
         applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
                 voiceInteraction);
 
+        for (int i = 0; i < openingApps.size(); ++i) {
+            openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
+        }
+        for (int i = 0; i < closingApps.size(); ++i) {
+            closingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
+        }
+
         final AccessibilityController accessibilityController =
                 mDisplayContent.mWmService.mAccessibilityController;
         if (accessibilityController != null) {
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 02e281f5..61fe023 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -25,7 +25,7 @@
 import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
 import static android.view.Display.TYPE_INTERNAL;
-import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
+import static android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES;
 import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
 import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
@@ -35,7 +35,7 @@
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_RIGHT_GESTURES;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.InsetsState.ITYPE_TOP_GESTURES;
+import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES;
 import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
 import static android.view.ViewRootImpl.computeWindowBounds;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
@@ -1074,7 +1074,7 @@
                             rect.bottom = rect.top + getStatusBarHeight(displayFrames);
                         };
                 mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, frameProvider);
-                mDisplayContent.setInsetProvider(ITYPE_TOP_GESTURES, win, frameProvider);
+                mDisplayContent.setInsetProvider(ITYPE_TOP_MANDATORY_GESTURES, win, frameProvider);
                 mDisplayContent.setInsetProvider(ITYPE_TOP_TAPPABLE_ELEMENT, win, frameProvider);
                 break;
             case TYPE_NAVIGATION_BAR:
@@ -1101,7 +1101,7 @@
                         (displayFrames, windowState, inOutFrame) ->
                                 inOutFrame.set(windowState.getFrame()));
 
-                mDisplayContent.setInsetProvider(ITYPE_BOTTOM_GESTURES, win,
+                mDisplayContent.setInsetProvider(ITYPE_BOTTOM_MANDATORY_GESTURES, win,
                         (displayFrames, windowState, inOutFrame) -> {
                             inOutFrame.top -= mBottomGestureAdditionalInset;
                         });
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 2274a4a..99c9e79 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -340,7 +340,7 @@
         }
 
         public void applySurfaceChanges(SurfaceControl.Transaction t) {
-            if (mSurfaceFrameRelative.equals(mLayoutFrameRelative)) {
+            if (!needsApplySurfaceChanges()) {
                 // Nothing changed.
                 return;
             }
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index a1d2072..392f27e 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -36,6 +36,7 @@
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
+import android.view.WindowManager;
 
 import com.android.internal.protolog.ProtoLogImpl;
 import com.android.internal.protolog.common.ProtoLog;
@@ -98,7 +99,7 @@
     /**
      * Called when the transition is ready to be started, and all leashes have been set up.
      */
-    void goodToGo() {
+    void goodToGo(@WindowManager.TransitionOldType int transit) {
         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");
         if (mPendingAnimations.isEmpty() || mCanceled) {
             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
@@ -123,11 +124,15 @@
 
         // Create the remote wallpaper animation targets (if any)
         final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
+
+        // TODO(bc-unlock): Create the remote non app animation targets (if any)
+        final RemoteAnimationTarget[] nonAppTargets = new RemoteAnimationTarget[0];
+
         mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
             try {
                 linkToDeathOfRunner();
-                mRemoteAnimationAdapter.getRunner().onAnimationStart(appTargets, wallpaperTargets,
-                        mFinishedCallback);
+                mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
+                        wallpaperTargets, nonAppTargets, mFinishedCallback);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to start remote animation", e);
                 onAnimationFinished();
@@ -274,6 +279,7 @@
     private void setRunningRemoteAnimation(boolean running) {
         final int pid = mRemoteAnimationAdapter.getCallingPid();
         final int uid = mRemoteAnimationAdapter.getCallingUid();
+
         if (pid == 0) {
             throw new RuntimeException("Calling pid of remote animation was null");
         }
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 6df4536..6567195 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -253,6 +253,20 @@
             throw new SecurityException(msg);
         }
 
+        // Check if the caller is allowed to override any app transition animation.
+        final boolean overrideTaskTransition = options.getOverrideTaskTransition();
+        if (aInfo != null && overrideTaskTransition) {
+            final int startTasksFromRecentsPerm = ActivityTaskManagerService.checkPermission(
+                    START_TASKS_FROM_RECENTS, callingPid, callingUid);
+            if (startTasksFromRecentsPerm != PERMISSION_GRANTED) {
+                final String msg = "Permission Denial: starting " + getIntentString(intent)
+                        + " from " + callerApp + " (pid=" + callingPid
+                        + ", uid=" + callingUid + ") with overrideTaskTransition=true";
+                Slog.w(TAG, msg);
+                throw new SecurityException(msg);
+            }
+        }
+
         // Check permission for remote animations
         final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
         if (adapter != null && supervisor.mService.checkPermission(
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 30af0ed..e44a028 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -77,7 +77,6 @@
 import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
 import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
@@ -145,7 +144,6 @@
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.TASK;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -921,10 +919,8 @@
             return;
         }
 
-        if (isLeafTask()) {
-            // This task is going away, so save the last state if necessary.
-            saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
-        }
+        // This task is going away, so save the last state if necessary.
+        saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
 
         // TODO: VI what about activity?
         final boolean isVoiceSession = voiceSession != null;
@@ -2459,14 +2455,16 @@
 
     /**
      * Saves launching state if necessary so that we can launch the activity to its latest state.
-     * It only saves state if this task has been shown to user and it's in fullscreen or freeform
-     * mode on freeform displays.
      */
     private void saveLaunchingStateIfNeeded() {
         saveLaunchingStateIfNeeded(getDisplayContent());
     }
 
     private void saveLaunchingStateIfNeeded(DisplayContent display) {
+        if (!isLeafTask()) {
+            return;
+        }
+
         if (!getHasBeenVisible()) {
             // Not ever visible to user.
             return;
@@ -2867,16 +2865,6 @@
         // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent".
         outBounds.setEmpty();
         computeLetterboxBounds(outBounds, newParentConfig);
-        // Since the task is letterboxed due to mismatched orientation against its parent,
-        // sandbox max bounds to the app bounds.
-        if (!outBounds.isEmpty()) {
-            if (DEBUG_CONFIGURATION) {
-                ProtoLog.d(WM_DEBUG_CONFIGURATION, "Sandbox max bounds due to mismatched "
-                                + "orientation with parent, to %s vs DisplayArea %s", outBounds,
-                        getDisplayArea() != null ? getDisplayArea().getBounds() : "null");
-            }
-            getResolvedOverrideConfiguration().windowConfiguration.setMaxBounds(outBounds);
-        }
     }
 
     /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 4185407..63732d8 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -47,6 +47,7 @@
 import android.os.UserHandle;
 import android.util.IntArray;
 import android.util.Slog;
+import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.window.WindowContainerTransaction;
 
@@ -905,6 +906,13 @@
         }
     }
 
+    @Override
+    RemoteAnimationTarget createRemoteAnimationTarget(
+            RemoteAnimationController.RemoteAnimationRecord record) {
+        final ActivityRecord activity = getTopMostActivity();
+        return activity != null ? activity.createRemoteAnimationTarget(record) : null;
+    }
+
     SurfaceControl getSplitScreenDividerAnchor() {
         return mSplitScreenDividerAnchor;
     }
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 46aea23..98eb11f 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -372,8 +372,6 @@
                     dc.mWallpaperController.startWallpaperAnimation(anim);
                 }
             }
-        }
-        if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
             dc.startKeyguardExitOnNonAppWindows(
                     (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0,
                     (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index a3a9eb7..eed3299 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -26,9 +26,11 @@
 import android.os.IBinder;
 import android.view.Display;
 import android.view.IInputFilter;
+import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IWindow;
 import android.view.InputChannel;
 import android.view.MagnificationSpec;
+import android.view.RemoteAnimationTarget;
 import android.view.WindowInfo;
 import android.view.WindowManager.DisplayImePolicy;
 
@@ -154,6 +156,21 @@
     }
 
     /**
+     * An interface to be notified when keyguard exit animation should start.
+     */
+    public interface KeyguardExitAnimationStartListener {
+        /**
+         * Called when keyguard exit animation should start.
+         * @param apps The list of apps to animate.
+         * @param wallpapers The list of wallpapers to animate.
+         * @param finishedCallback The callback to invoke when the animation is finished.
+         */
+        void onAnimationStart(RemoteAnimationTarget[] apps,
+                RemoteAnimationTarget[] wallpapers,
+                IRemoteAnimationFinishedCallback finishedCallback);
+    }
+
+    /**
       * An interface to be notified about hardware keyboard status.
       */
     public interface OnHardKeyboardStatusChangeListener {
@@ -372,6 +389,14 @@
     public abstract void registerAppTransitionListener(AppTransitionListener listener);
 
     /**
+     * Registers a listener to be notified to start the keyguard exit animation.
+     *
+     * @param listener The listener to register.
+     */
+    public abstract void registerKeyguardExitAnimationStartListener(
+            KeyguardExitAnimationStartListener listener);
+
+    /**
      * Reports that the password for the given user has changed.
      */
     public abstract void reportPasswordChanged(int userId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 673c6a5..8438118 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -405,6 +405,8 @@
     // trying to apply a new one.
     private static final boolean ALWAYS_KEEP_CURRENT = true;
 
+    static final int LOGTAG_INPUT_FOCUS = 62001;
+
     /**
      * Restrict ability of activities overriding transition animation in a way such that
      * an activity can do it only when the transition happens within a same task.
@@ -413,7 +415,6 @@
      */
     private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY =
             "persist.wm.disable_custom_task_animation";
-    static final int LOGTAG_INPUT_FOCUS = 62001;
 
     /**
      * @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
@@ -421,6 +422,19 @@
     static boolean sDisableCustomTaskAnimationProperty =
             SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
 
+    /**
+     * Run Keyguard animation as remote animation in System UI instead of local animation in
+     * the server process.
+     */
+    private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
+            "persist.wm.enable_remote_keyguard_animation";
+
+    /**
+     * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+     */
+    public static boolean sEnableRemoteKeyguardAnimation =
+            SystemProperties.getBoolean(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, false);
+
     private static final String DISABLE_TRIPLE_BUFFERING_PROPERTY =
             "ro.sf.disable_triple_buffer";
 
@@ -770,7 +784,8 @@
     final AnrController mAnrController;
 
     private final ScreenshotHashController mScreenshotHashController;
-    private final WindowContextListenerController mWindowContextListenerController =
+    @VisibleForTesting
+    final WindowContextListenerController mWindowContextListenerController =
             new WindowContextListenerController();
 
     @VisibleForTesting
@@ -2794,6 +2809,17 @@
         return WindowManagerGlobal.ADD_OKAY;
     }
 
+    /**
+     * Registers a listener for a {@link android.app.WindowContext} to subscribe to configuration
+     * changes of a {@link DisplayArea}.
+     *
+     * @param clientToken the window context's token
+     * @param type Window type of the window context
+     * @param displayId The display associated with the window context
+     * @param options A bundle used to pass window-related options and choose the right DisplayArea
+     *
+     * @return {@code true} if the listener was registered successfully.
+     */
     @Override
     public boolean registerWindowContextListener(IBinder clientToken, int type, int displayId,
             Bundle options) {
@@ -2849,6 +2875,7 @@
         }
     }
 
+    /** Returns {@code true} if this binder is a registered window token. */
     @Override
     public boolean isWindowToken(IBinder binder) {
         synchronized (mGlobalLock) {
@@ -7750,6 +7777,15 @@
         }
 
         @Override
+        public void registerKeyguardExitAnimationStartListener(
+                KeyguardExitAnimationStartListener listener) {
+            synchronized (mGlobalLock) {
+                getDefaultDisplayContentLocked().mAppTransition
+                        .registerKeygaurdExitAnimationStartListener(listener);
+            }
+        }
+
+        @Override
         public void reportPasswordChanged(int userId) {
             mKeyguardDisableHandler.updateKeyguardEnabled(userId);
         }
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 8a512bc..cd18311 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import static android.os.Process.INVALID_UID;
-import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -26,7 +25,6 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_MOVEMENT;
-import static com.android.internal.protolog.ProtoLogGroup.WM_ERROR;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
@@ -41,7 +39,6 @@
 
 import android.annotation.CallSuper;
 import android.annotation.Nullable;
-import android.app.IWindowToken;
 import android.app.servertransaction.FixedRotationAdjustmentsItem;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -58,7 +55,6 @@
 import android.view.SurfaceControl;
 import android.view.WindowManager;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.policy.WindowManagerPolicy;
 
@@ -112,17 +108,10 @@
 
     private FixedRotationTransformState mFixedRotationTransformState;
 
-    private Configuration mLastReportedConfig;
-    private int mLastReportedDisplay = INVALID_DISPLAY;
-
     /**
      * When set to {@code true}, this window token is created from {@link android.app.WindowContext}
      */
-    @VisibleForTesting
-    final boolean mFromClientToken;
-
-    private DeathRecipient mDeathRecipient;
-    private boolean mBinderDied = false;
+    private final boolean mFromClientToken;
 
     private final int mOwnerUid;
 
@@ -188,30 +177,6 @@
         }
     }
 
-    private class DeathRecipient implements IBinder.DeathRecipient {
-        private boolean mHasUnlinkToDeath = false;
-
-        @Override
-        public void binderDied() {
-            synchronized (mWmService.mGlobalLock) {
-                mBinderDied = true;
-                removeImmediately();
-            }
-        }
-
-        void linkToDeath() throws RemoteException {
-            token.linkToDeath(DeathRecipient.this, 0);
-        }
-
-        void unlinkToDeath() {
-            if (mHasUnlinkToDeath) {
-                return;
-            }
-            token.unlinkToDeath(DeathRecipient.this, 0);
-            mHasUnlinkToDeath = true;
-        }
-    }
-
     /**
      * Compares two child window of this token and returns -1 if the first is lesser than the
      * second in terms of z-order and 1 otherwise.
@@ -266,17 +231,6 @@
         if (dc != null) {
             dc.addWindowToken(token, this);
         }
-        if (shouldReportToClient()) {
-            try {
-                mDeathRecipient = new DeathRecipient();
-                mDeathRecipient.linkToDeath();
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to add window token with type " + windowType + " on "
-                        + "display " + dc.getDisplayId(), e);
-                mDeathRecipient = null;
-                return;
-            }
-        }
     }
 
     void removeAllWindowsIfPossible() {
@@ -414,22 +368,6 @@
         // Needs to occur after the token is removed from the display above to avoid attempt at
         // duplicate removal of this window container from it's parent.
         super.removeImmediately();
-
-        reportWindowTokenRemovedToClient();
-    }
-
-    // TODO(b/159767464): Remove after we migrate to listener approach.
-    private void reportWindowTokenRemovedToClient() {
-        if (!shouldReportToClient()) {
-            return;
-        }
-        mDeathRecipient.unlinkToDeath();
-        IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
-        try {
-            windowTokenClient.onWindowTokenRemoved();
-        } catch (RemoteException e) {
-            ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client.");
-        }
     }
 
     @Override
@@ -441,51 +379,11 @@
         // to another display before the window behind
         // it is ready.
         super.onDisplayChanged(dc);
-        reportConfigToWindowTokenClient();
     }
 
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
         super.onConfigurationChanged(newParentConfig);
-        reportConfigToWindowTokenClient();
-    }
-
-    void reportConfigToWindowTokenClient() {
-        if (!shouldReportToClient()) {
-            return;
-        }
-        if (mLastReportedConfig == null) {
-            mLastReportedConfig = new Configuration();
-        }
-        final Configuration config = getConfiguration();
-        final int displayId = getDisplayContent().getDisplayId();
-        if (config.diff(mLastReportedConfig) == 0 && displayId == mLastReportedDisplay) {
-            // No changes since last reported time.
-            return;
-        }
-
-        mLastReportedConfig.setTo(config);
-        mLastReportedDisplay = displayId;
-
-        IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token);
-        try {
-            windowTokenClient.onConfigurationChanged(config, displayId);
-        } catch (RemoteException e) {
-            ProtoLog.w(WM_ERROR,
-                    "Could not report config changes to the window token client.");
-        }
-    }
-
-    /**
-     * @return {@code true} if this {@link WindowToken} is not an {@link ActivityRecord} and
-     * registered from client side.
-     */
-    private boolean shouldReportToClient() {
-        // Only report to client for WindowToken because Activities are updated through ATM
-        // callbacks.
-        return asActivityRecord() == null
-        // Report to {@link android.view.WindowTokenClient} if this token was registered from it.
-                && mFromClientToken && !mBinderDied;
     }
 
     @Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 11e4db5..160033e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -16,6 +16,7 @@
 package com.android.server.devicepolicy;
 
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.admin.DevicePolicySafetyChecker;
 import android.app.admin.FullyManagedDeviceProvisioningParams;
 import android.app.admin.IDevicePolicyManager;
@@ -127,4 +128,7 @@
     public void provisionFullyManagedDevice(
             FullyManagedDeviceProvisioningParams provisioningParams, String callerPackage) {
     }
+
+    public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3d2e5de..717e77b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -910,12 +910,20 @@
     };
 
     protected static class RestrictionsListener implements UserRestrictionsListener {
-        private Context mContext;
+        private final Context mContext;
+        private final UserManagerInternal mUserManagerInternal;
+        private final DevicePolicyManagerService mDpms;
 
-        public RestrictionsListener(Context context) {
+        public RestrictionsListener(
+                Context context,
+                UserManagerInternal userManagerInternal,
+                DevicePolicyManagerService dpms) {
             mContext = context;
+            mUserManagerInternal = userManagerInternal;
+            mDpms = dpms;
         }
 
+        @Override
         public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
                 Bundle prevRestrictions) {
             final boolean newlyDisallowed =
@@ -925,13 +933,19 @@
             final boolean restrictionChanged = (newlyDisallowed != previouslyDisallowed);
 
             if (restrictionChanged) {
-                // Notify ManagedProvisioning to update the built-in cross profile intent filters.
-                Intent intent = new Intent(
-                        DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED);
-                intent.setPackage(getManagedProvisioningPackage(mContext));
-                intent.putExtra(Intent.EXTRA_USER_ID, userId);
-                intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+                final int parentId = mUserManagerInternal.getProfileParentId(userId);
+                if (parentId == userId) {
+                    return;
+                }
+
+                // Always reset filters on the parent user, which handles cross profile intent
+                // filters between the parent and its profiles.
+                Slog.i(LOG_TAG, "Resetting cross-profile intent filters on restriction "
+                        + "change");
+                mDpms.resetDefaultCrossProfileIntentFilters(parentId);
+                mContext.sendBroadcastAsUser(new Intent(
+                                DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED),
+                        UserHandle.of(userId));
             }
         }
     }
@@ -1621,7 +1635,8 @@
 
         mSetupContentObserver = new SetupContentObserver(mHandler);
 
-        mUserManagerInternal.addUserRestrictionsListener(new RestrictionsListener(mContext));
+        mUserManagerInternal.addUserRestrictionsListener(
+                new RestrictionsListener(mContext, mUserManagerInternal, this));
         mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
 
         loadOwners();
@@ -16008,19 +16023,9 @@
                     provisioningParams.isKeepAccountMigrated(), callerPackage);
 
             if (provisioningParams.isOrganizationOwnedProvisioning()) {
-                markIsProfileOwnerOnOrganizationOwnedDevice(admin, userInfo.id);
-                restrictRemovalOfManagedProfile(admin, userInfo.id);
+                setProfileOwnerOnOrgOwnedDeviceState(admin, userInfo.id, caller.getUserId());
             }
 
-            final Intent intent = new Intent(DevicePolicyManager.ACTION_MANAGED_PROFILE_CREATED)
-                    .putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id)
-                    .putExtra(
-                            DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
-                            provisioningParams.isLeaveAllSystemAppsEnabled())
-                    .setPackage(getManagedProvisioningPackage(mContext))
-                    .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-            mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
-
             return userInfo.getUserHandle();
         } catch (Exception e) {
             DevicePolicyEventLogger
@@ -16250,21 +16255,20 @@
         }
     }
 
-    private void markIsProfileOwnerOnOrganizationOwnedDevice(
-            ComponentName admin, @UserIdInt int profileId) {
-        getDpmForProfile(profileId).markProfileOwnerOnOrganizationOwnedDevice(admin);
+    private void setProfileOwnerOnOrgOwnedDeviceState(
+            ComponentName admin, @UserIdInt int profileId, @UserIdInt int parentUserId) {
+        synchronized (getLockObject()) {
+            markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(admin, profileId);
+        }
+        restrictRemovalOfManagedProfile(parentUserId);
     }
 
-    private void restrictRemovalOfManagedProfile(
-            ComponentName admin, @UserIdInt int profileId) {
-        getDpmForProfile(profileId).addUserRestriction(
-                admin, UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
-    }
-
-    private DevicePolicyManager getDpmForProfile(@UserIdInt int profileId) {
-        final Context profileContext = mContext.createContextAsUser(
-                UserHandle.of(profileId), /* flags= */ 0);
-        return profileContext.getSystemService(DevicePolicyManager.class);
+    private void restrictRemovalOfManagedProfile(@UserIdInt int parentUserId) {
+        final UserHandle parentUserHandle = UserHandle.of(parentUserId);
+        mUserManager.setUserRestriction(
+                UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+                /* value= */ true,
+                parentUserHandle);
     }
 
     @Override
@@ -16313,15 +16317,6 @@
             }
 
             disallowAddUser();
-
-            final Intent intent = new Intent(DevicePolicyManager.ACTION_PROVISIONED_MANAGED_DEVICE)
-                    .putExtra(Intent.EXTRA_USER_HANDLE, caller.getUserId())
-                    .putExtra(
-                            DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
-                            provisioningParams.isLeaveAllSystemAppsEnabled())
-                    .setPackage(getManagedProvisioningPackage(mContext))
-                    .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-            mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
         } catch (Exception e) {
             DevicePolicyEventLogger
                     .createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR)
@@ -16420,4 +16415,45 @@
                 .setStrings(callerPackage)
                 .write();
     }
+
+    @Override
+    public void resetDefaultCrossProfileIntentFilters(@UserIdInt int userId) {
+        Preconditions.checkCallAuthorization(
+                hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+        mInjector.binderWithCleanCallingIdentity(() -> {
+            try {
+                final List<UserInfo> profiles = mUserManager.getProfiles(userId);
+                final int numOfProfiles = profiles.size();
+                if (numOfProfiles <= 1) {
+                    return;
+                }
+
+                final String managedProvisioningPackageName = getManagedProvisioningPackage(
+                        mContext);
+                // Removes cross profile intent filters from the parent to all the profiles.
+                mIPackageManager.clearCrossProfileIntentFilters(
+                        userId, mContext.getOpPackageName());
+                // Setting and resetting default cross profile intent filters was previously handled
+                // by Managed Provisioning. For backwards compatibility, clear any intent filters
+                // that were set by ManagedProvisioning.
+                mIPackageManager.clearCrossProfileIntentFilters(
+                        userId, managedProvisioningPackageName);
+
+                // For each profile reset cross profile intent filters
+                for (int i = 0; i < numOfProfiles; i++) {
+                    UserInfo profile = profiles.get(i);
+                    mIPackageManager.clearCrossProfileIntentFilters(
+                            profile.id, mContext.getOpPackageName());
+                    // Clear any intent filters that were set by ManagedProvisioning.
+                    mIPackageManager.clearCrossProfileIntentFilters(
+                            profile.id, managedProvisioningPackageName);
+
+                    mUserManagerInternal.setDefaultCrossProfileIntentFilters(userId, profile.id);
+                }
+            } catch (RemoteException e) {
+                // Shouldn't happen.
+            }
+        });
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 8c853cc..7597cbf 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5616,43 +5616,52 @@
     public void testDisallowSharingIntoProfileSetRestriction() {
         when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package))
                 .thenReturn("com.android.managedprovisioning");
+        when(getServices().userManagerInternal.getProfileParentId(anyInt()))
+                .thenReturn(UserHandle.USER_SYSTEM);
+        mServiceContext.binder.callingPid = DpmMockContext.SYSTEM_PID;
+        mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
         Bundle restriction = new Bundle();
         restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true);
 
-        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
-        RestrictionsListener listener = new RestrictionsListener(mContext);
+        RestrictionsListener listener = new RestrictionsListener(
+                mServiceContext, getServices().userManagerInternal, dpms);
         listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, restriction, new Bundle());
-        verifyDataSharingChangedBroadcast();
+
+        verifyDataSharingAppliedBroadcast();
     }
 
     @Test
     public void testDisallowSharingIntoProfileClearRestriction() {
         when(mServiceContext.resources.getString(R.string.config_managed_provisioning_package))
                 .thenReturn("com.android.managedprovisioning");
+        when(getServices().userManagerInternal.getProfileParentId(anyInt()))
+                .thenReturn(UserHandle.USER_SYSTEM);
+        mServiceContext.binder.callingPid = DpmMockContext.SYSTEM_PID;
+        mServiceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
         Bundle restriction = new Bundle();
         restriction.putBoolean(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, true);
 
-        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
-        RestrictionsListener listener = new RestrictionsListener(mContext);
+        RestrictionsListener listener = new RestrictionsListener(
+                mServiceContext, getServices().userManagerInternal, dpms);
         listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), restriction);
-        verifyDataSharingChangedBroadcast();
+
+        verifyDataSharingAppliedBroadcast();
     }
 
     @Test
     public void testDisallowSharingIntoProfileUnchanged() {
-        RestrictionsListener listener = new RestrictionsListener(mContext);
+        RestrictionsListener listener = new RestrictionsListener(
+                mContext, getServices().userManagerInternal, dpms);
         listener.onUserRestrictionsChanged(CALLER_USER_HANDLE, new Bundle(), new Bundle());
         verify(mContext.spiedContext, never()).sendBroadcastAsUser(any(), any());
     }
 
-    private void verifyDataSharingChangedBroadcast() {
+    private void verifyDataSharingAppliedBroadcast() {
         Intent expectedIntent = new Intent(
-                DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_CHANGED);
-        expectedIntent.setPackage("com.android.managedprovisioning");
-        expectedIntent.putExtra(Intent.EXTRA_USER_ID, CALLER_USER_HANDLE);
+                DevicePolicyManager.ACTION_DATA_SHARING_RESTRICTION_APPLIED);
         verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
                 MockUtils.checkIntent(expectedIntent),
-                MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+                MockUtils.checkUserHandle(CALLER_USER_HANDLE));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index df19aeb..58ba907 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1829,11 +1829,11 @@
     }
 
     /**
-     * Exhaustively test isUidNetworkingBlocked to output the expected results based on external
+     * Exhaustively test checkUidNetworkingBlocked to output the expected results based on external
      * conditions.
      */
     @Test
-    public void testIsUidNetworkingBlocked() {
+    public void testCheckUidNetworkingBlocked() {
         final ArrayList<Pair<Boolean, Integer>> expectedBlockedStates = new ArrayList<>();
 
         // Metered network. Data saver on.
@@ -1877,17 +1877,16 @@
 
     private void verifyNetworkBlockedState(boolean metered, boolean backgroundRestricted,
             ArrayList<Pair<Boolean, Integer>> expectedBlockedStateForRules) {
-        final NetworkPolicyManagerInternal npmi = LocalServices
-                .getService(NetworkPolicyManagerInternal.class);
 
         for (Pair<Boolean, Integer> pair : expectedBlockedStateForRules) {
             final boolean expectedResult = pair.first;
             final int rule = pair.second;
             assertEquals(formatBlockedStateError(UID_A, rule, metered, backgroundRestricted),
-                    expectedResult,
-                    npmi.isUidNetworkingBlocked(UID_A, rule, metered, backgroundRestricted));
+                    expectedResult, mService.checkUidNetworkingBlocked(UID_A, rule,
+                            metered, backgroundRestricted));
             assertFalse(formatBlockedStateError(SYSTEM_UID, rule, metered, backgroundRestricted),
-                    npmi.isUidNetworkingBlocked(SYSTEM_UID, rule, metered, backgroundRestricted));
+                    mService.checkUidNetworkingBlocked(SYSTEM_UID, rule, metered,
+                            backgroundRestricted));
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
new file mode 100644
index 0000000..764c504
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/BundleUtilsTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import static com.android.server.devicepolicy.DpmTestUtils.assertRestrictions;
+import static com.android.server.devicepolicy.DpmTestUtils.newRestrictions;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.BundleUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Build/Install/Run:
+ * atest com.android.server.pm.BundleUtilsTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BundleUtilsTest {
+
+    @Test
+    public void testIsEmpty() {
+        assertThat(BundleUtils.isEmpty(null)).isTrue();
+        assertThat(BundleUtils.isEmpty(new Bundle())).isTrue();
+        assertThat(BundleUtils.isEmpty(newRestrictions("a"))).isFalse();
+    }
+
+    @Test
+    public void testClone() {
+        Bundle in = new Bundle();
+        Bundle out = BundleUtils.clone(in);
+        assertThat(in).isNotSameInstanceAs(out);
+        assertRestrictions(out, new Bundle());
+
+        out = BundleUtils.clone(null);
+        assertThat(out).isNotNull();
+        out.putBoolean("a", true); // Should not be Bundle.EMPTY.
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index ee30f68..cd98d44 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -72,11 +72,18 @@
     @Test
     public void testUserTypeBuilder_createUserType() {
         final Bundle restrictions = makeRestrictionsBundle("r1", "r2");
+        final Bundle systemSettings = makeSettingsBundle("s1", "s2");
+        final Bundle secureSettings = makeSettingsBundle("secure_s1", "secure_s2");
+        final List<DefaultCrossProfileIntentFilter> filters = List.of(
+                new DefaultCrossProfileIntentFilter.Builder(
+                DefaultCrossProfileIntentFilter.Direction.TO_PARENT,
+                /* flags= */0,
+                /* letsPersonalDataIntoProfile= */false).build());
         final UserTypeDetails type = new UserTypeDetails.Builder()
                 .setName("a.name")
                 .setEnabled(true)
                 .setMaxAllowed(21)
-                .setBaseType(FLAG_FULL)
+                .setBaseType(FLAG_PROFILE)
                 .setDefaultUserInfoPropertyFlags(FLAG_EPHEMERAL)
                 .setBadgeLabels(23, 24, 25)
                 .setBadgeColors(26, 27)
@@ -86,20 +93,45 @@
                 .setLabel(31)
                 .setMaxAllowedPerParent(32)
                 .setDefaultRestrictions(restrictions)
+                .setDefaultSystemSettings(systemSettings)
+                .setDefaultSecureSettings(secureSettings)
+                .setDefaultCrossProfileIntentFilters(filters)
                 .createUserTypeDetails();
 
         assertEquals("a.name", type.getName());
         assertTrue(type.isEnabled());
         assertEquals(21, type.getMaxAllowed());
-        assertEquals(FLAG_FULL | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
+        assertEquals(FLAG_PROFILE | FLAG_EPHEMERAL, type.getDefaultUserInfoFlags());
         assertEquals(28, type.getIconBadge());
         assertEquals(29, type.getBadgePlain());
         assertEquals(30, type.getBadgeNoBackground());
         assertEquals(31, type.getLabel());
         assertEquals(32, type.getMaxAllowedPerParent());
+
         assertTrue(UserRestrictionsUtils.areEqual(restrictions, type.getDefaultRestrictions()));
         assertNotSame(restrictions, type.getDefaultRestrictions());
 
+        assertNotSame(systemSettings, type.getDefaultSystemSettings());
+        assertEquals(systemSettings.size(), type.getDefaultSystemSettings().size());
+        for (String key : systemSettings.keySet()) {
+            assertEquals(
+                    systemSettings.getString(key),
+                    type.getDefaultSystemSettings().getString(key));
+        }
+
+        assertNotSame(secureSettings, type.getDefaultSecureSettings());
+        assertEquals(secureSettings.size(), type.getDefaultSecureSettings().size());
+        for (String key : secureSettings.keySet()) {
+            assertEquals(
+                    secureSettings.getString(key),
+                    type.getDefaultSecureSettings().getString(key));
+        }
+
+        assertNotSame(filters, type.getDefaultCrossProfileIntentFilters());
+        assertEquals(filters.size(), type.getDefaultCrossProfileIntentFilters().size());
+        for (int i = 0; i < filters.size(); i++) {
+            assertEquals(filters.get(i), type.getDefaultCrossProfileIntentFilters().get(i));
+        }
 
         assertEquals(23, type.getBadgeLabel(0));
         assertEquals(24, type.getBadgeLabel(1));
@@ -135,6 +167,9 @@
         assertEquals(Resources.ID_NULL, type.getBadgeColor(0));
         assertEquals(Resources.ID_NULL, type.getLabel());
         assertTrue(type.getDefaultRestrictions().isEmpty());
+        assertTrue(type.getDefaultSystemSettings().isEmpty());
+        assertTrue(type.getDefaultSecureSettings().isEmpty());
+        assertTrue(type.getDefaultCrossProfileIntentFilters().isEmpty());
 
         assertFalse(type.hasBadge());
     }
@@ -416,4 +451,13 @@
         }
         return bundle;
     }
+
+    /** Creates a Bundle of the given settings keys and puts true for the value. */
+    private static Bundle makeSettingsBundle(String ... settings) {
+        final Bundle bundle = new Bundle();
+        for (String setting : settings) {
+            bundle.putBoolean(setting, true);
+        }
+        return bundle;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index dc181a9..ddf0cd0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -48,23 +48,6 @@
         assertSame(in, UserRestrictionsUtils.nonNull(in));
     }
 
-    public void testIsEmpty() {
-        assertTrue(UserRestrictionsUtils.isEmpty(null));
-        assertTrue(UserRestrictionsUtils.isEmpty(new Bundle()));
-        assertFalse(UserRestrictionsUtils.isEmpty(newRestrictions("a")));
-    }
-
-    public void testClone() {
-        Bundle in = new Bundle();
-        Bundle out = UserRestrictionsUtils.clone(in);
-        assertNotSame(in, out);
-        assertRestrictions(out, new Bundle());
-
-        out = UserRestrictionsUtils.clone(null);
-        assertNotNull(out);
-        out.putBoolean("a", true); // Should not be Bundle.EMPTY.
-    }
-
     public void testMerge() {
         Bundle a = newRestrictions("a", "d");
         Bundle b = newRestrictions("b", "d", "e");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index fc1cb70..acda4d5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -29,8 +29,8 @@
 import static android.app.NotificationManager.IMPORTANCE_MAX;
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
 
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
 import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_ID_FIELD_NUMBER;
 import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_NAME_FIELD_NUMBER;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 57d5323..72c6028 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -33,8 +33,8 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
 
-import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID;
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 import static com.android.os.AtomsProto.DNDModeProto.CHANNELS_BYPASSING_FIELD_NUMBER;
 import static com.android.os.AtomsProto.DNDModeProto.ENABLED_FIELD_NUMBER;
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 42b080e..9385110 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -679,8 +679,10 @@
                 new RemoteAnimationAdapter(new Stub() {
 
                     @Override
-                    public void onAnimationStart(RemoteAnimationTarget[] apps,
+                    public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                            RemoteAnimationTarget[] apps,
                             RemoteAnimationTarget[] wallpapers,
+                            RemoteAnimationTarget[] nonApps,
                             IRemoteAnimationFinishedCallback finishedCallback) {
                     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 91b9449..71f1914 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -36,6 +36,7 @@
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationTarget;
+import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
 
@@ -70,8 +71,10 @@
 
     class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
         @Override
-        public void onAnimationStart(RemoteAnimationTarget[] apps,
+        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                RemoteAnimationTarget[] apps,
                 RemoteAnimationTarget[] wallpapers,
+                RemoteAnimationTarget[] nonApps,
                 IRemoteAnimationFinishedCallback finishedCallback) {
             for (RemoteAnimationTarget target : apps) {
                 assertNotNull(target.startBounds);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index f1e3609..83aca5e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -265,8 +265,10 @@
     private class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
         boolean mCancelled = false;
         @Override
-        public void onAnimationStart(RemoteAnimationTarget[] apps,
+        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                RemoteAnimationTarget[] apps,
                 RemoteAnimationTarget[] wallpapers,
+                RemoteAnimationTarget[] nonApps,
                 IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
index f75c98f..78074d6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodMenuControllerTest.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -26,7 +27,6 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 
@@ -70,13 +70,15 @@
         spyOn(wms);
         doAnswer(invocation -> {
             Object[] args = invocation.getArguments();
-            final IBinder token = (IBinder) args[0];
-            final int windowType = (int) args[1];
-            new WindowToken(mWm, token, windowType, true /* persistOnEmpty */,
-                    mDefaultDisplay, true /* ownerCanManageAppTokens */, 1000 /* ownerUid */,
-                    false /* roundedCornerOverlay */, true /* fromClientToken */);
-            return WindowManagerGlobal.ADD_OKAY;
-        }).when(wms).addWindowTokenWithOptions(any(), anyInt(), anyInt(), any(), anyString());
+            IBinder clientToken = (IBinder) args[0];
+            int displayId = (int) args[2];
+            DisplayContent dc = mWm.mRoot.getDisplayContent(displayId);
+            mWm.mWindowContextListenerController.registerWindowContainerListener(clientToken,
+                    dc.getImeContainer(), 1000 /* ownerUid */, TYPE_INPUT_METHOD_DIALOG,
+                    null /* options */);
+            return true;
+        }).when(wms).registerWindowContextListener(any(), eq(TYPE_INPUT_METHOD_DIALOG),
+                anyInt(), any());
 
         mSecondaryDisplay = new TestDisplayContent.Builder(mAtm, 1000, 1000).build();
 
@@ -95,14 +97,12 @@
 
         assertImeSwitchContextMetricsValidity(contextOnDefaultDisplay, mDefaultDisplay);
 
-        // Obtain the context again and check they are the same instance and match the display
-        // metrics of the secondary display.
+        // Obtain the context again and check if the window metrics match the IME container bounds
+        // of the secondary display.
         final Context contextOnSecondaryDisplay = mController.getSettingsContext(
                 mSecondaryDisplay.getDisplayId());
 
         assertImeSwitchContextMetricsValidity(contextOnSecondaryDisplay, mSecondaryDisplay);
-        assertThat(contextOnDefaultDisplay.getWindowContextToken())
-                .isEqualTo(contextOnSecondaryDisplay.getWindowContextToken());
     }
 
     private void assertImeSwitchContextMetricsValidity(Context context, DisplayContent dc) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index fa3e3ae..7714a6c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -183,6 +183,10 @@
         mColor = Color.GREEN;
 
         assertTrue(mLetterbox.needsApplySurfaceChanges());
+
+        mLetterbox.applySurfaceChanges(mTransaction);
+
+        verify(mTransaction).setColor(mSurfaces.top, new float[]{0, 1, 0});
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 2efd4b5..15e045c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -17,6 +17,9 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_OLD_NONE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -100,15 +103,18 @@
                     new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
             mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> nonApsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                     ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-            verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+                    appsCaptor.capture(), wallpapersCaptor.capture(), nonApsCaptor.capture(),
                     finishedCaptor.capture());
             assertEquals(1, appsCaptor.getValue().length);
             final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -136,7 +142,7 @@
                 new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
 
         adapter.onAnimationCancelled(mMockLeash);
         verify(mMockRunner).onAnimationCancelled();
@@ -149,7 +155,7 @@
                 new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
 
         mClock.fastForward(2500);
         mHandler.timeAdvance();
@@ -170,7 +176,7 @@
                     null).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
 
             mClock.fastForward(2500);
             mHandler.timeAdvance();
@@ -190,7 +196,7 @@
 
     @Test
     public void testZeroAnimations() {
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_NONE);
         verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
     }
 
@@ -199,7 +205,7 @@
         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
         mController.createRemoteAnimationRecord(win.mActivityRecord,
                 new Point(50, 100), null, new Rect(50, 100, 150, 150), null);
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
         verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
     }
 
@@ -213,15 +219,18 @@
                 new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
         mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
         final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                 ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
         final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                 ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+        final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+                ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
         final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                 ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-        verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+        verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+                appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
                 finishedCaptor.capture());
         assertEquals(1, appsCaptor.getValue().length);
         assertEquals(mMockLeash, appsCaptor.getValue()[0].leash);
@@ -235,7 +244,7 @@
         adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                 mFinishedCallback);
         win.mActivityRecord.removeImmediately();
-        mController.goodToGo();
+        mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
         verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
         verify(mFinishedCallback).onAnimationFinished(eq(ANIMATION_TYPE_APP_TRANSITION),
                 eq(adapter));
@@ -255,15 +264,18 @@
                             mFinishedCallback);
             ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
                     mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
             mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                     ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-            verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE),
+                    appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
                     finishedCaptor.capture());
             assertEquals(1, appsCaptor.getValue().length);
             final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -305,15 +317,18 @@
                             mFinishedCallback);
             ((AnimationAdapter) record.mThumbnailAdapter).startAnimation(mMockThumbnailLeash,
                     mMockTransaction, ANIMATION_TYPE_WINDOW_ANIMATION, mThumbnailFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE);
             mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                     ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-            verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE),
+                    appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
                     finishedCaptor.capture());
             assertEquals(1, appsCaptor.getValue().length);
             final RemoteAnimationTarget app = appsCaptor.getValue()[0];
@@ -354,15 +369,18 @@
                     new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
             mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> nonAppsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                     ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-            verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+                    appsCaptor.capture(), wallpapersCaptor.capture(), nonAppsCaptor.capture(),
                     finishedCaptor.capture());
             assertEquals(1, wallpapersCaptor.getValue().length);
         } finally {
@@ -383,15 +401,18 @@
                     new Point(50, 100), null, new Rect(50, 100, 150, 150), null).mAdapter;
             adapter.startAnimation(mMockLeash, mMockTransaction, ANIMATION_TYPE_APP_TRANSITION,
                     mFinishedCallback);
-            mController.goodToGo();
+            mController.goodToGo(TRANSIT_OLD_ACTIVITY_OPEN);
             mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
             final ArgumentCaptor<RemoteAnimationTarget[]> appsCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<RemoteAnimationTarget[]> wallpapersCaptor =
                     ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
+            final ArgumentCaptor<RemoteAnimationTarget[]> nonAPpsCaptor =
+                    ArgumentCaptor.forClass(RemoteAnimationTarget[].class);
             final ArgumentCaptor<IRemoteAnimationFinishedCallback> finishedCaptor =
                     ArgumentCaptor.forClass(IRemoteAnimationFinishedCallback.class);
-            verify(mMockRunner).onAnimationStart(appsCaptor.capture(), wallpapersCaptor.capture(),
+            verify(mMockRunner).onAnimationStart(eq(TRANSIT_OLD_ACTIVITY_OPEN),
+                    appsCaptor.capture(), wallpapersCaptor.capture(), nonAPpsCaptor.capture(),
                     finishedCaptor.capture());
             assertEquals(1, wallpapersCaptor.getValue().length);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 6f775cf..cc4d4ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -39,8 +39,6 @@
 import static com.android.server.wm.Task.ActivityState.STOPPED;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
-import static com.google.common.truth.Truth.assertThat;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -119,13 +117,13 @@
     @Test
     public void testKeepBoundsWhenChangingFromFreeformToFullscreen() {
         removeGlobalMinSizeRestriction();
-        // Create landscape freeform display and a freeform app.
+        // create freeform display and a freeform app
         DisplayContent display = new TestDisplayContent.Builder(mAtm, 2000, 1000)
                 .setCanRotate(false)
                 .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM).build();
         setUpApp(display);
 
-        // Put app window into portrait freeform and then make it a compat app.
+        // Put app window into freeform and then make it a compat app.
         final Rect bounds = new Rect(100, 100, 400, 600);
         mTask.setBounds(bounds);
         prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
@@ -138,7 +136,7 @@
 
         final int density = mActivity.getConfiguration().densityDpi;
 
-        // Change display configuration to fullscreen.
+        // change display configuration to fullscreen
         Configuration c = new Configuration(display.getRequestedOverrideConfiguration());
         c.windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
         display.onRequestedOverrideConfigurationChanged(c);
@@ -148,8 +146,6 @@
         assertEquals(bounds.width(), mActivity.getBounds().width());
         assertEquals(bounds.height(), mActivity.getBounds().height());
         assertEquals(density, mActivity.getConfiguration().densityDpi);
-        // Size compat mode is sandboxed at the activity level.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
     }
 
     @Test
@@ -175,12 +171,6 @@
         assertEquals(appBounds.height(), appBounds.width() * aspectRatio, 0.5f /* delta */);
         // The decor height should be a part of the effective bounds.
         assertEquals(mActivity.getBounds().height(), appBounds.height() + notchHeight);
-        // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
-        assertActivityMaxBoundsSandboxedForLetterbox();
-        // Activity max bounds ignore notch, since an app can be shown past the notch (although app
-        // is currently limited by the notch).
-        assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
-                .isEqualTo(displayBounds.height());
 
         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
         assertFitted();
@@ -190,17 +180,9 @@
         assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
         // The notch is no longer on top.
         assertEquals(appBounds, mActivity.getBounds());
-        // Activity max bounds are sandboxed.
-        assertActivityMaxBoundsSandboxedForLetterbox();
 
         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
         assertFitted();
-        // Activity max bounds should be sandboxed; activity is letterboxed due to aspect ratio.
-        assertActivityMaxBoundsSandboxedForLetterbox();
-        // Activity max bounds ignore notch, since an app can be shown past the notch (although app
-        // is currently limited by the notch).
-        assertThat(mActivity.getWindowConfiguration().getMaxBounds().height())
-                .isEqualTo(displayBounds.height());
     }
 
     @Test
@@ -228,9 +210,6 @@
         assertEquals(originalBounds.width(), mActivity.getBounds().width());
         assertEquals(originalBounds.height(), mActivity.getBounds().height());
         assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
-        // Activity is sandboxed; it is in size compat mode since it is not resizable and has a
-        // max aspect ratio.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
         assertScaled();
     }
 
@@ -238,13 +217,11 @@
     public void testFixedScreenBoundsWhenDisplaySizeChanged() {
         setUpDisplaySizeWithApp(1000, 2500);
         prepareUnresizable(mActivity, -1f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
-        final DisplayContent display = mActivity.mDisplayContent;
         assertFitted();
-        // Activity and task inherit bounds from TaskDisplayArea, since not sandboxed.
-        assertMaxBoundsInheritDisplayAreaBounds();
 
         final Rect origBounds = new Rect(mActivity.getBounds());
         final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
+        final DisplayContent display = mActivity.mDisplayContent;
 
         // Change the size of current display.
         resizeDisplay(display, 1000, 2000);
@@ -261,8 +238,6 @@
         // The position of configuration bounds should be the same as compat bounds.
         assertEquals(mActivity.getBounds().left, currentBounds.left);
         assertEquals(mActivity.getBounds().top, currentBounds.top);
-        // Activity is sandboxed to the offset size compat bounds.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         // Change display size to a different orientation
         resizeDisplay(display, 2000, 1000);
@@ -271,8 +246,6 @@
         assertEquals(origBounds.height(), currentBounds.height());
         assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
         assertEquals(Configuration.ORIENTATION_PORTRAIT, mActivity.getConfiguration().orientation);
-        // Activity is sandboxed to the offset size compat bounds.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         // The previous resize operation doesn't consider the rotation change after size changed.
         // These setups apply the requested orientation to rotation as real case that the top fixed
@@ -292,8 +265,6 @@
         assertEquals(origBounds.height(), currentBounds.height());
         assertEquals(offsetX, currentBounds.left);
         assertScaled();
-        // Activity is sandboxed due to size compat mode.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
     }
 
     @Test
@@ -309,8 +280,6 @@
         assertEquals(bounds.width(), bounds.height() * maxAspect, 0.0001f /* delta */);
         // The position should be horizontal centered.
         assertEquals((displayWidth - bounds.width()) / 2, bounds.left);
-        // Activity max bounds should be sandboxed since it is letterboxed.
-        assertActivityMaxBoundsSandboxedForLetterbox();
 
         mActivity.mDisplayContent.setImeLayeringTarget(addWindowToActivity(mActivity));
         // Make sure IME cannot attach to the app, otherwise IME window will also be shifted.
@@ -322,8 +291,6 @@
         // It should keep non-attachable because the resolved bounds will be computed according to
         // the aspect ratio that won't match its parent bounds.
         assertFalse(mActivity.mDisplayContent.isImeAttachedToApp());
-        // Activity max bounds should be sandboxed since it is letterboxed.
-        assertActivityMaxBoundsSandboxedForLetterbox();
     }
 
     @Test
@@ -349,13 +316,14 @@
     }
 
     @Test
-    public void testMoveToDifferentOrientationDisplay() {
+    public void testMoveToDifferentOrientDisplay() {
         setUpDisplaySizeWithApp(1000, 2500);
         prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
         assertFitted();
 
-        final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
-        final Rect originalBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
+        final Rect configBounds = mActivity.getWindowConfiguration().getBounds();
+        final int origWidth = configBounds.width();
+        final int origHeight = configBounds.height();
 
         final int notchHeight = 100;
         final DisplayContent newDisplay = new TestDisplayContent.Builder(mAtm, 2000, 1000)
@@ -364,44 +332,37 @@
         // Move the non-resizable activity to the new display.
         mTask.reparent(newDisplay.getDefaultTaskDisplayArea(), true /* onTop */);
         // The configuration bounds [820, 0 - 1820, 2500] should keep the same.
-        assertEquals(originalBounds.width(), currentBounds.width());
-        assertEquals(originalBounds.height(), currentBounds.height());
+        assertEquals(origWidth, configBounds.width());
+        assertEquals(origHeight, configBounds.height());
         assertScaled();
-        // Activity max bounds are sandboxed due to size compat mode on the new display.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         final Rect newDisplayBounds = newDisplay.getWindowConfiguration().getBounds();
         // The scaled bounds should exclude notch area (1000 - 100 == 360 * 2500 / 1000 = 900).
         assertEquals(newDisplayBounds.height() - notchHeight,
-                (int) ((float) mActivity.getBounds().width() * originalBounds.height()
-                        / originalBounds.width()));
+                (int) ((float) mActivity.getBounds().width() * origHeight / origWidth));
 
         // Recompute the natural configuration in the new display.
         mActivity.clearSizeCompatMode();
         mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
         // Because the display cannot rotate, the portrait activity will fit the short side of
         // display with keeping portrait bounds [200, 0 - 700, 1000] in center.
-        assertEquals(newDisplayBounds.height(), currentBounds.height());
-        assertEquals(currentBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
-                currentBounds.width());
+        assertEquals(newDisplayBounds.height(), configBounds.height());
+        assertEquals(configBounds.height() * newDisplayBounds.height() / newDisplayBounds.width(),
+                configBounds.width());
         assertFitted();
         // The appBounds should be [200, 100 - 700, 1000].
         final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
-        assertEquals(currentBounds.width(), appBounds.width());
-        assertEquals(currentBounds.height() - notchHeight, appBounds.height());
-        // Task max bounds are sandboxed due to letterboxing from orientation mismatch with display.
-        assertTaskMaxBoundsSandboxed();
+        assertEquals(configBounds.width(), appBounds.width());
+        assertEquals(configBounds.height() - notchHeight, appBounds.height());
     }
 
     @Test
-    public void testFixedOrientationRotateCutoutDisplay() {
+    public void testFixedOrientRotateCutoutDisplay() {
         // Create a display with a notch/cutout
         final int notchHeight = 60;
-        final int width = 1000;
-        setUpApp(new TestDisplayContent.Builder(mAtm, width, 2500)
+        setUpApp(new TestDisplayContent.Builder(mAtm, 1000, 2500)
                 .setNotch(notchHeight).build());
-        // Bounds=[0, 0 - 1000, 1400], AppBounds=[0, 60 - 1000, 1460].
-        final float maxAspect = 1.4f;
+        // Bounds=[0, 0 - 1000, 1460], AppBounds=[0, 60 - 1000, 1460].
         prepareUnresizable(mActivity, 1.4f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
 
         final Rect currentBounds = mActivity.getWindowConfiguration().getBounds();
@@ -409,11 +370,6 @@
         final Rect origBounds = new Rect(currentBounds);
         final Rect origAppBounds = new Rect(appBounds);
 
-        // Activity is sandboxed, and bounds include the area consumed by the notch.
-        assertActivityMaxBoundsSandboxedForLetterbox();
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds().height())
-                .isEqualTo(Math.round(width * maxAspect) + notchHeight);
-
         // Although the activity is fixed orientation, force rotate the display.
         rotateDisplay(mActivity.mDisplayContent, ROTATION_270);
         assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation());
@@ -429,13 +385,10 @@
         // The position in configuration should be global coordinates.
         assertEquals(mActivity.getBounds().left, currentBounds.left);
         assertEquals(mActivity.getBounds().top, currentBounds.top);
-
-        // Activity max bounds are sandboxed due to size compat mode.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
     }
 
     @Test
-    public void testFixedAspectRatioOrientationChangeOrientation() {
+    public void testFixedAspOrientChangeOrient() {
         setUpDisplaySizeWithApp(1000, 2500);
 
         final float maxAspect = 1.4f;
@@ -447,8 +400,6 @@
         final Rect originalAppBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
 
         assertEquals((int) (originalBounds.width() * maxAspect), originalBounds.height());
-        // Activity is sandboxed due to fixed aspect ratio.
-        assertActivityMaxBoundsSandboxedForLetterbox();
 
         // Change the fixed orientation.
         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
@@ -460,8 +411,6 @@
                 mActivity.getWindowConfiguration().getAppBounds().height());
         assertEquals(originalAppBounds.height(),
                 mActivity.getWindowConfiguration().getAppBounds().width());
-        // Activity is sandboxed due to fixed aspect ratio.
-        assertActivityMaxBoundsSandboxedForLetterbox();
     }
 
     @Test
@@ -510,8 +459,6 @@
         // restarted and the override configuration won't be cleared.
         verify(mActivity, never()).restartProcessIfVisible();
         assertScaled();
-        // Activity max bounds are sandboxed due to size compat mode, even if is not visible.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         // Change display density
         display.mBaseDisplayDensity = (int) (0.7f * display.mBaseDisplayDensity);
@@ -586,16 +533,12 @@
         // in multi-window mode.
         mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
         assertFalse(activity.shouldUseSizeCompatMode());
-        // Activity and task should not be sandboxed.
-        assertMaxBoundsInheritDisplayAreaBounds();
 
         // The non-resizable activity should not be size compat because the display support
         // changing windowing mode from fullscreen to freeform.
         mTask.mDisplayContent.setDisplayWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
         mTask.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
         assertFalse(activity.shouldUseSizeCompatMode());
-        // Activity and task should not be sandboxed.
-        assertMaxBoundsInheritDisplayAreaBounds();
     }
 
     @Test
@@ -659,9 +602,6 @@
         // be transparent.
         assertFalse(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
 
-        // Activity is sandboxed.
-        assertActivityMaxBoundsSandboxedForLetterbox();
-
         // Make the activity fill the display.
         prepareUnresizable(mActivity, 10 /* maxAspect */, SCREEN_ORIENTATION_LANDSCAPE);
         w.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
@@ -671,7 +611,6 @@
         // The letterbox should only cover the notch area, so status bar can be transparent.
         assertEquals(new Rect(notchHeight, 0, 0, 0), mActivity.getLetterboxInsets());
         assertTrue(displayPolicy.isFullyTransparentAllowed(w, TYPE_STATUS_BAR));
-        assertActivityMaxBoundsSandboxedForLetterbox();
     }
 
     @Test
@@ -696,8 +635,6 @@
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
         assertEquals(taskBounds, activityBounds);
-        // Activity inherits max bounds from task, since sandboxing applied to task.
-        assertTaskMaxBoundsSandboxed();
 
         // Task bounds should be 700x1400 with the ratio as the display.
         assertEquals(displayBounds.height(), taskBounds.height());
@@ -728,8 +665,6 @@
         assertScaled();
         assertEquals(activityBounds.width(), newActivityBounds.width());
         assertEquals(activityBounds.height(), newActivityBounds.height());
-        // Activity max bounds are sandboxed due to size compat mode.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
     }
 
     @Test
@@ -741,30 +676,29 @@
         // Portrait fixed app without max aspect.
         prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT);
 
+        Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+        Rect activityBounds = new Rect(mActivity.getBounds());
+
         // App should launch in fullscreen.
         assertFalse(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
-        // Activity and task inherit max bounds from TaskDisplayArea.
-        assertMaxBoundsInheritDisplayAreaBounds();
+        assertEquals(displayBounds, activityBounds);
 
         // Rotate display to landscape.
         rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
 
-        final Rect rotatedDisplayBounds = new Rect(mActivity.mDisplayContent.getBounds());
-        final Rect rotatedActivityBounds = new Rect(mActivity.getBounds());
-        assertTrue(rotatedDisplayBounds.width() > rotatedDisplayBounds.height());
+        displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
+        activityBounds = new Rect(mActivity.getBounds());
+        assertTrue(displayBounds.width() > displayBounds.height());
 
         // App should be in size compat.
         assertFalse(mTask.isTaskLetterboxed());
         assertScaled();
-        assertThat(mActivity.inSizeCompatMode()).isTrue();
-        // Activity max bounds are sandboxed due to size compat mode.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         // App bounds should be 700x1400 with the ratio as the display.
-        assertEquals(rotatedDisplayBounds.height(), rotatedActivityBounds.height());
-        assertEquals(rotatedDisplayBounds.height() * rotatedDisplayBounds.height()
-                        / rotatedDisplayBounds.width(), rotatedActivityBounds.width());
+        assertEquals(displayBounds.height(), activityBounds.height());
+        assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+                activityBounds.width());
     }
 
     @Test
@@ -797,17 +731,14 @@
         final Rect displayBounds = new Rect(display.getBounds());
         final Rect taskBounds = new Rect(mTask.getBounds());
         final Rect newActivityBounds = new Rect(newActivity.getBounds());
-        final float displayAspectRatio = (float) displayBounds.height() / displayBounds.width();
 
         // Task and app bounds should be 700x1400 with the ratio as the display.
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(newActivity.inSizeCompatMode());
         assertEquals(taskBounds, newActivityBounds);
         assertEquals(displayBounds.height(), taskBounds.height());
-        assertThat(taskBounds.width())
-                .isEqualTo(Math.round(displayBounds.height() * displayAspectRatio));
-        // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
-        assertTaskMaxBoundsSandboxed();
+        assertEquals(displayBounds.height() * displayBounds.height() / displayBounds.width(),
+                taskBounds.width());
     }
 
     @Test
@@ -847,14 +778,6 @@
         assertEquals(displayBounds.height(), taskBounds.height());
         assertEquals((long) Math.rint(taskBounds.height() / newActivity.info.maxAspectRatio),
                 taskBounds.width());
-        // New activity max bounds are sandboxed due to letterbox.
-        assertThat(newActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(taskBounds);
-        // Task max bounds are sandboxed due to letterbox, with the ratio of the display.
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().height())
-                .isEqualTo(displayBounds.height());
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds().width())
-                .isEqualTo(Math.round(displayBounds.height() / newActivity.info.maxAspectRatio));
 
         // App bounds should be fullscreen in Task bounds.
         assertFalse(newActivity.inSizeCompatMode());
@@ -883,9 +806,6 @@
         assertFalse(mTask.isTaskLetterboxed());
         assertScaled();
         assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
-        assertThat(mActivity.inSizeCompatMode()).isTrue();
-        // Activity max bounds are sandboxed due to size compat mode.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         final Rect activityBounds = new Rect(mActivity.getBounds());
         mTask.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
@@ -896,8 +816,6 @@
         assertScaled();
         assertEquals(mTask.getLastTaskBoundsComputeActivity(), mActivity);
         assertEquals(activityBounds, mActivity.getBounds());
-        // Activity max bounds are sandboxed due to size compat.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
     }
 
     @Test
@@ -913,7 +831,6 @@
         // In Task letterbox
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
-        assertTaskMaxBoundsSandboxed();
 
         // Rotate display to portrait.
         rotateDisplay(display, ROTATION_90);
@@ -921,7 +838,6 @@
         // App should be in size compat.
         assertFalse(mTask.isTaskLetterboxed());
         assertScaled();
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
         // Rotate display to landscape.
         rotateDisplay(display, ROTATION_180);
@@ -929,7 +845,6 @@
         // In Task letterbox
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
-        assertTaskMaxBoundsSandboxed();
     }
 
     @Test
@@ -947,26 +862,20 @@
         // In Task letterbox
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
-        // Task is letterboxed due to mismatched orientation request.
-        assertTaskMaxBoundsSandboxed();
 
-        // Rotate display to landscape.
+        // Rotate display to portrait.
         rotateDisplay(display, ROTATION_90);
 
         // App should be in size compat.
         assertFalse(mTask.isTaskLetterboxed());
         assertScaled();
-        // Activity max bounds are sandboxed due to unresizable app.
-        assertActivityMaxBoundsSandboxedForSizeCompat();
 
-        // Rotate display to portrait.
+        // Rotate display to landscape.
         rotateDisplay(display, ROTATION_180);
 
         // In Task letterbox
         assertTrue(mTask.isTaskLetterboxed());
         assertFalse(mActivity.inSizeCompatMode());
-        // Task is letterboxed, as in first case.
-        assertTaskMaxBoundsSandboxed();
     }
 
     @Test
@@ -983,18 +892,12 @@
         assertEquals(ORIENTATION_LANDSCAPE, display.getConfiguration().orientation);
         assertEquals(2800, displayBounds.width());
         assertEquals(1400, displayBounds.height());
-        Rect displayAreaBounds = new Rect(0, 0, 2400, 1000);
-        taskDisplayArea.setBounds(displayAreaBounds);
+        taskDisplayArea.setBounds(0, 0, 2400, 1000);
 
         final Rect activityBounds = new Rect(mActivity.getBounds());
         assertFalse(mActivity.inSizeCompatMode());
         assertEquals(2400, activityBounds.width());
         assertEquals(1000, activityBounds.height());
-        // Task and activity maximum bounds inherit from TaskDisplayArea bounds.
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(displayAreaBounds);
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(displayAreaBounds);
     }
 
     @Test
@@ -1142,48 +1045,6 @@
         assertFalse(mActivity.hasSizeCompatBounds());
     }
 
-    /** Asserts both the activity and task max bounds inherit from the TaskDisplayArea. */
-    private void assertMaxBoundsInheritDisplayAreaBounds() {
-        final Rect taskDisplayAreaBounds = mTask.getDisplayArea().getBounds();
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(taskDisplayAreaBounds);
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(taskDisplayAreaBounds);
-    }
-
-    /**
-     * Asserts task-level letterboxing, so both activity and task max bounds
-     * are sandboxed to the letterbox bounds.
-     */
-    private void assertTaskMaxBoundsSandboxed() {
-        // Activity inherits max bounds from task, since sandboxing applied to task.
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mTask.getBounds());
-        // Task max bounds are sandboxed due to letterbox.
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mTask.getBounds());
-    }
-
-    /** Asserts activity-level size compat mode, so only activity max bounds are sandboxed. */
-    private void assertActivityMaxBoundsSandboxedForSizeCompat() {
-        // Activity max bounds are sandboxed due to size compat mode.
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mActivity.getWindowConfiguration().getBounds());
-        // Task inherits max bounds from display.
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mTask.getDisplayContent().getBounds());
-    }
-
-    /** Asserts activity-level letterboxing, so only activity max bounds are sandboxed. */
-    private void assertActivityMaxBoundsSandboxedForLetterbox() {
-        // Activity is sandboxed due to fixed aspect ratio.
-        assertThat(mActivity.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mActivity.getBounds());
-        // Task inherits bounds from display.
-        assertThat(mTask.getConfiguration().windowConfiguration.getMaxBounds())
-                .isEqualTo(mTask.getDisplayContent().getBounds());
-    }
-
     static Configuration rotateDisplay(DisplayContent display, int rotation) {
         final Configuration c = new Configuration();
         display.getDisplayRotation().setRotation(rotation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index f20513d..0eb8c8d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -1004,6 +1004,26 @@
     }
 
     @Test
+    public void testNotSaveLaunchingStateForNonLeafTask() {
+        LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister;
+        spyOn(persister);
+
+        final Task task = getTestTask();
+        task.setHasBeenVisible(false);
+        task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
+        task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+        final Task leafTask = createTaskInStack(task, 0 /* userId */);
+
+        leafTask.setHasBeenVisible(true);
+        task.setHasBeenVisible(true);
+        task.onConfigurationChanged(task.getParent().getConfiguration());
+
+        verify(persister, never()).saveTask(same(task), any());
+        verify(persister).saveTask(same(leafTask), any());
+    }
+
+    @Test
     public void testNotSpecifyOrientationByFloatingTask() {
         final Task task = new TaskBuilder(mSupervisor)
                 .setCreateActivity(true).setCreateParentTask(true).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index d71993d..ae85ceb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -136,11 +136,6 @@
             final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                     mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
             final TestDisplayContent newDisplay = createInternal(display);
-            // Ensure letterbox aspect ratio is not overridden on any device target.
-            // {@link com.android.internal.R.dimen.config_taskLetterboxAspectRatio}, provided by
-            // the below method, is set on some device form factors.
-            mService.mWindowManager.setTaskLetterboxAspectRatio(0);
-
             // disable the normal system decorations
             final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy();
             spyOn(displayPolicy);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index df5b48a..99c96bd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -66,6 +66,7 @@
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
+import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
 
@@ -905,8 +906,10 @@
         final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
                 new IRemoteAnimationRunner.Stub() {
                     @Override
-                    public void onAnimationStart(RemoteAnimationTarget[] apps,
+                    public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+                            RemoteAnimationTarget[] apps,
                             RemoteAnimationTarget[] wallpapers,
+                            RemoteAnimationTarget[] nonApps,
                             IRemoteAnimationFinishedCallback finishedCallback) {
                         try {
                             finishedCallback.onAnimationFinished();
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 7cc233b..cac7b82 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -84,7 +84,6 @@
     static_libs: [
         "libviewcompiler",
     ],
-    test_suites: ["general-tests"],
 }
 
 cc_binary_host {
diff --git a/startop/view_compiler/TEST_MAPPING b/startop/view_compiler/TEST_MAPPING
index 5f7d3f9..791e471 100644
--- a/startop/view_compiler/TEST_MAPPING
+++ b/startop/view_compiler/TEST_MAPPING
@@ -10,10 +10,6 @@
           "include-filter": "android.view.cts.PrecompiledLayoutTest"
         }
       ]
-    },
-    {
-      "name": "view-compiler-tests",
-      "host": true
     }
   ]
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7215cd5..ac584c1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4038,6 +4038,20 @@
         public static final String KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL =
                 KEY_PREFIX + "enable_presence_group_subscribe_bool";
 
+        /**
+         * An integer key associated with the period of time in seconds the non-rcs capability
+         * information of each contact is cached on the device.
+         * <p>
+         * The rcs capability cache expiration sec is managed by
+         * {@code android.telephony.ims.ProvisioningManager} but non-rcs capability is managed by
+         * {@link CarrierConfigManager} since non-rcs capability will be provided via ACS or carrier
+         * config.
+         * <p>
+         * The default value is 2592000 secs (30 days), see RCC.07 Annex A.1.9.
+         */
+        public static final String KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT =
+                KEY_PREFIX + "non_rcs_capabilities_cache_expiration_sec_int";
+
         private Ims() {}
 
         private static PersistableBundle getDefaults() {
@@ -4048,6 +4062,7 @@
             defaults.putBoolean(KEY_ENABLE_PRESENCE_CAPABILITY_EXCHANGE_BOOL, false);
             defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false);
             defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true);
+            defaults.putInt(KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT, 30 * 24 * 60 * 60);
             return defaults;
         }
     }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2734ad1..1473b7a 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -47,6 +47,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ParcelUuid;
@@ -150,6 +151,22 @@
     public static final String CACHE_KEY_SLOT_INDEX_PROPERTY =
             "cache_key.telephony.get_slot_index";
 
+    /** @hide */
+    public static final String GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME = "getSimSpecificSettings";
+
+    /** @hide */
+    public static final String RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME =
+            "restoreSimSpecificSettings";
+
+    /**
+     * Key to the backup & restore data byte array in the Bundle that is returned by {@link
+     * #getAllSimSpecificSettingsForBackup()} or to be pass in to {@link
+     * #restoreAllSimSpecificSettings()}.
+     *
+     * @hide
+     */
+    public static final String KEY_SIM_SPECIFIC_SETTINGS_DATA = "KEY_SIM_SPECIFIC_SETTINGS_DATA";
+
     private static final int MAX_CACHE_SIZE = 4;
 
     private static class VoidPropertyInvalidatedCache<T>
@@ -372,6 +389,28 @@
     public static final Uri WFC_ROAMING_ENABLED_CONTENT_URI = Uri.withAppendedPath(
             CONTENT_URI, "wfc_roaming_enabled");
 
+
+    /**
+     * A content {@link uri} used to call the appropriate backup or restore method for sim-specific
+     * settings
+     * <p>
+     * See {@link #GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME} and {@link
+     * #RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME} for information on what method to call.
+     * @hide
+     */
+    @NonNull
+    public static final Uri SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI = Uri.withAppendedPath(
+            CONTENT_URI, "backup_and_restore");
+
+    /**
+     * A content {@link uri} used to notify contentobservers listening to siminfo restore during
+     * SuW.
+     * @hide
+     */
+    @NonNull
+    public static final Uri SIM_INFO_SUW_RESTORE_CONTENT_URI = Uri.withAppendedPath(
+            SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI, "suw_restore");
+
     /**
      * A content {@link Uri} used to receive updates on cross sim enabled user setting.
      * <p>
@@ -3455,4 +3494,71 @@
         sSlotIndexCache.clear();
         sPhoneIdCache.clear();
     }
+
+    /**
+     * Called to retrieve SIM-specific settings data to be backed up.
+     *
+     * @return data in byte[] to be backed up.
+     *
+     * @hide
+     */
+    @NonNull
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public byte[] getAllSimSpecificSettingsForBackup() {
+        Bundle bundle =  mContext.getContentResolver().call(
+                SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+                GET_SIM_SPECIFIC_SETTINGS_METHOD_NAME, null, null);
+        return bundle.getByteArray(SubscriptionManager.KEY_SIM_SPECIFIC_SETTINGS_DATA);
+    }
+
+    /**
+     * Called to attempt to restore the backed up sim-specific configs to device for specific sim.
+     * This will try to restore the data that was stored internally when {@link
+     * #restoreAllSimSpecificSettingsFromBackup(byte[] data)} was called during setup wizard.
+     * End result is SimInfoDB is modified to match any backed up configs for the requested
+     * inserted sim.
+     *
+     * <p>
+     * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB
+     * entry is updated as the result of this method call.
+     *
+     * @param iccId of the sim that a restore is requested for.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void restoreSimSpecificSettingsForIccIdFromBackup(@NonNull String iccId) {
+        mContext.getContentResolver().call(
+                SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+                RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+                iccId, null);
+    }
+
+    /**
+     * Called during setup wizard restore flow to attempt to restore the backed up sim-specific
+     * configs to device for all existing SIMs in SimInfoDB. Internally, it will store the backup
+     * data in an internal file. This file will persist on device for device's lifetime and will be
+     * used later on when a SIM is inserted to restore that specific SIM's settings by calling
+     * {@link #restoreSimSpecificSettingsForIccIdFromBackup(String iccId)}. End result is
+     * SimInfoDB is modified to match any backed up configs for the appropriate inserted SIMs.
+     *
+     * <p>
+     * The {@link Uri} {@link #SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI} is notified if any SimInfoDB
+     * entry is updated as the result of this method call.
+     *
+     * @param data with the sim specific configs to be backed up.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void restoreAllSimSpecificSettingsFromBackup(@NonNull byte[] data) {
+        Bundle bundle = new Bundle();
+        bundle.putByteArray(KEY_SIM_SPECIFIC_SETTINGS_DATA, data);
+        mContext.getContentResolver().call(
+                SIM_INFO_BACKUP_AND_RESTORE_CONTENT_URI,
+                RESTORE_SIM_SPECIFIC_SETTINGS_METHOD_NAME,
+                null, bundle);
+    }
 }
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
index 2e35d27..5f8e93d 100644
--- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -44,8 +44,12 @@
         @Override
         public void setListener(IImsEcbmListener listener) {
             synchronized (mLock) {
-                if (mImsEcbm != null && listener != null && Objects.equals(
-                        mImsEcbm.asBinder(), listener.asBinder())) {
+                if (mListener != null && !mListener.asBinder().isBinderAlive()) {
+                    Log.w(TAG, "setListener: discarding dead Binder");
+                    mListener = null;
+                }
+                if (mListener != null && listener != null && Objects.equals(
+                        mListener.asBinder(), listener.asBinder())) {
                     return;
                 }
                 if (listener == null) {
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
index 555a47e..8e961ac 100644
--- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -48,6 +48,10 @@
         @Override
         public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
             synchronized (mLock) {
+                if (mListener != null && !mListener.asBinder().isBinderAlive()) {
+                    Log.w(TAG, "setListener: discarding dead Binder");
+                    mListener = null;
+                }
                 if (mListener != null && listener != null && Objects.equals(
                         mListener.asBinder(), listener.asBinder())) {
                     return;
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index eef4fca..83b89aa 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -23,6 +23,7 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.telephony.ims.ImsUtListener;
+import android.util.Log;
 
 import com.android.ims.internal.IImsUt;
 import com.android.ims.internal.IImsUtListener;
@@ -41,6 +42,7 @@
 // will break other implementations of ImsUt maintained by other ImsServices.
 @SystemApi
 public class ImsUtImplBase {
+    private static final String TAG = "ImsUtImplBase";
     /**
      * Bar all incoming calls. (See 3GPP TS 24.611)
      * @hide
@@ -207,6 +209,11 @@
         @Override
         public void setListener(IImsUtListener listener) throws RemoteException {
             synchronized (mLock) {
+                if (mUtListener != null
+                        && !mUtListener.getListenerInterface().asBinder().isBinderAlive()) {
+                    Log.w(TAG, "setListener: discarding dead Binder");
+                    mUtListener = null;
+                }
                 if (mUtListener != null && listener != null && Objects.equals(
                         mUtListener.getListenerInterface().asBinder(), listener.asBinder())) {
                     return;
diff --git a/tests/UpdatableSystemFontTest/TEST_MAPPING b/tests/UpdatableSystemFontTest/TEST_MAPPING
new file mode 100644
index 0000000..a5c4479
--- /dev/null
+++ b/tests/UpdatableSystemFontTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit": [
+    {
+      "name": "UpdatableSystemFontTest"
+    }
+  ]
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c7554f6..0674138 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1262,22 +1262,28 @@
         }
     }
 
-    private void updateUidNetworkingBlocked() {
-        doAnswer(i -> NetworkPolicyManagerInternal.isUidNetworkingBlocked(
-                i.getArgument(0) /* uid */, mUidRules, i.getArgument(1) /* metered */,
-                mRestrictBackground)
+    private void mockUidNetworkingBlocked() {
+        doAnswer(i -> mContext.getSystemService(NetworkPolicyManager.class)
+                .checkUidNetworkingBlocked(i.getArgument(0) /* uid */, mUidRules,
+                        i.getArgument(1) /* metered */, mRestrictBackground)
         ).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean());
+
+        doAnswer(inv -> mContext.getSystemService(NetworkPolicyManager.class)
+                .checkUidNetworkingBlocked(inv.getArgument(0) /* uid */,
+                        inv.getArgument(1) /* uidRules */,
+                        inv.getArgument(2) /* isNetworkMetered */,
+                        inv.getArgument(3) /* isBackgroundRestricted */)
+        ).when(mNetworkPolicyManager).checkUidNetworkingBlocked(
+                anyInt(), anyInt(), anyBoolean(), anyBoolean());
     }
 
     private void setUidRulesChanged(int uidRules) throws RemoteException {
         mUidRules = uidRules;
-        updateUidNetworkingBlocked();
         mPolicyListener.onUidRulesChanged(Process.myUid(), mUidRules);
     }
 
     private void setRestrictBackgroundChanged(boolean restrictBackground) throws RemoteException {
         mRestrictBackground = restrictBackground;
-        updateUidNetworkingBlocked();
         mPolicyListener.onRestrictBackgroundChanged(mRestrictBackground);
     }
 
@@ -6809,6 +6815,7 @@
                 .addTransportType(TRANSPORT_CELLULAR)
                 .build();
         mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+        mockUidNetworkingBlocked();
 
         mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
         mCellNetworkAgent.connect(true);
@@ -6891,6 +6898,7 @@
     public void testNetworkBlockedStatusBeforeAndAfterConnect() throws Exception {
         final TestNetworkCallback defaultCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(defaultCallback);
+        mockUidNetworkingBlocked();
 
         // No Networkcallbacks invoked before any network is active.
         setUidRulesChanged(RULE_REJECT_ALL);
@@ -7160,6 +7168,13 @@
         when(mKeyStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
     }
 
+    private void establishLegacyLockdownVpn() throws Exception {
+        // The legacy lockdown VPN only supports userId 0.
+        final Set<UidRange> ranges = Collections.singleton(UidRange.createForUser(PRIMARY_USER));
+        mMockVpn.registerAgent(ranges);
+        mMockVpn.connect(true);
+    }
+
     @Test
     public void testLegacyLockdownVpn() throws Exception {
         mServiceContext.setPermission(
@@ -7254,22 +7269,30 @@
         mMockVpn.expectStartLegacyVpnRunner();
         b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
         ExpectedBroadcast b2 = expectConnectivityAction(TYPE_MOBILE, DetailedState.CONNECTED);
-        mMockVpn.establishForMyUid();
+        establishLegacyLockdownVpn();
         callback.expectAvailableThenValidatedCallbacks(mMockVpn);
         defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+        NetworkCapabilities vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
         b1.expectBroadcast();
         b2.expectBroadcast();
         assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+        assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
+        assertTrue(vpnNc.hasTransport(TRANSPORT_CELLULAR));
+        assertFalse(vpnNc.hasTransport(TRANSPORT_WIFI));
+        assertFalse(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
 
         // Switch default network from cell to wifi. Expect VPN to disconnect and reconnect.
         final LinkProperties wifiLp = new LinkProperties();
         wifiLp.setInterfaceName("wlan0");
         wifiLp.addLinkAddress(new LinkAddress("192.0.2.163/25"));
         wifiLp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"), null, "wlan0"));
-        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
+        final NetworkCapabilities wifiNc = new NetworkCapabilities();
+        wifiNc.addTransportType(TRANSPORT_WIFI);
+        wifiNc.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp, wifiNc);
 
         b1 = expectConnectivityAction(TYPE_MOBILE, DetailedState.DISCONNECTED);
         // Wifi is CONNECTING because the VPN isn't up yet.
@@ -7302,16 +7325,20 @@
         // The VPN comes up again on wifi.
         b1 = expectConnectivityAction(TYPE_VPN, DetailedState.CONNECTED);
         b2 = expectConnectivityAction(TYPE_WIFI, DetailedState.CONNECTED);
-        mMockVpn.establishForMyUid();
+        establishLegacyLockdownVpn();
         callback.expectAvailableThenValidatedCallbacks(mMockVpn);
         defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
         b1.expectBroadcast();
         b2.expectBroadcast();
-
         assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
+        vpnNc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
+        assertTrue(vpnNc.hasTransport(TRANSPORT_VPN));
+        assertTrue(vpnNc.hasTransport(TRANSPORT_WIFI));
+        assertFalse(vpnNc.hasTransport(TRANSPORT_CELLULAR));
+        assertTrue(vpnNc.hasCapability(NET_CAPABILITY_NOT_METERED));
 
         // Disconnect cell. Nothing much happens since it's not the default network.
         // Whenever LockdownVpnTracker is connected, it will send a connected broadcast any time any
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index f478282..32c6a75 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -49,6 +49,7 @@
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
 import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -119,6 +120,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
@@ -213,6 +215,8 @@
 
         when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG);
         when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG);
+        when(mContext.getSystemServiceName(UserManager.class))
+                .thenReturn(Context.USER_SERVICE);
         when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
         when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
         when(mContext.getSystemServiceName(NotificationManager.class))
@@ -253,12 +257,14 @@
 
     @Test
     public void testRestrictedProfilesAreAddedToVpn() {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
 
         final Vpn vpn = createVpn(primaryUser.id);
-        final Set<UidRange> ranges = vpn.createUserAndRestrictedProfilesRanges(primaryUser.id,
-                null, null);
+
+        // Assume the user can have restricted profiles.
+        doReturn(true).when(mUserManager).canHaveRestrictedProfile();
+        final Set<UidRange> ranges =
+                vpn.createUserAndRestrictedProfilesRanges(primaryUser.id, null, null);
 
         assertEquals(new ArraySet<>(Arrays.asList(new UidRange[] {
                 PRI_USER_RANGE, UidRange.createForUser(restrictedProfileA.id)
@@ -267,7 +273,6 @@
 
     @Test
     public void testManagedProfilesAreNotAddedToVpn() {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         setMockedUsers(primaryUser, managedProfileA);
 
         final Vpn vpn = createVpn(primaryUser.id);
@@ -290,7 +295,6 @@
 
     @Test
     public void testUidAllowAndDenylist() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRange user = PRI_USER_RANGE;
         final String[] packages = {PKGS[0], PKGS[1], PKGS[2]};
@@ -316,7 +320,6 @@
 
     @Test
     public void testGetAlwaysAndOnGetLockDown() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
 
         // Default state.
@@ -341,7 +344,6 @@
 
     @Test
     public void testLockdownChangingPackage() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRange user = PRI_USER_RANGE;
 
@@ -369,7 +371,6 @@
 
     @Test
     public void testLockdownAllowlist() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRange user = PRI_USER_RANGE;
 
@@ -444,7 +445,6 @@
 
     @Test
     public void testLockdownRuleRepeatability() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] {
                 new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)};
@@ -477,7 +477,6 @@
 
     @Test
     public void testLockdownRuleReversibility() throws Exception {
-        if (true) return; // TODO(b/175883995): Test disabled until updated for new UserManager API.
         final Vpn vpn = createVpn(primaryUser.id);
         final UidRangeParcel[] entireUser = {
             new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)
@@ -954,7 +953,14 @@
     }
 
     private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception {
-        setMockedUsers(primaryUser);
+        // TODO(b/175883995): once these tests have been updated for the changes to the UserManager
+        // API, remove this ad-hoc setup code and use setMockedUsers(primaryUser) again.
+        // setMockedUsers(primaryUser);
+        final ArrayList<UserInfo> users = new ArrayList<>();
+        users.add(primaryUser);
+        when(mUserManager.getAliveUsers()).thenReturn(users);
+        when(mUserManager.getUserInfo(primaryUser.id)).thenReturn(primaryUser);
+        when(mUserManager.canHaveRestrictedProfile()).thenReturn(false);
 
         // Dummy egress interface
         final LinkProperties lp = new LinkProperties();
@@ -997,14 +1003,12 @@
         profile.ipsecIdentifier = "id";
         profile.ipsecSecret = "secret";
         profile.l2tpSecret = "l2tpsecret";
+
         when(mConnectivityManager.getAllNetworks())
             .thenReturn(new Network[] { new Network(101) });
+
         when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(),
-                anyInt(), any(), anyInt())).thenAnswer(invocation -> {
-                    // The runner has registered an agent and is now ready.
-                    legacyRunnerReady.open();
-                    return new Network(102);
-                });
+                anyInt(), any(), anyInt())).thenReturn(new Network(102));
         final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile);
         final TestDeps deps = (TestDeps) vpn.mDeps;
         try {
@@ -1020,14 +1024,20 @@
                             "linkname", "vpn", "refuse-eap", "nodefaultroute", "usepeerdns",
                             "idle", "1800", "mtu", "1270", "mru", "1270" },
                     deps.mtpdArgs.get(10, TimeUnit.SECONDS));
+
             // Now wait for the runner to be ready before testing for the route.
-            legacyRunnerReady.block(10_000);
-            // In this test the expected address is always v4 so /32
+            ArgumentCaptor<LinkProperties> lpCaptor = ArgumentCaptor.forClass(LinkProperties.class);
+            verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(),
+                    lpCaptor.capture(), any(), anyInt(), any(), anyInt());
+
+            // In this test the expected address is always v4 so /32.
+            // Note that the interface needs to be specified because RouteInfo objects stored in
+            // LinkProperties objects always acquire the LinkProperties' interface.
             final RouteInfo expectedRoute = new RouteInfo(new IpPrefix(expectedAddr + "/32"),
-                    RouteInfo.RTN_THROW);
-            assertTrue("Routes lack the expected throw route (" + expectedRoute + ") : "
-                    + vpn.mConfig.routes,
-                    vpn.mConfig.routes.contains(expectedRoute));
+                    null, EGRESS_IFACE, RouteInfo.RTN_THROW);
+            final List<RouteInfo> actualRoutes = lpCaptor.getValue().getRoutes();
+            assertTrue("Expected throw route (" + expectedRoute + ") not found in " + actualRoutes,
+                    actualRoutes.contains(expectedRoute));
         } finally {
             // Now interrupt the thread, unblock the runner and clean up.
             vpn.mVpnRunner.exitVpnRunner();
@@ -1083,6 +1093,11 @@
         }
 
         @Override
+        public PendingIntent getIntentForStatusPanel(Context context) {
+            return null;
+        }
+
+        @Override
         public void sendArgumentsToDaemon(
                 final String daemon, final LocalSocket socket, final String[] arguments,
                 final Vpn.RetryScheduler interruptChecker) throws IOException {
@@ -1144,6 +1159,10 @@
         doReturn(UserHandle.of(userId)).when(asUserContext).getUser();
         when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt()))
                 .thenReturn(asUserContext);
+        when(asUserContext.getSystemServiceName(UserManager.class))
+                .thenReturn(Context.USER_SERVICE);
+        when(asUserContext.getSystemService(UserManager.class))
+                .thenReturn(mUserManager);
         final TestLooper testLooper = new TestLooper();
         final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService,
                 mNetd, userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
@@ -1179,11 +1198,6 @@
             final int id = (int) invocation.getArguments()[0];
             return userMap.get(id);
         }).when(mUserManager).getUserInfo(anyInt());
-
-        doAnswer(invocation -> {
-            final int id = (int) invocation.getArguments()[0];
-            return (userMap.get(id).flags & UserInfo.FLAG_ADMIN) != 0;
-        }).when(mUserManager).canHaveRestrictedProfile();
     }
 
     /**
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index a7d0860..a07bce3 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -46,6 +46,8 @@
             "android.view.InsetsSourceTest",
             "android.view.InsetsSourceConsumerTest",
             "android.view.InsetsStateTest",
+            "android.view.RoundedCornerTest",
+            "android.view.RoundedCornersTest",
             "android.view.WindowMetricsTest",
             "android.view.PendingInsetsControllerTest",
             "android.app.WindowContextTest",
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 86a1591..3e659d0 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -59,12 +59,17 @@
 
     // Public for use in VcnGatewayConnectionTest
     public static VcnGatewayConnectionConfig buildTestConfig() {
+        return buildTestConfigWithExposedCaps(EXPOSED_CAPS);
+    }
+
+    // Public for use in VcnGatewayConnectionTest
+    public static VcnGatewayConnectionConfig buildTestConfigWithExposedCaps(int... exposedCaps) {
         final VcnGatewayConnectionConfig.Builder builder =
                 new VcnGatewayConnectionConfig.Builder()
                         .setRetryInterval(RETRY_INTERVALS_MS)
                         .setMaxMtu(MAX_MTU);
 
-        for (int caps : EXPOSED_CAPS) {
+        for (int caps : exposedCaps) {
             builder.addExposedCapability(caps);
         }
 
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index e32e1e8..4859644 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -66,6 +66,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
 import com.android.server.vcn.TelephonySubscriptionTracker;
 import com.android.server.vcn.Vcn;
 import com.android.server.vcn.VcnContext;
@@ -142,6 +143,9 @@
     private final TelephonySubscriptionTracker mSubscriptionTracker =
             mock(TelephonySubscriptionTracker.class);
 
+    private final ArgumentCaptor<VcnSafemodeCallback> mSafemodeCallbackCaptor =
+            ArgumentCaptor.forClass(VcnSafemodeCallback.class);
+
     private final VcnManagementService mVcnMgmtSvc;
 
     private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener =
@@ -184,7 +188,7 @@
         doAnswer((invocation) -> {
             // Mock-within a doAnswer is safe, because it doesn't actually run nested.
             return mock(Vcn.class);
-        }).when(mMockDeps).newVcn(any(), any(), any(), any());
+        }).when(mMockDeps).newVcn(any(), any(), any(), any(), any());
 
         final PersistableBundle bundle =
                 PersistableBundleUtils.fromMap(
@@ -307,7 +311,7 @@
         TelephonySubscriptionSnapshot snapshot =
                 triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
         verify(mMockDeps)
-                .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot));
+                .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any());
     }
 
     @Test
@@ -485,7 +489,8 @@
                         eq(mVcnContext),
                         eq(TEST_UUID_2),
                         eq(TEST_VCN_CONFIG),
-                        eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT));
+                        eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT),
+                        any());
 
         // Verify Vcn is updated if it was previously started
         mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
@@ -634,4 +639,25 @@
 
         verify(mMockPolicyListener).onPolicyChanged();
     }
+
+    @Test
+    public void testVcnSafemodeCallbackOnEnteredSafemode() throws Exception {
+        TelephonySubscriptionSnapshot snapshot =
+                triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
+        verify(mMockDeps)
+                .newVcn(
+                        eq(mVcnContext),
+                        eq(TEST_UUID_1),
+                        eq(TEST_VCN_CONFIG),
+                        eq(snapshot),
+                        mSafemodeCallbackCaptor.capture());
+
+        mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+        VcnSafemodeCallback safemodeCallback = mSafemodeCallbackCaptor.getValue();
+        safemodeCallback.onEnteredSafemode();
+
+        assertFalse(mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1).isActive());
+        verify(mMockPolicyListener).onPolicyChanged();
+    }
 }
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
index fbaae6f..8643d8a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -45,7 +45,12 @@
     public void testEnterWhileNotRunningTriggersQuit() throws Exception {
         final VcnGatewayConnection vgc =
                 new VcnGatewayConnection(
-                        mVcnContext, TEST_SUB_GRP, TEST_SUBSCRIPTION_SNAPSHOT, mConfig, mDeps);
+                        mVcnContext,
+                        TEST_SUB_GRP,
+                        TEST_SUBSCRIPTION_SNAPSHOT,
+                        mConfig,
+                        mGatewayStatusCallback,
+                        mDeps);
 
         vgc.setIsRunning(false);
         vgc.transitionTo(vgc.mDisconnectedState);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index df1341c..333b5b9 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -43,6 +43,7 @@
 
 import com.android.server.IpSecService;
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
 
 import org.junit.Before;
 import org.mockito.ArgumentCaptor;
@@ -80,6 +81,7 @@
     @NonNull protected final VcnNetworkProvider mVcnNetworkProvider;
     @NonNull protected final VcnContext mVcnContext;
     @NonNull protected final VcnGatewayConnectionConfig mConfig;
+    @NonNull protected final VcnGatewayStatusCallback mGatewayStatusCallback;
     @NonNull protected final VcnGatewayConnection.Dependencies mDeps;
     @NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
 
@@ -94,6 +96,7 @@
         mVcnNetworkProvider = mock(VcnNetworkProvider.class);
         mVcnContext = mock(VcnContext.class);
         mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
+        mGatewayStatusCallback = mock(VcnGatewayStatusCallback.class);
         mDeps = mock(VcnGatewayConnection.Dependencies.class);
         mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
 
@@ -123,7 +126,12 @@
 
         mGatewayConnection =
                 new VcnGatewayConnection(
-                        mVcnContext, TEST_SUB_GRP, TEST_SUBSCRIPTION_SNAPSHOT, mConfig, mDeps);
+                        mVcnContext,
+                        TEST_SUB_GRP,
+                        TEST_SUBSCRIPTION_SNAPSHOT,
+                        mConfig,
+                        mGatewayStatusCallback,
+                        mDeps);
     }
 
     protected IpSecTransform makeDummyIpSecTransform() throws Exception {
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index 0c1df76..66cbf84 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -16,22 +16,27 @@
 
 package com.android.server.vcn;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
 import android.net.NetworkRequest;
 import android.net.vcn.VcnConfig;
+import android.net.vcn.VcnGatewayConnectionConfig;
 import android.net.vcn.VcnGatewayConnectionConfigTest;
 import android.os.ParcelUuid;
 import android.os.test.TestLooper;
 
+import com.android.server.VcnManagementService.VcnSafemodeCallback;
 import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
 import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
 
 import org.junit.Before;
@@ -51,9 +56,13 @@
     private VcnContext mVcnContext;
     private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
     private VcnNetworkProvider mVcnNetworkProvider;
+    private VcnSafemodeCallback mVcnSafemodeCallback;
     private Vcn.Dependencies mDeps;
 
+    private ArgumentCaptor<VcnGatewayStatusCallback> mGatewayStatusCallbackCaptor;
+
     private TestLooper mTestLooper;
+    private VcnGatewayConnectionConfig mGatewayConnectionConfig;
     private VcnConfig mConfig;
     private Vcn mVcn;
 
@@ -63,6 +72,7 @@
         mVcnContext = mock(VcnContext.class);
         mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
         mVcnNetworkProvider = mock(VcnNetworkProvider.class);
+        mVcnSafemodeCallback = mock(VcnSafemodeCallback.class);
         mDeps = mock(Vcn.Dependencies.class);
 
         mTestLooper = new TestLooper();
@@ -76,15 +86,26 @@
         doAnswer((invocation) -> {
             // Mock-within a doAnswer is safe, because it doesn't actually run nested.
             return mock(VcnGatewayConnection.class);
-        }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any());
+        }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any(), any());
 
-        mConfig =
-                new VcnConfig.Builder(mContext)
-                        .addGatewayConnectionConfig(
-                                VcnGatewayConnectionConfigTest.buildTestConfig())
-                        .build();
+        mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class);
 
-        mVcn = new Vcn(mVcnContext, TEST_SUB_GROUP, mConfig, mSubscriptionSnapshot, mDeps);
+        final VcnConfig.Builder configBuilder = new VcnConfig.Builder(mContext);
+        for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+            configBuilder.addGatewayConnectionConfig(
+                    VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(capability));
+        }
+        configBuilder.addGatewayConnectionConfig(VcnGatewayConnectionConfigTest.buildTestConfig());
+        mConfig = configBuilder.build();
+
+        mVcn =
+                new Vcn(
+                        mVcnContext,
+                        TEST_SUB_GROUP,
+                        mConfig,
+                        mSubscriptionSnapshot,
+                        mVcnSafemodeCallback,
+                        mDeps);
     }
 
     private NetworkRequestListener verifyAndGetRequestListener() {
@@ -95,23 +116,22 @@
         return mNetworkRequestListenerCaptor.getValue();
     }
 
-    private NetworkRequest getNetworkRequestWithCapabilities(int[] networkCapabilities) {
-        final NetworkRequest.Builder builder = new NetworkRequest.Builder();
-        for (final int netCapability : networkCapabilities) {
-            builder.addCapability(netCapability);
+    private void startVcnGatewayWithCapabilities(
+            NetworkRequestListener requestListener, int... netCapabilities) {
+        final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
+        for (final int netCapability : netCapabilities) {
+            requestBuilder.addCapability(netCapability);
         }
-        return builder.build();
+
+        requestListener.onNetworkRequested(requestBuilder.build(), NETWORK_SCORE, PROVIDER_ID);
+        mTestLooper.dispatchAll();
     }
 
     @Test
     public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
         final NetworkRequestListener requestListener = verifyAndGetRequestListener();
-
-        requestListener.onNetworkRequested(
-                getNetworkRequestWithCapabilities(VcnGatewayConnectionConfigTest.EXPOSED_CAPS),
-                NETWORK_SCORE,
-                PROVIDER_ID);
-        mTestLooper.dispatchAll();
+        startVcnGatewayWithCapabilities(
+                requestListener, VcnGatewayConnectionConfigTest.EXPOSED_CAPS);
 
         final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
         assertFalse(gatewayConnections.isEmpty());
@@ -126,4 +146,38 @@
             verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot));
         }
     }
+
+    @Test
+    public void testGatewayEnteringSafemodeNotifiesVcn() {
+        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
+        for (final int capability : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+            startVcnGatewayWithCapabilities(requestListener, capability);
+        }
+
+        // Each Capability in EXPOSED_CAPS was split into a separate VcnGatewayConnection in #setUp.
+        // Expect one VcnGatewayConnection per capability.
+        final int numExpectedGateways = VcnGatewayConnectionConfigTest.EXPOSED_CAPS.length;
+
+        final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
+        assertEquals(numExpectedGateways, gatewayConnections.size());
+        verify(mDeps, times(numExpectedGateways))
+                .newVcnGatewayConnection(
+                        eq(mVcnContext),
+                        eq(TEST_SUB_GROUP),
+                        eq(mSubscriptionSnapshot),
+                        any(),
+                        mGatewayStatusCallbackCaptor.capture());
+
+        // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down
+        // all Gateways
+        final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
+        statusCallback.onEnteredSafemode();
+        mTestLooper.dispatchAll();
+
+        for (final VcnGatewayConnection gatewayConnection : gatewayConnections) {
+            verify(gatewayConnection).teardownAsynchronously();
+        }
+        verify(mVcnNetworkProvider).unregisterListener(requestListener);
+        verify(mVcnSafemodeCallback).onEnteredSafemode();
+    }
 }