Merge "Modify uwb permission strings" into sc-dev
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
index 1324451..5672bc7 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java
@@ -297,11 +297,26 @@
         }
 
         /**
-         * Sets the {@link Migrator}.
+         * Sets the {@link Migrator} associated with the given SchemaType.
+         *
+         * <p>The {@link Migrator} migrates all {@link GenericDocument}s under given schema type
+         * from the current version number stored in AppSearch to the final version set via {@link
+         * #setVersion}.
+         *
+         * <p>A {@link Migrator} will be invoked if the current version number stored in AppSearch
+         * is different from the final version set via {@link #setVersion} and {@link
+         * Migrator#shouldMigrate} returns {@code true}.
+         *
+         * <p>The target schema type of the output {@link GenericDocument} of {@link
+         * Migrator#onUpgrade} or {@link Migrator#onDowngrade} must exist in this {@link
+         * SetSchemaRequest}.
          *
          * @param schemaType The schema type to set migrator on.
-         * @param migrator The migrator translate a document from it's old version to a new
-         *     incompatible version.
+         * @param migrator The migrator translates a document from its current version to the final
+         *     version set via {@link #setVersion}.
+         * @see SetSchemaRequest.Builder#setVersion
+         * @see SetSchemaRequest.Builder#addSchemas
+         * @see AppSearchSession#setSchema
          */
         @NonNull
         @SuppressLint("MissingGetterMatchingBuilder") // Getter return plural objects.
@@ -313,10 +328,25 @@
         }
 
         /**
-         * Sets {@link Migrator}s.
+         * Sets a Map of {@link Migrator}s.
          *
-         * @param migrators A {@link Map} of migrators that translate a document from its old
-         *     version to a new incompatible version.
+         * <p>The {@link Migrator} migrates all {@link GenericDocument}s under given schema type
+         * from the current version number stored in AppSearch to the final version set via {@link
+         * #setVersion}.
+         *
+         * <p>A {@link Migrator} will be invoked if the current version number stored in AppSearch
+         * is different from the final version set via {@link #setVersion} and {@link
+         * Migrator#shouldMigrate} returns {@code true}.
+         *
+         * <p>The target schema type of the output {@link GenericDocument} of {@link
+         * Migrator#onUpgrade} or {@link Migrator#onDowngrade} must exist in this {@link
+         * SetSchemaRequest}.
+         *
+         * @param migrators A {@link Map} of migrators that translate a document from it's current
+         *     version to the final version set via {@link #setVersion}.
+         * @see SetSchemaRequest.Builder#setVersion
+         * @see SetSchemaRequest.Builder#addSchemas
+         * @see AppSearchSession#setSchema
          */
         @NonNull
         public Builder setMigrators(@NonNull Map<String, Migrator> migrators) {
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 877dacb..5f3a3ae 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -16,6 +16,7 @@
 package com.android.server.appsearch;
 
 import static android.app.appsearch.AppSearchResult.throwableToFailedResult;
+import static android.os.UserHandle.USER_NULL;
 
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
@@ -34,7 +35,10 @@
 import android.app.appsearch.SearchSpec;
 import android.app.appsearch.SetSchemaResponse;
 import android.app.appsearch.StorageInfo;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
 import android.os.Bundle;
@@ -46,6 +50,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
@@ -67,6 +72,7 @@
 /** TODO(b/142567528): add comments when implement this class */
 public class AppSearchManagerService extends SystemService {
     private static final String TAG = "AppSearchManagerService";
+    private final Context mContext;
     private PackageManagerInternal mPackageManagerInternal;
     private ImplInstanceManager mImplInstanceManager;
     private UserManager mUserManager;
@@ -79,14 +85,60 @@
 
     public AppSearchManagerService(Context context) {
         super(context);
+        mContext = context;
     }
 
     @Override
     public void onStart() {
         publishBinderService(Context.APP_SEARCH_SERVICE, new Stub());
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
-        mImplInstanceManager = ImplInstanceManager.getInstance(getContext());
-        mUserManager = getContext().getSystemService(UserManager.class);
+        mImplInstanceManager = ImplInstanceManager.getInstance(mContext);
+        mUserManager = mContext.getSystemService(UserManager.class);
+        registerReceivers();
+    }
+
+    private void registerReceivers() {
+        mContext.registerReceiverAsUser(new UserActionReceiver(), UserHandle.ALL,
+                new IntentFilter(Intent.ACTION_USER_REMOVED), /*broadcastPermission=*/ null,
+                /*scheduler=*/ null);
+    }
+
+    private class UserActionReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(@NonNull Context context, @NonNull Intent intent) {
+            switch (intent.getAction()) {
+                case Intent.ACTION_USER_REMOVED:
+                    final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
+                    if (userId == USER_NULL) {
+                        Slog.e(TAG, "userId is missing in the intent: " + intent);
+                        return;
+                    }
+                    handleUserRemoved(userId);
+                    break;
+                default:
+                    Slog.e(TAG, "Received unknown intent: " + intent);
+            }
+        }
+    }
+
+    /**
+     * Handles user removed action.
+     *
+     * <p>Only need to clear the AppSearchImpl instance. The data of AppSearch is saved in the
+     * "credential encrypted" system directory of each user. That directory will be auto-deleted
+     * when a user is removed.
+     *
+     * @param userId The multi-user userId of the user that need to be removed.
+     *
+     * @see android.os.Environment#getDataSystemCeDirectory
+     */
+    private void handleUserRemoved(@UserIdInt int userId) {
+        try {
+            mImplInstanceManager.removeAppSearchImplForUser(userId);
+            Slog.i(TAG, "Removed AppSearchImpl instance for user: " + userId);
+        } catch (Throwable t) {
+            Slog.e(TAG, "Unable to remove data for user: " + userId, t);
+        }
     }
 
     @Override
@@ -663,7 +715,7 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 verifyUserUnlocked(callingUserId);
-                mImplInstanceManager.getOrCreateAppSearchImpl(getContext(), callingUserId);
+                mImplInstanceManager.getOrCreateAppSearchImpl(mContext, callingUserId);
                 invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
             } catch (Throwable t) {
                 invokeCallbackOnError(callback, t);
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index cacf880..e82dd9a 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -95,6 +95,23 @@
         }
     }
 
+    /**
+     * Remove an instance of {@link AppSearchImpl} for the given user.
+     *
+     * <p>This method should only be called if {@link AppSearchManagerService} receives an
+     * ACTION_USER_REMOVED, which the instance of given user should be removed.
+     *
+     * <p>If the user is removed, the "credential encrypted" system directory where icing lives will
+     * be auto-deleted. So we shouldn't worry about persist data or close the AppSearchImpl.
+     *
+     * @param userId The multi-user userId of the user that need to be removed.
+     */
+    @NonNull
+    public void removeAppSearchImplForUser(@UserIdInt int userId) {
+        synchronized (mInstancesLocked) {
+            mInstancesLocked.remove(userId);
+        }
+    }
 
     /**
      * Gets an instance of AppSearchImpl for the given user.
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING b/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING
index ca5b884..38cd7a8 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING
+++ b/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING
@@ -4,6 +4,9 @@
       "name": "CtsAppSearchTestCases"
     },
     {
+      "name": "CtsAppSearchHostTestCases"
+    },
+    {
       "name": "FrameworksServicesTests",
       "options": [
         {
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index 5b43ac3..5f8cbee 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -1127,6 +1127,40 @@
     }
 
     /**
+     * Remove all {@link AppSearchSchema}s and {@link GenericDocument}s under the given package.
+     *
+     * @param packageName The name of package to be removed.
+     * @throws AppSearchException if we cannot remove the data.
+     */
+    public void clearPackageData(@NonNull String packageName) throws AppSearchException {
+        mReadWriteLock.writeLock().lock();
+        try {
+            throwIfClosedLocked();
+
+            SchemaProto existingSchema = getSchemaProtoLocked();
+            SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder();
+
+            String prefix = createPackagePrefix(packageName);
+            for (int i = 0; i < existingSchema.getTypesCount(); i++) {
+                if (!existingSchema.getTypes(i).getSchemaType().startsWith(prefix)) {
+                    newSchemaBuilder.addTypes(existingSchema.getTypes(i));
+                }
+            }
+
+            // Apply schema, set force override to true to remove all schemas and documents under
+            // that package.
+            SetSchemaResultProto setSchemaResultProto =
+                    mIcingSearchEngineLocked.setSchema(
+                            newSchemaBuilder.build(), /*ignoreErrorsAndDeleteDocuments=*/ true);
+
+            // Determine whether it succeeded.
+            checkSuccess(setSchemaResultProto.getStatus());
+        } finally {
+            mReadWriteLock.writeLock().unlock();
+        }
+    }
+
+    /**
      * Clears documents and schema across all packages and databaseNames.
      *
      * <p>This method also clear all data in {@link VisibilityStore}, an {@link
@@ -1624,7 +1658,12 @@
 
     @NonNull
     static String createPrefix(@NonNull String packageName, @NonNull String databaseName) {
-        return packageName + PACKAGE_DELIMITER + databaseName + DATABASE_DELIMITER;
+        return createPackagePrefix(packageName) + databaseName + DATABASE_DELIMITER;
+    }
+
+    @NonNull
+    private static String createPackagePrefix(@NonNull String packageName) {
+        return packageName + PACKAGE_DELIMITER;
     }
 
     /**
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index 29bd541..e46c147 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-Ibbd3a92ad091f6911de652e2ba7e44f555a70a72
+I925ec12f4901c7759976c344ba3428210aada8ad
diff --git a/apex/appsearch/testing/Android.bp b/apex/appsearch/testing/Android.bp
index 3c5082e..2518394 100644
--- a/apex/appsearch/testing/Android.bp
+++ b/apex/appsearch/testing/Android.bp
@@ -31,6 +31,7 @@
         "truth-prebuilt",
     ],
     visibility: [
+        "//cts/hostsidetests/appsearch",
         "//cts/tests/appsearch",
         "//vendor:__subpackages__",
     ],
diff --git a/apex/media/framework/TEST_MAPPING b/apex/media/framework/TEST_MAPPING
index 70c9087..3d21914 100644
--- a/apex/media/framework/TEST_MAPPING
+++ b/apex/media/framework/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name": "CtsMediaParserTestCases"
     },
     {
-      "name": "CtsMediaParserHostSideTestCases"
+      "name": "CtsMediaParserHostTestCases"
     }
   ]
 }
diff --git a/core/api/current.txt b/core/api/current.txt
index 6fb6116..38104b5 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -18826,10 +18826,10 @@
   public final class DeviceProductInfo implements android.os.Parcelable {
     method public int describeContents();
     method public int getConnectionToSinkType();
-    method public int getManufactureWeek();
-    method public int getManufactureYear();
+    method @IntRange(from=0xffffffff, to=53) public int getManufactureWeek();
+    method @IntRange(from=0xffffffff) public int getManufactureYear();
     method @NonNull public String getManufacturerPnpId();
-    method public int getModelYear();
+    method @IntRange(from=0xffffffff) public int getModelYear();
     method @Nullable public String getName();
     method @NonNull public String getProductId();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -35478,8 +35478,8 @@
 
   public static final class SimPhonebookContract.SimRecords {
     method @NonNull public static android.net.Uri getContentUri(int, int);
-    method @WorkerThread public static int getEncodedNameLength(@NonNull android.content.ContentResolver, @NonNull String);
-    method @NonNull public static android.net.Uri getItemUri(int, int, int);
+    method @IntRange(from=0) @WorkerThread public static int getEncodedNameLength(@NonNull android.content.ContentResolver, @NonNull String);
+    method @NonNull public static android.net.Uri getItemUri(int, int, @IntRange(from=1) int);
     field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/sim-contact_v2";
     field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-contact_v2";
     field public static final String ELEMENTARY_FILE_TYPE = "elementary_file_type";
@@ -41921,42 +41921,42 @@
   @Deprecated public class PhoneStateListener {
     ctor @Deprecated public PhoneStateListener();
     ctor @Deprecated public PhoneStateListener(@NonNull java.util.concurrent.Executor);
-    method @Deprecated public void onActiveDataSubscriptionIdChanged(int);
-    method @Deprecated public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onActiveDataSubscriptionIdChanged(int);
+    method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallDisconnectCauseChanged(int, int);
-    method @Deprecated public void onCallForwardingIndicatorChanged(boolean);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onCallForwardingIndicatorChanged(boolean);
     method @Deprecated @RequiresPermission(value=android.Manifest.permission.READ_PHONE_STATE, conditional=true) public void onCallStateChanged(int, String);
-    method @Deprecated public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>);
-    method @Deprecated public void onCellLocationChanged(android.telephony.CellLocation);
+    method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onCellInfoChanged(java.util.List<android.telephony.CellInfo>);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellLocationChanged(android.telephony.CellLocation);
     method @Deprecated public void onDataActivity(int);
     method @Deprecated public void onDataConnectionStateChanged(int);
     method @Deprecated public void onDataConnectionStateChanged(int, int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
-    method @Deprecated public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
-    method @Deprecated public void onMessageWaitingIndicatorChanged(boolean);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
-    method @Deprecated public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onMessageWaitingIndicatorChanged(boolean);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
+    method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
     method @Deprecated public void onServiceStateChanged(android.telephony.ServiceState);
     method @Deprecated public void onSignalStrengthChanged(int);
     method @Deprecated public void onSignalStrengthsChanged(android.telephony.SignalStrength);
     method @Deprecated public void onUserMobileDataStateChanged(boolean);
-    field @Deprecated public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000
+    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000
+    field @Deprecated @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
-    field @Deprecated public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8
+    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8
     field @Deprecated public static final int LISTEN_CALL_STATE = 32; // 0x20
-    field @Deprecated public static final int LISTEN_CELL_INFO = 1024; // 0x400
-    field @Deprecated public static final int LISTEN_CELL_LOCATION = 16; // 0x10
+    field @Deprecated @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int LISTEN_CELL_INFO = 1024; // 0x400
+    field @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int LISTEN_CELL_LOCATION = 16; // 0x10
     field @Deprecated public static final int LISTEN_DATA_ACTIVITY = 128; // 0x80
     field @Deprecated public static final int LISTEN_DATA_CONNECTION_STATE = 64; // 0x40
     field @Deprecated public static final int LISTEN_DISPLAY_INFO_CHANGED = 1048576; // 0x100000
-    field @Deprecated public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000
+    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_EMERGENCY_NUMBER_LIST = 16777216; // 0x1000000
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000
-    field @Deprecated public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
+    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
     field @Deprecated public static final int LISTEN_NONE = 0; // 0x0
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
-    field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
+    field @Deprecated @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
     field @Deprecated public static final int LISTEN_SERVICE_STATE = 1; // 0x1
     field @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 2; // 0x2
     field @Deprecated public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100
@@ -42374,7 +42374,7 @@
     method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
     method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void removeSubscriptionsFromGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharingContacts(@NonNull java.util.List<android.net.Uri>, int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharingContacts(int, @NonNull java.util.List<android.net.Uri>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharingPreference(int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunistic(boolean, int);
     method public void setSubscriptionOverrideCongested(int, boolean, long);
@@ -42455,10 +42455,6 @@
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void onActiveDataSubscriptionIdChanged(int);
   }
 
-  public static interface TelephonyCallback.AlwaysReportedSignalStrengthListener {
-    method @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
-  }
-
   public static interface TelephonyCallback.BarringInfoListener {
     method @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onBarringInfoChanged(@NonNull android.telephony.BarringInfo);
   }
@@ -42480,7 +42476,7 @@
   }
 
   public static interface TelephonyCallback.CellInfoListener {
-    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void onCellInfoChanged(@NonNull java.util.List<android.telephony.CellInfo>);
+    method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void onCellInfoChanged(@NonNull java.util.List<android.telephony.CellInfo>);
   }
 
   public static interface TelephonyCallback.CellLocationListener {
@@ -42488,15 +42484,15 @@
   }
 
   public static interface TelephonyCallback.DataActivationStateListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivationStateChanged(int);
+    method public void onDataActivationStateChanged(int);
   }
 
   public static interface TelephonyCallback.DataActivityListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataActivity(int);
+    method public void onDataActivity(int);
   }
 
   public static interface TelephonyCallback.DataConnectionStateListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onDataConnectionStateChanged(int, int);
+    method public void onDataConnectionStateChanged(int, int);
   }
 
   public static interface TelephonyCallback.DisplayInfoListener {
@@ -42528,15 +42524,15 @@
   }
 
   public static interface TelephonyCallback.ServiceStateListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onServiceStateChanged(@NonNull android.telephony.ServiceState);
+    method public void onServiceStateChanged(@NonNull android.telephony.ServiceState);
   }
 
   public static interface TelephonyCallback.SignalStrengthsListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
+    method public void onSignalStrengthsChanged(@NonNull android.telephony.SignalStrength);
   }
 
   public static interface TelephonyCallback.UserMobileDataStateListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onUserMobileDataStateChanged(boolean);
+    method public void onUserMobileDataStateChanged(boolean);
   }
 
   public final class TelephonyDisplayInfo implements android.os.Parcelable {
@@ -50825,6 +50821,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityNodeInfo> CREATOR;
     field public static final String EXTRA_DATA_RENDERING_INFO_KEY = "android.view.accessibility.extra.DATA_RENDERING_INFO_KEY";
     field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
+    field public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000; // 0x4e20
     field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
     field public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY = "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
     field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
@@ -55495,6 +55492,7 @@
 
   public class RemoteViews implements android.view.LayoutInflater.Filter android.os.Parcelable {
     ctor public RemoteViews(String, int);
+    ctor public RemoteViews(@NonNull String, @LayoutRes int, @IdRes int);
     ctor public RemoteViews(android.widget.RemoteViews, android.widget.RemoteViews);
     ctor public RemoteViews(@NonNull java.util.Map<android.util.SizeF,android.widget.RemoteViews>);
     ctor public RemoteViews(android.widget.RemoteViews);
@@ -55568,7 +55566,6 @@
     method public void setTextViewText(@IdRes int, CharSequence);
     method public void setTextViewTextSize(@IdRes int, int, float);
     method public void setUri(@IdRes int, String, android.net.Uri);
-    method public void setViewId(@IdRes int);
     method public void setViewLayoutHeight(@IdRes int, float, int);
     method public void setViewLayoutHeightDimen(@IdRes int, @DimenRes int);
     method public void setViewLayoutMargin(@IdRes int, int, float, int);
@@ -56741,18 +56738,19 @@
 package android.window {
 
   public interface SplashScreen {
-    method public void setOnExitAnimationListener(@Nullable android.window.SplashScreen.OnExitAnimationListener);
+    method public void clearOnExitAnimationListener();
+    method public void setOnExitAnimationListener(@NonNull android.window.SplashScreen.OnExitAnimationListener);
   }
 
   public static interface SplashScreen.OnExitAnimationListener {
-    method public void onSplashScreenExit(@NonNull android.window.SplashScreenView);
+    method @UiThread public void onSplashScreenExit(@NonNull android.window.SplashScreenView);
   }
 
   public final class SplashScreenView extends android.widget.FrameLayout {
-    method public long getIconAnimationDurationMillis();
-    method public long getIconAnimationStartMillis();
+    method @Nullable public java.time.Duration getIconAnimationDuration();
+    method @Nullable public java.time.Instant getIconAnimationStart();
     method @Nullable public android.view.View getIconView();
-    method public void remove();
+    method @UiThread public void remove();
   }
 
 }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 930c298..40f26e8 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -28,6 +28,7 @@
     field public static final String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
     field public static final String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER";
     field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
+    field public static final String ASSOCIATE_COMPANION_DEVICES = "android.permission.ASSOCIATE_COMPANION_DEVICES";
     field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA";
     field public static final String BACKUP = "android.permission.BACKUP";
     field public static final String BATTERY_PREDICTION = "android.permission.BATTERY_PREDICTION";
@@ -1923,6 +1924,7 @@
     method public boolean disableBLE();
     method public boolean enableBLE();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean enableNoAutoConnect();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void generateLocalOobData(int, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OobDataCallback);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public long getDiscoveryEndMillis();
     method public boolean isBleScanAlwaysAvailable();
     method public boolean isLeEnabled();
@@ -1934,12 +1936,18 @@
     field public static final int ACTIVE_DEVICE_ALL = 2; // 0x2
     field public static final int ACTIVE_DEVICE_AUDIO = 0; // 0x0
     field public static final int ACTIVE_DEVICE_PHONE_CALL = 1; // 0x1
+    field public static final int OOB_ERROR_ADAPTER_DISABLED = 2; // 0x2
+    field public static final int OOB_ERROR_ANOTHER_ACTIVE_REQUEST = 1; // 0x1
+    field public static final int OOB_ERROR_UNKNOWN = 0; // 0x0
   }
 
   public static interface BluetoothAdapter.OnMetadataChangedListener {
     method public void onMetadataChanged(@NonNull android.bluetooth.BluetoothDevice, int, @Nullable byte[]);
   }
 
+  public static interface BluetoothAdapter.OobDataCallback {
+  }
+
   public final class BluetoothDevice implements android.os.Parcelable {
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean canBondWithoutDialog();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean cancelBondProcess();
@@ -2220,7 +2228,7 @@
 package android.companion {
 
   public final class CompanionDeviceManager {
-    method @RequiresPermission("android.permission.ASSOCIATE_COMPANION_DEVICES") public boolean associate(@NonNull String, @NonNull android.net.MacAddress);
+    method @RequiresPermission(android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES) public boolean associate(@NonNull String, @NonNull android.net.MacAddress);
     method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean canPairWithoutPrompt(@NonNull String, @NonNull String, int);
     method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociatedForWifiConnection(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle);
   }
@@ -3005,12 +3013,19 @@
   }
 
   public static final class FontFamilyUpdateRequest.Font {
-    ctor public FontFamilyUpdateRequest.Font(@NonNull String, @NonNull android.graphics.fonts.FontStyle, @NonNull java.util.List<android.graphics.fonts.FontVariationAxis>);
     method @NonNull public java.util.List<android.graphics.fonts.FontVariationAxis> getAxes();
+    method @IntRange(from=0) public int getIndex();
     method @NonNull public String getPostScriptName();
     method @NonNull public android.graphics.fonts.FontStyle getStyle();
   }
 
+  public static final class FontFamilyUpdateRequest.Font.Builder {
+    ctor public FontFamilyUpdateRequest.Font.Builder(@NonNull String, @NonNull android.graphics.fonts.FontStyle);
+    method @NonNull public android.graphics.fonts.FontFamilyUpdateRequest.Font build();
+    method @NonNull public android.graphics.fonts.FontFamilyUpdateRequest.Font.Builder setAxes(@NonNull java.util.List<android.graphics.fonts.FontVariationAxis>);
+    method @NonNull public android.graphics.fonts.FontFamilyUpdateRequest.Font.Builder setIndex(@IntRange(from=0) int);
+  }
+
   public static final class FontFamilyUpdateRequest.FontFamily {
     method @NonNull public java.util.List<android.graphics.fonts.FontFamilyUpdateRequest.Font> getFonts();
     method @NonNull public String getName();
@@ -7921,12 +7936,18 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported();
+    method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnStateCallback);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean);
     method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int);
+    method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnStateCallback(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnStateCallback);
     field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1
   }
 
+  public static interface NfcAdapter.ControllerAlwaysOnStateCallback {
+    method public void onStateChanged(boolean);
+  }
+
   public static interface NfcAdapter.NfcUnlockHandler {
     method public boolean onUnlockAttempted(android.nfc.Tag);
   }
@@ -11364,15 +11385,15 @@
   }
 
   @Deprecated public class PhoneStateListener {
-    method @Deprecated public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
-    method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
-    method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
-    method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
     method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
-    method @Deprecated public void onRadioPowerStateChanged(int);
-    method @Deprecated public void onSrvccStateChanged(int);
-    method @Deprecated public void onVoiceActivationStateChanged(int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onRadioPowerStateChanged(int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onSrvccStateChanged(int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onVoiceActivationStateChanged(int);
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
     field @Deprecated @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
@@ -11721,14 +11742,14 @@
   public class TelephonyCallback {
     field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED = 23; // 0x17
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED = 35; // 0x23
-    field @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; // 0xa
+    field public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10; // 0xa
     field @RequiresPermission(allOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_BARRING_INFO_CHANGED = 32; // 0x20
     field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_ATTRIBUTES_CHANGED = 27; // 0x1b
     field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int EVENT_CALL_DISCONNECT_CAUSE_CHANGED = 26; // 0x1a
     field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4
     field public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6
     field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11
-    field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb
+    field @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb
     field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5
     field public static final int EVENT_DATA_ACTIVATION_STATE_CHANGED = 19; // 0x13
     field public static final int EVENT_DATA_ACTIVITY_CHANGED = 8; // 0x8
@@ -11783,7 +11804,7 @@
   }
 
   public static interface TelephonyCallback.PhoneCapabilityListener {
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
+    method public void onPhoneCapabilityChanged(@NonNull android.telephony.PhoneCapability);
   }
 
   public static interface TelephonyCallback.PreciseCallStateListener {
@@ -13594,10 +13615,11 @@
     method public int describeContents();
     method @Nullable public String getCallIdParameter();
     method @NonNull public byte[] getContent();
-    method @NonNull public byte[] getEncodedMessage();
+    method @Deprecated @NonNull public byte[] getEncodedMessage();
     method @NonNull public String getHeaderSection();
     method @NonNull public String getStartLine();
     method @NonNull public String getViaBranchParameter();
+    method @NonNull public byte[] toEncodedMessage();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipMessage> CREATOR;
   }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 36c66e5..db42803 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -794,6 +794,9 @@
     /** @hide Flag for registerUidObserver: report uid cached state has changed. */
     public static final int UID_OBSERVER_CACHED = 1<<4;
 
+    /** @hide Flag for registerUidObserver: report uid capability has changed. */
+    public static final int UID_OBSERVER_CAPABILITY = 1<<5;
+
     /** @hide Mode for {@link IActivityManager#isAppStartModeDisabled}: normal free-to-run operation. */
     public static final int APP_START_MODE_NORMAL = 0;
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e596e7c..e50432e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -236,12 +236,6 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 
-final class RemoteServiceException extends AndroidRuntimeException {
-    public RemoteServiceException(String msg) {
-        super(msg);
-    }
-}
-
 /**
  * This manages the execution of the main thread in an
  * application process, scheduling and executing activities,
@@ -1274,8 +1268,9 @@
             sendMessage(H.DISPATCH_PACKAGE_BROADCAST, packages, cmd);
         }
 
-        public void scheduleCrash(String msg) {
-            sendMessage(H.SCHEDULE_CRASH, msg);
+        @Override
+        public void scheduleCrash(String msg, int typeId) {
+            sendMessage(H.SCHEDULE_CRASH, msg, typeId);
         }
 
         public void dumpActivity(ParcelFileDescriptor pfd, IBinder activitytoken,
@@ -1892,6 +1887,17 @@
         }
     }
 
+    private void throwRemoteServiceException(String message, int typeId) {
+        // Use a switch to ensure all the type IDs are unique.
+        switch (typeId) {
+            case ForegroundServiceDidNotStartInTimeException.TYPE_ID: // 1
+                throw new ForegroundServiceDidNotStartInTimeException(message);
+            case RemoteServiceException.TYPE_ID: // 0
+            default:
+                throw new RemoteServiceException(message);
+        }
+    }
+
     class H extends Handler {
         public static final int BIND_APPLICATION        = 110;
         @UnsupportedAppUsage
@@ -2105,7 +2111,8 @@
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case SCHEDULE_CRASH:
-                    throw new RemoteServiceException((String)msg.obj);
+                    throwRemoteServiceException((String) msg.obj, msg.arg1);
+                    break;
                 case DUMP_HEAP:
                     handleDumpHeap((DumpHeapData) msg.obj);
                     break;
diff --git a/core/java/android/app/ForegroundServiceDidNotStartInTimeException.java b/core/java/android/app/ForegroundServiceDidNotStartInTimeException.java
new file mode 100644
index 0000000..364291e
--- /dev/null
+++ b/core/java/android/app/ForegroundServiceDidNotStartInTimeException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.app;
+
+/**
+ * Exception used to crash an app process when it didn't call {@link Service#startForeground}
+ * in time after the service was started with
+ * {@link android.content.Context#startForegroundService}.
+ *
+ * @hide
+ */
+public class ForegroundServiceDidNotStartInTimeException extends RemoteServiceException {
+    /** The type ID passed to {@link IApplicationThread#scheduleCrash}. */
+    public static final int TYPE_ID = 1;
+
+    public ForegroundServiceDidNotStartInTimeException(String msg) {
+        super(msg);
+    }
+}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 1b8eb8a..f9279da 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -321,6 +321,8 @@
     boolean isTopActivityImmersive();
     void crashApplication(int uid, int initialPid, in String packageName, int userId,
             in String message, boolean force);
+    void crashApplicationWithType(int uid, int initialPid, in String packageName, int userId,
+            in String message, boolean force, int exceptionTypeId);
     /** @deprecated -- use getProviderMimeTypeAsync */
     @UnsupportedAppUsage(maxTargetSdk = 29, publicAlternatives =
             "Use {@link android.content.ContentResolver#getType} public API instead.")
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index b5294d5..78e7ce8 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -106,7 +106,7 @@
     void scheduleOnNewActivityOptions(IBinder token, in Bundle options);
     void scheduleSuicide();
     void dispatchPackageBroadcast(int cmd, in String[] packages);
-    void scheduleCrash(in String msg);
+    void scheduleCrash(in String msg, int typeId);
     void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, in String path,
             in ParcelFileDescriptor fd, in RemoteCallback finishCallback);
     void dumpActivity(in ParcelFileDescriptor fd, IBinder servicetoken, in String prefix,
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 3de78f6..fa35025 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5360,12 +5360,8 @@
             contentView.setInt(R.id.expand_button, "setDefaultPillColor", pillColor);
             // Use different highlighted colors for conversations' unread count
             if (p.mHighlightExpander) {
-                pillColor = getAccentTertiaryColor(p);
-                // TODO(b/183710694): The accent tertiary is currently too bright in dark mode, so
-                //  we need to pick a contrasting color.
-                textColor = ColorUtils.setAlphaComponent(
-                        ContrastColorUtil.resolvePrimaryColor(mContext, pillColor, mInNightMode),
-                        0xFF);
+                textColor = getBackgroundColor(p);
+                pillColor = getAccentColor(p);
             }
             contentView.setInt(R.id.expand_button, "setHighlightTextColor", textColor);
             contentView.setInt(R.id.expand_button, "setHighlightPillColor", pillColor);
@@ -6296,23 +6292,6 @@
         }
 
         /**
-         * Gets the tertiary accent color for colored UI elements. If we're tinting with the theme
-         * accent, this comes from the tertiary system accent palette, otherwise this would be
-         * identical to {@link #getSmallIconColor(StandardTemplateParams)}.
-         */
-        private @ColorInt int getAccentTertiaryColor(StandardTemplateParams p) {
-            if (isColorized(p)) {
-                return getPrimaryTextColor(p);
-            }
-            int color = obtainThemeColor(com.android.internal.R.attr.colorAccentTertiary,
-                    COLOR_INVALID);
-            if (color != COLOR_INVALID) {
-                return color;
-            }
-            return getContrastColor(p);
-        }
-
-        /**
          * Gets the theme's error color, or the primary text color for colorized notifications.
          */
         private @ColorInt int getErrorColor(StandardTemplateParams p) {
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index f4b9542..4cf3a80 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -379,8 +379,7 @@
         if (Compatibility.isChangeEnabled(PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED)
                 && !flagImmutableSet && !flagMutableSet) {
 
-            //TODO(b/178065720) Remove check for chrome and enforce this requirement
-            if (packageName.equals("com.android.chrome") || mInstrumentation.isInstrumenting()) {
+            if (mInstrumentation.isInstrumenting()) {
                 Log.e(TAG, msg);
             } else {
                 throw new IllegalArgumentException(msg);
diff --git a/core/java/android/app/RemoteServiceException.java b/core/java/android/app/RemoteServiceException.java
new file mode 100644
index 0000000..4b32463
--- /dev/null
+++ b/core/java/android/app/RemoteServiceException.java
@@ -0,0 +1,38 @@
+/*
+ * 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.app;
+
+import android.util.AndroidRuntimeException;
+
+/**
+ * Exception used by {@link ActivityThread} to crash an app process.
+ *
+ * @hide
+ */
+public class RemoteServiceException extends AndroidRuntimeException {
+    /**
+     * The type ID passed to {@link IApplicationThread#scheduleCrash}.
+     *
+     * Assign a unique ID to each subclass. See the above method for the numbers that are already
+     * taken.
+     */
+    public static final int TYPE_ID = 0;
+
+    public RemoteServiceException(String msg) {
+        super(msg);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 5446deb..79fd807 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -28,6 +28,7 @@
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
 import android.app.PropertyInvalidatedCache;
+import android.bluetooth.BluetoothDevice.Transport;
 import android.bluetooth.BluetoothProfile.ConnectionPolicy;
 import android.bluetooth.le.BluetoothLeAdvertiser;
 import android.bluetooth.le.BluetoothLeScanner;
@@ -3046,6 +3047,168 @@
         return false;
     }
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "OOB_ERROR_" }, value = {
+        OOB_ERROR_UNKNOWN,
+        OOB_ERROR_ANOTHER_ACTIVE_REQUEST,
+        OOB_ERROR_ADAPTER_DISABLED
+    })
+    public @interface OobError {}
+
+    /**
+     * An unknown error has occurred in the controller, stack, or callback pipeline.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int OOB_ERROR_UNKNOWN = 0;
+
+    /**
+     * If another application has already requested {@link OobData} then another fetch will be
+     * disallowed until the callback is removed.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int OOB_ERROR_ANOTHER_ACTIVE_REQUEST = 1;
+
+    /**
+     * The adapter is currently disabled, please enable it.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int OOB_ERROR_ADAPTER_DISABLED = 2;
+
+    /**
+     * Provides callback methods for receiving {@link OobData} from the host stack, as well as an
+     * error interface in order to allow the caller to determine next steps based on the {@link
+     * ErrorCode}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface OobDataCallback {
+        /**
+         * Handles the {@link OobData} received from the host stack.
+         *
+         * @param transport - whether the {@link OobData} is generated for LE or Classic.
+         * @param oobData - data generated in the host stack(LE) or controller (Classic)
+         *
+         * @hide
+         */
+        void onOobData(@Transport int transport, @Nullable OobData oobData);
+
+        /**
+         * Provides feedback when things don't go as expected.
+         *
+         * @param errorCode - the code descibing the type of error that occurred.
+         *
+         * @hide
+         */
+        void onError(@OobError int errorCode);
+    }
+
+    /**
+     * Wraps an AIDL interface around an {@link OobDataCallback} interface.
+     *
+     * @see {@link IBluetoothOobDataCallback} for interface definition.
+     *
+     * @hide
+     */
+    public class WrappedOobDataCallback extends IBluetoothOobDataCallback.Stub {
+        private final OobDataCallback mCallback;
+        private final Executor mExecutor;
+
+        /**
+         * @param callback - object to receive {@link OobData} must be a non null argument
+         *
+         * @throws NullPointerException if the callback is null.
+         */
+        WrappedOobDataCallback(@NonNull OobDataCallback callback,
+                @NonNull @CallbackExecutor Executor executor) {
+            Preconditions.checkNotNull(callback);
+            Preconditions.checkNotNull(executor);
+            mCallback = callback;
+            mExecutor = executor;
+        }
+        /**
+         * Wrapper function to relay to the {@link OobDataCallback#onOobData}
+         *
+         * @param transport - whether the {@link OobData} is generated for LE or Classic.
+         * @param oobData - data generated in the host stack(LE) or controller (Classic)
+         *
+         * @hide
+         */
+        public void onOobData(@Transport int transport, OobData oobData) {
+            mExecutor.execute(new Runnable() {
+                public void run() {
+                    mCallback.onOobData(transport, oobData);
+                }
+            });
+        }
+        /**
+         * Wrapper function to relay to the {@link OobDataCallback#onError}
+         *
+         * @param errorCode - the code descibing the type of error that occurred.
+         *
+         * @hide
+         */
+        public void onError(@OobError int errorCode) {
+            mExecutor.execute(new Runnable() {
+                public void run() {
+                    mCallback.onError(errorCode);
+                }
+            });
+        }
+    }
+
+    /**
+     * Fetches a secret data value that can be used for a secure and simple pairing experience.
+     *
+     * <p>This is the Local Out of Band data the comes from the
+     *
+     * <p>This secret is the local Out of Band data.  This data is used to securely and quickly
+     * pair two devices with minimal user interaction.
+     *
+     * <p>For example, this secret can be transferred to a remote device out of band (meaning any
+     * other way besides using bluetooth).  Once the remote device finds this device using the
+     * information given in the data, such as the PUBLIC ADDRESS, the remote device could then
+     * connect to this device using this secret when the pairing sequenece asks for the secret.
+     * This device will respond by automatically accepting the pairing due to the secret being so
+     * trustworthy.
+     *
+     * @param transport - provide type of transport (e.g. LE or Classic).
+     * @param callback - target object to receive the {@link OobData} value.
+     *
+     * @throws NullPointerException if callback is null.
+     * @throws IllegalArgumentException if the transport is not valid.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public void generateLocalOobData(@Transport int transport,
+            @NonNull @CallbackExecutor Executor executor, @NonNull OobDataCallback callback) {
+        if (transport != BluetoothDevice.TRANSPORT_BREDR && transport
+                != BluetoothDevice.TRANSPORT_LE) {
+            throw new IllegalArgumentException("Invalid transport '" + transport + "'!");
+        }
+        Preconditions.checkNotNull(callback);
+        if (!isEnabled()) {
+            Log.w(TAG, "generateLocalOobData(): Adapter isn't enabled!");
+            callback.onError(OOB_ERROR_ADAPTER_DISABLED);
+        } else {
+            try {
+                mService.generateLocalOobData(transport, new WrappedOobDataCallback(callback,
+                        executor));
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+    }
+
     /**
      * Enable control of the Bluetooth Adapter for a single application.
      *
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index a96c14f..0c208fd 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -952,6 +952,21 @@
     @SystemApi
     public static final int ACCESS_REJECTED = 2;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(
+        prefix = { "TRANSPORT_" },
+        value = {
+            /** Allow host to automatically select a transport (dual-mode only) */
+            TRANSPORT_AUTO,
+            /** Use Classic or BR/EDR transport.*/
+            TRANSPORT_BREDR,
+            /** Use Low Energy transport.*/
+            TRANSPORT_LE,
+        }
+    )
+    public @interface Transport {}
+
     /**
      * No preference of physical transport for GATT connections to remote dual-mode devices
      */
@@ -1084,6 +1099,10 @@
         public void onBrEdrDown() {
             if (DBG) Log.d(TAG, "onBrEdrDown: reached BLE ON state");
         }
+
+        public void onOobData(@Transport int transport, OobData oobData) {
+            if (DBG) Log.d(TAG, "onOobData: got data");
+        }
     };
 
     /**
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index c851519..b13bf09 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -21,15 +21,12 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.app.AppGlobals;
 import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
-import android.os.RemoteException;
-import android.os.UserHandle;
 import android.permission.PermissionManager;
 import android.util.ArraySet;
 
@@ -93,6 +90,8 @@
 // TODO: Codegen applies method level annotations to argument vs the generated member (@SystemApi)
 // TODO: Codegen doesn't properly read/write IBinder members
 // TODO: Codegen doesn't properly handle Set arguments
+// TODO: Codegen requires @SystemApi annotations on fields which breaks
+//      android.signature.cts.api.AnnotationTest (need to update the test)
 // @DataClass(genEqualsHashCode = true, genConstructor = false, genBuilder = true)
 public final class AttributionSource implements Parcelable {
     /**
@@ -153,8 +152,6 @@
      *
      * @hide
      */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
     @DataClass.ParcelWith(RenouncedPermissionsParcelling.class)
     private @Nullable Set<String> mRenouncedPermissions = null;
 
@@ -516,7 +513,7 @@
         private @Nullable String mPackageName;
         private @Nullable String mAttributionTag;
         private @Nullable IBinder mToken;
-        private @SystemApi @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS) @Nullable Set<String> mRenouncedPermissions;
+        private @Nullable Set<String> mRenouncedPermissions;
         private @Nullable AttributionSource mNext;
 
         private long mBuilderFieldsSet = 0L;
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index 01b554a..1735aa2 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -5,6 +5,7 @@
 per-file IntentFilter.java = patb@google.com
 per-file Intent.java = toddke@google.com
 per-file Intent.java = patb@google.com
+per-file Intent.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file AutofillOptions* = file:/core/java/android/service/autofill/OWNERS
 per-file ContentCaptureOptions* = file:/core/java/android/service/contentcapture/OWNERS
 per-file LocusId* = file:/core/java/android/service/contentcapture/OWNERS
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index 049bfe7..5089f30 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -1066,11 +1066,25 @@
                 return AppOpsManager.MODE_ERRORED;
             }
             if (selfAccess) {
-                return appOpsManager.startOpNoThrow(op, resolvedAttributionSource.getUid(),
-                        resolvedAttributionSource.getPackageName(),
-                        /*startIfModeDefault*/ false,
-                        resolvedAttributionSource.getAttributionTag(),
-                        message);
+                // If the datasource is not in a trusted platform component then in would not
+                // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that
+                // an app is exposing runtime permission protected data but cannot blame others
+                // in a trusted way which would not properly show in permission usage UIs.
+                // As a fallback we note a proxy op that blames the app and the datasource.
+                try {
+                    return appOpsManager.startOpNoThrow(op, resolvedAttributionSource.getUid(),
+                            resolvedAttributionSource.getPackageName(),
+                            /*startIfModeDefault*/ false,
+                            resolvedAttributionSource.getAttributionTag(),
+                            message);
+                } catch (SecurityException e) {
+                    Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with"
+                            + " platform defined runtime permission "
+                            + AppOpsManager.opToPermission(op) + " while not having "
+                            + Manifest.permission.UPDATE_APP_OPS_STATS);
+                    return appOpsManager.startProxyOpNoThrow(op, attributionSource, message,
+                            skipProxyOperation);
+                }
             } else {
                 return appOpsManager.startProxyOpNoThrow(op, resolvedAttributionSource, message,
                         skipProxyOperation);
@@ -1082,10 +1096,24 @@
                 return AppOpsManager.MODE_ERRORED;
             }
             if (selfAccess) {
-                return appOpsManager.noteOpNoThrow(op, resolvedAttributionSource.getUid(),
-                        resolvedAttributionSource.getPackageName(),
-                        resolvedAttributionSource.getAttributionTag(),
-                        message);
+                // If the datasource is not in a trusted platform component then in would not
+                // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that
+                // an app is exposing runtime permission protected data but cannot blame others
+                // in a trusted way which would not properly show in permission usage UIs.
+                // As a fallback we note a proxy op that blames the app and the datasource.
+                try {
+                    return appOpsManager.noteOpNoThrow(op, resolvedAttributionSource.getUid(),
+                            resolvedAttributionSource.getPackageName(),
+                            resolvedAttributionSource.getAttributionTag(),
+                            message);
+                } catch (SecurityException e) {
+                    Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with"
+                            + " platform defined runtime permission "
+                            + AppOpsManager.opToPermission(op) + " while not having "
+                            + Manifest.permission.UPDATE_APP_OPS_STATS);
+                    return appOpsManager.noteProxyOpNoThrow(op, attributionSource, message,
+                            skipProxyOperation);
+                }
             } else {
                 return appOpsManager.noteProxyOpNoThrow(op, resolvedAttributionSource, message,
                         skipProxyOperation);
diff --git a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
index fbc951e..9290497 100644
--- a/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
+++ b/core/java/android/graphics/fonts/FontFamilyUpdateRequest.java
@@ -23,6 +23,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
@@ -157,6 +158,61 @@
      */
     public static final class Font {
 
+        /**
+         * Builds a {@link Font}.
+         */
+        public static final class Builder {
+            private final@NonNull  String mPostScriptName;
+            private final @NonNull FontStyle mStyle;
+            private @NonNull List<FontVariationAxis> mAxes = Collections.emptyList();
+            private @IntRange(from = 0) int mIndex = 0;
+
+            /**
+             * Construct a {@link Font.Builder}
+             *
+             * @param postScriptName The PostScript name of the font file to use. PostScript name is
+             *                       in Name ID 6 field in 'name' table, as specified by OpenType
+             *                       specification.
+             * @param style          The style for this font.
+             */
+            public Builder(@NonNull String postScriptName, @NonNull FontStyle style) {
+                Objects.requireNonNull(postScriptName);
+                Preconditions.checkStringNotEmpty(postScriptName);
+                Objects.requireNonNull(style);
+                mPostScriptName = postScriptName;
+                mStyle = style;
+            }
+
+            /**
+             * A list of {@link FontVariationAxis} to specify axis tags and values for variable
+             * fonts.
+             */
+            public @NonNull Builder setAxes(@NonNull List<FontVariationAxis> axes) {
+                Objects.requireNonNull(axes);
+                Preconditions.checkCollectionElementsNotNull(axes, "axes");
+                mAxes = axes;
+                return this;
+            }
+
+            /**
+             * Sets font collection index for the Font.
+             *
+             * @see {@link android.R.attr#ttcIndex}.
+             */
+            public @NonNull Builder setIndex(@IntRange(from = 0) int index) {
+                Preconditions.checkArgumentNonnegative(index);
+                mIndex = index;
+                return this;
+            }
+
+            /**
+             * Build a {@link Font} instance.
+             */
+            public @NonNull Font build() {
+                return new Font(mPostScriptName, mStyle, mIndex, mAxes);
+            }
+        }
+
         @NonNull
         private final String mPostScriptName;
         @NonNull
@@ -164,6 +220,8 @@
         @NonNull
         private final List<FontVariationAxis> mAxes;
 
+        private final @IntRange(from = 0) int mIndex;
+
         /**
          * Constructs a FontStyleVariation.
          *
@@ -176,18 +234,15 @@
          *                       Name ID 6 field in 'name' table, as specified by OpenType
          *                       specification.
          * @param style          The style for this font.
+         * @param index          The index of the font in the collection.
          * @param axes           A list of {@link FontVariationAxis} to specify axis tags and values
          *                       for variable fonts.
          */
-        public Font(@NonNull String postScriptName, @NonNull FontStyle style,
-                @NonNull List<FontVariationAxis> axes) {
-            Objects.requireNonNull(postScriptName);
-            Preconditions.checkStringNotEmpty(postScriptName);
-            Objects.requireNonNull(style);
-            Objects.requireNonNull(axes);
-            Preconditions.checkCollectionElementsNotNull(axes, "axes");
+        private Font(@NonNull String postScriptName, @NonNull FontStyle style,
+                @IntRange(from = 0) int index, @NonNull List<FontVariationAxis> axes) {
             mPostScriptName = postScriptName;
             mStyle = style;
+            mIndex = index;
             mAxes = axes;
         }
 
@@ -207,6 +262,7 @@
             return mStyle;
         }
 
+
         /**
          * Returns the list of {@link FontVariationAxis}.
          */
@@ -217,9 +273,6 @@
 
         /**
          * Returns the index of collection
-         *
-         * TODO(183752879): Make font index configurable and make this SystemApi.
-         * @hide
          */
         public @IntRange(from = 0) int getIndex() {
             return 0;
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 365dea6..3c11d8e 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -575,7 +575,6 @@
                 && rate > CAPPED_SAMPLING_RATE_LEVEL
                 && mIsPackageDebuggable
                 && !mHasHighSamplingRateSensorsPermission) {
-            Compatibility.reportChange(CHANGE_ID_SAMPLING_RATE_SENSORS_PERMISSION);
             throw new SecurityException("To use the sampling rate level " + rate
                     + ", app needs to declare the normal permission"
                     + " HIGH_SAMPLING_RATE_SENSORS.");
@@ -787,7 +786,6 @@
                     && rateUs < CAPPED_SAMPLING_PERIOD_US
                     && mManager.mIsPackageDebuggable
                     && !mManager.mHasHighSamplingRateSensorsPermission) {
-                Compatibility.reportChange(CHANGE_ID_SAMPLING_RATE_SENSORS_PERMISSION);
                 throw new SecurityException("To use the sampling rate of " + rateUs
                         + " microseconds, app needs to declare the normal permission"
                         + " HIGH_SAMPLING_RATE_SENSORS.");
diff --git a/core/java/android/hardware/display/DeviceProductInfo.java b/core/java/android/hardware/display/DeviceProductInfo.java
index 9457d8f1..11c426a 100644
--- a/core/java/android/hardware/display/DeviceProductInfo.java
+++ b/core/java/android/hardware/display/DeviceProductInfo.java
@@ -17,6 +17,7 @@
 package android.hardware.display;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
@@ -118,6 +119,7 @@
      * @return Model year of the device. Return -1 if not available. Typically,
      * one of model year or manufacture year is available.
      */
+    @IntRange(from = -1)
     public int getModelYear()  {
         return mModelYear != null ? mModelYear : -1;
     }
@@ -126,6 +128,7 @@
      * @return The year of manufacture, or -1 it is not available. Typically,
      * one of model year or manufacture year is available.
      */
+    @IntRange(from = -1)
     public int getManufactureYear()  {
         if (mManufactureDate == null) {
             return -1;
@@ -134,9 +137,10 @@
     }
 
     /**
-     * @return The week of manufacture, or -1 it is not available. Typically,
-     * not present if model year is available.
+     * @return The week of manufacture which ranges from 1 to 53, or -1 it is not available.
+     * Typically, it is not present if model year is available.
      */
+    @IntRange(from = -1, to = 53)
     public int getManufactureWeek() {
         if (mManufactureDate == null) {
             return -1;
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index cdc219a..983a43a 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -391,8 +391,9 @@
             }
 
             final int numListeners = mDisplayListeners.size();
+            DisplayInfo info = getDisplayInfo(displayId);
             for (int i = 0; i < numListeners; i++) {
-                mDisplayListeners.get(i).sendDisplayEvent(displayId, event);
+                mDisplayListeners.get(i).sendDisplayEvent(displayId, event, info);
             }
             if (event == EVENT_DISPLAY_CHANGED && mDispatchNativeCallbacks) {
                 // Choreographer only supports a single display, so only dispatch refresh rate
@@ -894,6 +895,8 @@
         public final DisplayListener mListener;
         public long mEventsMask;
 
+        private final DisplayInfo mDisplayInfo = new DisplayInfo();
+
         DisplayListenerDelegate(DisplayListener listener, @NonNull Looper looper,
                 @EventsMask long eventsMask) {
             super(looper, null, true /*async*/);
@@ -901,8 +904,8 @@
             mEventsMask = eventsMask;
         }
 
-        public void sendDisplayEvent(int displayId, @DisplayEvent int event) {
-            Message msg = obtainMessage(event, displayId, 0);
+        public void sendDisplayEvent(int displayId, @DisplayEvent int event, DisplayInfo info) {
+            Message msg = obtainMessage(event, displayId, 0, info);
             sendMessage(msg);
         }
 
@@ -924,7 +927,11 @@
                     break;
                 case EVENT_DISPLAY_CHANGED:
                     if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CHANGED) != 0) {
-                        mListener.onDisplayChanged(msg.arg1);
+                        DisplayInfo newInfo = (DisplayInfo) msg.obj;
+                        if (newInfo != null && !newInfo.equals(mDisplayInfo)) {
+                            mDisplayInfo.copyFrom(newInfo);
+                            mListener.onDisplayChanged(msg.arg1);
+                        }
                     }
                     break;
                 case EVENT_DISPLAY_REMOVED:
diff --git a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
index f13326b..d06bc1d 100644
--- a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
+++ b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
@@ -84,7 +84,6 @@
      *         of the result
      */
     public void oneTouchPlay(OneTouchPlayCallback callback) {
-        // TODO: Use PendingResult.
         try {
             mService.oneTouchPlay(getCallbackWrapper(callback));
         } catch (RemoteException e) {
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 632eb15..ec83c4e 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -772,6 +772,12 @@
         return DebugUtils.flagsToString(ConnectivityManager.class, "BLOCKED_", blockedReasons);
     }
 
+    /** @hide */
+    @NonNull
+    public static String allowedReasonsToString(int allowedReasons) {
+        return DebugUtils.flagsToString(NetworkPolicyManager.class, "ALLOWED_", allowedReasons);
+    }
+
     /**
      * Register a {@link NetworkPolicyCallback} to listen for changes to network blocked status
      * of apps.
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 11445e9..d5cc01a 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -24,6 +24,7 @@
 import android.nfc.TechListParcel;
 import android.nfc.IAppCallback;
 import android.nfc.INfcAdapterExtras;
+import android.nfc.INfcControllerAlwaysOnStateCallback;
 import android.nfc.INfcTag;
 import android.nfc.INfcCardEmulation;
 import android.nfc.INfcFCardEmulation;
@@ -75,4 +76,6 @@
     boolean setControllerAlwaysOn(boolean value);
     boolean isControllerAlwaysOn();
     boolean isControllerAlwaysOnSupported();
+    void registerControllerAlwaysOnStateCallback(in INfcControllerAlwaysOnStateCallback callback);
+    void unregisterControllerAlwaysOnStateCallback(in INfcControllerAlwaysOnStateCallback callback);
 }
diff --git a/core/java/android/nfc/INfcControllerAlwaysOnStateCallback.aidl b/core/java/android/nfc/INfcControllerAlwaysOnStateCallback.aidl
new file mode 100644
index 0000000..1e4fdd7
--- /dev/null
+++ b/core/java/android/nfc/INfcControllerAlwaysOnStateCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright 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.nfc;
+
+/**
+ * @hide
+ */
+oneway interface INfcControllerAlwaysOnStateCallback {
+  /**
+   * Called whenever the controller always on state changes
+   *
+   * @param isEnabled true if the state is enabled, false otherwise
+   */
+  void onControllerAlwaysOnStateChanged(boolean isEnabled);
+}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index eed2c77..bbf802c 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -16,6 +16,7 @@
 
 package android.nfc;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
@@ -47,6 +48,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Represents the local NFC adapter.
@@ -65,6 +67,8 @@
 public final class NfcAdapter {
     static final String TAG = "NFC";
 
+    private final NfcControllerAlwaysOnStateListener mControllerAlwaysOnStateListener;
+
     /**
      * Intent to start an activity when a tag with NDEF payload is discovered.
      *
@@ -350,22 +354,6 @@
             "android.nfc.extra.HANDOVER_TRANSFER_STATUS";
 
     /** @hide */
-    public static final String ACTION_ALWAYS_ON_STATE_CHANGED =
-            "android.nfc.action.ALWAYS_ON_STATE_CHANGED";
-
-    /**
-     * Used as an int extra field in {@link #ACTION_ALWAYS_ON_STATE_CHANGED}
-     * intents to request the current power state. Possible values are:
-     * {@link #STATE_OFF},
-     * {@link #STATE_TURNING_ON},
-     * {@link #STATE_ON},
-     * {@link #STATE_TURNING_OFF},
-     * @hide
-     */
-    public static final String EXTRA_ALWAYS_ON_STATE =
-            "android.nfc.extra.ALWAYS_ON_STATE";
-
-    /** @hide */
     public static final int HANDOVER_TRANSFER_STATUS_SUCCESS = 0;
     /** @hide */
     public static final int HANDOVER_TRANSFER_STATUS_FAILURE = 1;
@@ -430,6 +418,22 @@
     }
 
     /**
+     * A callback to be invoked when NFC controller always on state changes.
+     * <p>Register your {@code ControllerAlwaysOnStateCallback} implementation with {@link
+     * NfcAdapter#registerControllerAlwaysOnStateCallback} and disable it with {@link
+     * NfcAdapter#unregisterControllerAlwaysOnStateCallback}.
+     * @see #registerControllerAlwaysOnStateCallback
+     * @hide
+     */
+    @SystemApi
+    public interface ControllerAlwaysOnStateCallback {
+        /**
+         * Called on NFC controller always on state changes
+         */
+        void onStateChanged(boolean isEnabled);
+    }
+
+    /**
      * A callback to be invoked when the system successfully delivers your {@link NdefMessage}
      * to another device.
      * @see #setOnNdefPushCompleteCallback
@@ -744,6 +748,7 @@
         mNfcUnlockHandlers = new HashMap<NfcUnlockHandler, INfcUnlockHandler>();
         mTagRemovedListener = null;
         mLock = new Object();
+        mControllerAlwaysOnStateListener = new NfcControllerAlwaysOnStateListener(getService());
     }
 
     /**
@@ -2239,14 +2244,16 @@
     /**
      * Sets NFC controller always on feature.
      * <p>This API is for the NFCC internal state management. It allows to discriminate
-     * the controller function from the NFC function by keeping the NFC Controller on without
+     * the controller function from the NFC function by keeping the NFC controller on without
      * any NFC RF enabled if necessary.
-     * <p>This call is asynchronous. Listen for {@link #ACTION_ALWAYS_ON_STATE_CHANGED}
-     * broadcasts to find out when the operation is complete.
-     * <p>If this returns true, then either NFCC is already on, or
-     * a {@link #ACTION_ALWAYS_ON_STATE_CHANGED} broadcast will be sent to indicate
-     * a state transition.
-     * If this returns false, then there is some problem that prevents an attempt to turn NFCC on.
+     * <p>This call is asynchronous. Register a callback {@link #ControllerAlwaysOnStateCallback}
+     * by {@link #registerControllerAlwaysOnStateCallback} to find out when the operation is
+     * complete.
+     * <p>If this returns true, then either NFCC always on state has been set based on the value,
+     * or a {@link ControllerAlwaysOnStateCallback#onStateChanged(boolean)} will be invoked to
+     * indicate the state change.
+     * If this returns false, then there is some problem that prevents an attempt to turn NFCC
+     * always on.
      * @param value if true the NFCC will be kept on (with no RF enabled if NFC adapter is
      * disabled), if false the NFCC will follow completely the Nfc adapter state.
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
@@ -2284,7 +2291,6 @@
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
      * @hide
      */
-
     @SystemApi
     @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
     public boolean isControllerAlwaysOn() {
@@ -2313,7 +2319,6 @@
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
      * @hide
      */
-
     @SystemApi
     @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
     public boolean isControllerAlwaysOnSupported() {
@@ -2337,4 +2342,39 @@
             return false;
         }
     }
+
+    /**
+     * Register a {@link ControllerAlwaysOnStateCallback} to listen for NFC controller always on
+     * state changes
+     * <p>The provided callback will be invoked by the given {@link Executor}.
+     *
+     * @param executor an {@link Executor} to execute given callback
+     * @param callback user implementation of the {@link ControllerAlwaysOnStateCallback}
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+    public void registerControllerAlwaysOnStateCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull ControllerAlwaysOnStateCallback callback) {
+        mControllerAlwaysOnStateListener.register(executor, callback);
+    }
+
+    /**
+     * Unregister the specified {@link ControllerAlwaysOnStateCallback}
+     * <p>The same {@link ControllerAlwaysOnStateCallback} object used when calling
+     * {@link #registerControllerAlwaysOnStateCallback(Executor, ControllerAlwaysOnStateCallback)}
+     * must be used.
+     *
+     * <p>Callbacks are automatically unregistered when application process goes away
+     *
+     * @param callback user implementation of the {@link ControllerAlwaysOnStateCallback}
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
+    public void unregisterControllerAlwaysOnStateCallback(
+            @NonNull ControllerAlwaysOnStateCallback callback) {
+        mControllerAlwaysOnStateListener.unregister(callback);
+    }
 }
diff --git a/core/java/android/nfc/NfcControllerAlwaysOnStateListener.java b/core/java/android/nfc/NfcControllerAlwaysOnStateListener.java
new file mode 100644
index 0000000..69a9ec7
--- /dev/null
+++ b/core/java/android/nfc/NfcControllerAlwaysOnStateListener.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 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.nfc;
+
+import android.annotation.NonNull;
+import android.nfc.NfcAdapter.ControllerAlwaysOnStateCallback;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class NfcControllerAlwaysOnStateListener extends INfcControllerAlwaysOnStateCallback.Stub {
+    private static final String TAG = "NfcControllerAlwaysOnStateListener";
+
+    private final INfcAdapter mAdapter;
+
+    private final Map<ControllerAlwaysOnStateCallback, Executor> mCallbackMap = new HashMap<>();
+
+    private boolean mCurrentState = false;
+    private boolean mIsRegistered = false;
+
+    public NfcControllerAlwaysOnStateListener(@NonNull INfcAdapter adapter) {
+        mAdapter = adapter;
+    }
+
+    /**
+     * Register a {@link ControllerAlwaysOnStateCallback} with this
+     * {@link NfcControllerAlwaysOnStateListener}
+     *
+     * @param executor an {@link Executor} to execute given callback
+     * @param callback user implementation of the {@link ControllerAlwaysOnStateCallback}
+     */
+    public void register(@NonNull Executor executor,
+            @NonNull ControllerAlwaysOnStateCallback callback) {
+        synchronized (this) {
+            if (mCallbackMap.containsKey(callback)) {
+                return;
+            }
+
+            mCallbackMap.put(callback, executor);
+            if (!mIsRegistered) {
+                try {
+                    mAdapter.registerControllerAlwaysOnStateCallback(this);
+                    mIsRegistered = true;
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to register ControllerAlwaysOnStateListener");
+                }
+            }
+        }
+    }
+
+    /**
+     * Unregister the specified {@link ControllerAlwaysOnStateCallback}
+     *
+     * @param callback user implementation of the {@link ControllerAlwaysOnStateCallback}
+     */
+    public void unregister(@NonNull ControllerAlwaysOnStateCallback callback) {
+        synchronized (this) {
+            if (!mCallbackMap.containsKey(callback)) {
+                return;
+            }
+
+            mCallbackMap.remove(callback);
+
+            if (mCallbackMap.isEmpty() && mIsRegistered) {
+                try {
+                    mAdapter.unregisterControllerAlwaysOnStateCallback(this);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to unregister ControllerAlwaysOnStateListener");
+                }
+                mIsRegistered = false;
+            }
+        }
+    }
+
+    private void sendCurrentState(@NonNull ControllerAlwaysOnStateCallback callback) {
+        synchronized (this) {
+            Executor executor = mCallbackMap.get(callback);
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                executor.execute(() -> callback.onStateChanged(
+                        mCurrentState));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    @Override
+    public void onControllerAlwaysOnStateChanged(boolean isEnabled) {
+        synchronized (this) {
+            mCurrentState = isEnabled;
+            for (ControllerAlwaysOnStateCallback cb : mCallbackMap.keySet()) {
+                sendCurrentState(cb);
+            }
+        }
+    }
+}
+
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index 2d6fa3c..b427df7 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -302,7 +302,7 @@
                     OpUsage usage = new OpUsage(packageName, attributionTag, op, uid,
                             lastAccessTime, isRunning, proxyUsage);
 
-                    Integer packageAttr = usage.getPackageAttrHash();
+                    Integer packageAttr = usage.getPackageIdHash();
                     if (!usages.containsKey(permGroupName)) {
                         ArrayMap<Integer, OpUsage> map = new ArrayMap<>();
                         map.put(packageAttr, usage);
@@ -342,19 +342,19 @@
         }
 
         ArrayMap<Integer, OpUsage> allUsages = new ArrayMap<>();
-        // map of uid -> most recent non-proxy-related usage for that uid.
+        // map of packageName and uid hash -> most recent non-proxy-related usage for that uid.
         ArrayMap<Integer, OpUsage> mostRecentUsages = new ArrayMap<>();
-        // set of all uids involved in a proxy usage
-        ArraySet<Integer> proxyUids = new ArraySet<>();
+        // set of all packages involved in a proxy usage
+        ArraySet<Integer> proxyPackages = new ArraySet<>();
         // map of usage -> list of proxy app labels
         ArrayMap<OpUsage, ArrayList<CharSequence>> proxyLabels = new ArrayMap<>();
         // map of usage.proxy hash -> usage hash, telling us if a usage is a proxy
         ArrayMap<Integer, OpUsage> proxies = new ArrayMap<>();
         for (int i = 0; i < usages.size(); i++) {
             OpUsage usage = usages.get(i);
-            allUsages.put(usage.getPackageAttrHash(), usage);
+            allUsages.put(usage.getPackageIdHash(), usage);
             if (usage.proxy != null) {
-                proxies.put(usage.proxy.getPackageAttrHash(), usage);
+                proxies.put(usage.proxy.getPackageIdHash(), usage);
             }
         }
 
@@ -365,25 +365,27 @@
                 continue;
             }
 
-            int usageAttr = usage.getPackageAttrHash();
+            int usageAttr = usage.getPackageIdHash();
             // If this usage has a proxy, but is not a proxy, it is the end of a chain.
             if (!proxies.containsKey(usageAttr) && usage.proxy != null) {
                 proxyLabels.put(usage, new ArrayList<>());
-                proxyUids.add(usage.uid);
+                proxyPackages.add(usage.getPackageIdHash());
             }
             // If this usage is not by the system, and is more recent than the next-most recent
-            // for it's uid, save it.
-            if (!usage.packageName.equals(SYSTEM_PKG) && (!mostRecentUsages.containsKey(usage.uid)
-                    || usage.lastAccessTime > mostRecentUsages.get(usage.uid).lastAccessTime)) {
-                mostRecentUsages.put(usage.uid, usage);
+            // for it's uid and package name, save it.
+            int usageId = usage.getPackageIdHash();
+            OpUsage lastMostRecent = mostRecentUsages.get(usageId);
+            if (!usage.packageName.equals(SYSTEM_PKG) && (lastMostRecent == null
+                    || usage.lastAccessTime > lastMostRecent.lastAccessTime)) {
+                mostRecentUsages.put(usageId, usage);
             }
         }
 
         // get all the proxy labels
         for (int numStart = 0; numStart < proxyLabels.size(); numStart++) {
             OpUsage start = proxyLabels.keyAt(numStart);
-            // Remove any non-proxy usage for the starting uid
-            mostRecentUsages.remove(start.uid);
+            // Remove any non-proxy usage for the starting package
+            mostRecentUsages.remove(start.getPackageIdHash());
             OpUsage currentUsage = proxyLabels.keyAt(numStart);
             ArrayList<CharSequence> proxyLabelList = proxyLabels.get(currentUsage);
             if (currentUsage == null || proxyLabelList == null) {
@@ -393,8 +395,8 @@
             int maxUsages = allUsages.size();
             while (currentUsage.proxy != null) {
 
-                if (allUsages.containsKey(currentUsage.proxy.getPackageAttrHash())) {
-                    currentUsage = allUsages.get(currentUsage.proxy.getPackageAttrHash());
+                if (allUsages.containsKey(currentUsage.proxy.getPackageIdHash())) {
+                    currentUsage = allUsages.get(currentUsage.proxy.getPackageIdHash());
                 } else {
                     // We are missing the proxy usage. This may be because it's a one-step trusted
                     // proxy. Check if we should show the proxy label, and show it, if so.
@@ -411,12 +413,12 @@
 
 
                 if (currentUsage == null || iterNum == maxUsages
-                        || currentUsage.getPackageAttrHash() == start.getPackageAttrHash()) {
+                        || currentUsage.getPackageIdHash() == start.getPackageIdHash()) {
                     // We have an invalid state, or a cycle, so break
                     break;
                 }
 
-                proxyUids.add(currentUsage.uid);
+                proxyPackages.add(currentUsage.getPackageIdHash());
                 // Don't add an app label for the main app, or the system app
                 if (!currentUsage.packageName.equals(start.packageName)
                         && !currentUsage.packageName.equals(SYSTEM_PKG)) {
@@ -440,9 +442,9 @@
                     proxyLabelList.isEmpty() ? null : formatLabelList(proxyLabelList));
         }
 
-        for (int uid : mostRecentUsages.keySet()) {
-            if (!proxyUids.contains(uid)) {
-                usagesAndLabels.put(mostRecentUsages.get(uid), null);
+        for (int packageHash : mostRecentUsages.keySet()) {
+            if (!proxyPackages.contains(packageHash)) {
+                usagesAndLabels.put(mostRecentUsages.get(packageHash), null);
             }
         }
 
@@ -490,8 +492,8 @@
             return UserHandle.getUserHandleForUid(uid);
         }
 
-        public int getPackageAttrHash() {
-            return Objects.hash(packageName, attributionTag, uid);
+        public int getPackageIdHash() {
+            return Objects.hash(packageName, uid);
         }
 
         @Override
diff --git a/core/java/android/provider/SimPhonebookContract.java b/core/java/android/provider/SimPhonebookContract.java
index 030b863..fb89eb0 100644
--- a/core/java/android/provider/SimPhonebookContract.java
+++ b/core/java/android/provider/SimPhonebookContract.java
@@ -17,13 +17,14 @@
 package android.provider;
 
 import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN;
-import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN_PATH_SEGMENT;
 import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN;
-import static android.provider.SimPhonebookContract.ElementaryFiles.EF_FDN_PATH_SEGMENT;
 import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN;
-import static android.provider.SimPhonebookContract.ElementaryFiles.EF_SDN_PATH_SEGMENT;
+import static android.provider.SimPhonebookContract.ElementaryFiles.PATH_SEGMENT_EF_ADN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.PATH_SEGMENT_EF_FDN;
+import static android.provider.SimPhonebookContract.ElementaryFiles.PATH_SEGMENT_EF_SDN;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.WorkerThread;
@@ -78,11 +79,11 @@
     public static String getEfUriPath(@ElementaryFiles.EfType int efType) {
         switch (efType) {
             case EF_ADN:
-                return EF_ADN_PATH_SEGMENT;
+                return PATH_SEGMENT_EF_ADN;
             case EF_FDN:
-                return EF_FDN_PATH_SEGMENT;
+                return PATH_SEGMENT_EF_FDN;
             case EF_SDN:
-                return EF_SDN_PATH_SEGMENT;
+                return PATH_SEGMENT_EF_SDN;
             default:
                 throw new IllegalArgumentException("Unsupported EfType " + efType);
         }
@@ -109,9 +110,9 @@
      * the phone number can contain at most {@link ElementaryFiles#PHONE_NUMBER_MAX_LENGTH}
      * characters. The {@link SimRecords#NAME} column can contain at most
      * {@link ElementaryFiles#NAME_MAX_LENGTH} bytes when it is encoded for storage on the SIM.
-     * Encoding is done internally and so the name should be provided unencoded but the number of
-     * bytes required to encode it will vary depending on the characters it contains. This length
-     * can be determined by calling
+     * Encoding is done internally and so the name should be provided to these provider APIs as a
+     * Java String but the number of bytes required to encode it for storage will vary depending on
+     * the characters it contains. This length can be determined by calling
      * {@link SimRecords#getEncodedNameLength(ContentResolver, String)}.
      * </p>
      * <h3>Operations </h3>
@@ -308,7 +309,8 @@
          */
         @NonNull
         public static Uri getItemUri(
-                int subscriptionId, @ElementaryFiles.EfType int efType, int recordNumber) {
+                int subscriptionId, @ElementaryFiles.EfType int efType,
+                @IntRange(from = 1) int recordNumber) {
             // Elementary file record indices are 1-based.
             Preconditions.checkArgument(recordNumber > 0, "Invalid recordNumber");
 
@@ -332,6 +334,7 @@
          * @see ElementaryFiles#NAME_MAX_LENGTH
          */
         @WorkerThread
+        @IntRange(from = 0)
         public static int getEncodedNameLength(
                 @NonNull ContentResolver resolver, @NonNull String name) {
             Objects.requireNonNull(name);
@@ -442,12 +445,27 @@
          * methods operating on this Uri will throw UnsupportedOperationException
          */
         public static final int EF_SDN = 3;
-        /** @hide */
-        public static final String EF_ADN_PATH_SEGMENT = "adn";
-        /** @hide */
-        public static final String EF_FDN_PATH_SEGMENT = "fdn";
-        /** @hide */
-        public static final String EF_SDN_PATH_SEGMENT = "sdn";
+        /**
+         * The Uri path segment used to target the ADN elementary file for SimPhonebookProvider
+         * content operations.
+         *
+         * @hide
+         */
+        public static final String PATH_SEGMENT_EF_ADN = "adn";
+        /**
+         * The Uri path segment used to target the FDN elementary file for SimPhonebookProvider
+         * content operations.
+         *
+         * @hide
+         */
+        public static final String PATH_SEGMENT_EF_FDN = "fdn";
+        /**
+         * The Uri path segment used to target the SDN elementary file for SimPhonebookProvider
+         * content operations.
+         *
+         * @hide
+         */
+        public static final String PATH_SEGMENT_EF_SDN = "sdn";
         /** The MIME type of CONTENT_URI providing a directory of ADN-like elementary files. */
         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/sim-elementary-file";
         /** The MIME type of a CONTENT_URI subdirectory of a single ADN-like elementary file. */
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 38945f5..f3a8b5d 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -362,6 +362,18 @@
 
         /**
          * Used to determine the currently configured default SMS package.
+         * <p>
+         * As of Android 11 apps will need specific permission to query other packages. To use
+         * this method an app must include in their AndroidManifest:
+         * <queries>
+         *   <intent>
+         *     <action android:name="android.provider.Telephony.SMS_DELIVER"/>
+         *   </intent>
+         * </queries>
+         * Which will allow them to query packages which declare intent filters that include
+         * the {@link android.provider.Telephony.Sms.Intents#SMS_DELIVER_ACTION} intent.
+         * </p>
+         *
          * @param context context of the requesting application
          * @return package name for the default SMS package or null
          */
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 78e5eab..7e8622a 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -2427,9 +2427,8 @@
                     @Override
                     public void onError(String errorInfo) {
                         Log.w(TAG, "System TTS connection error: " + errorInfo);
-                        // The connection was not established successfully - handle as
-                        // disconnection: clear the state and notify the user.
-                        onServiceDisconnected(/* componentName= */ null);
+                        // There is an error connecting to the engine - notify the listener.
+                        dispatchOnInit(ERROR);
                     }
                 });
 
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 49065aa..a1ffe34 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -110,6 +110,7 @@
      * @deprecated Use {@link TelephonyCallback.MessageWaitingIndicatorListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public static final int LISTEN_MESSAGE_WAITING_INDICATOR                = 0x00000004;
 
     /**
@@ -123,6 +124,7 @@
      * @deprecated Use {@link TelephonyCallback.CallForwardingIndicatorListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public static final int LISTEN_CALL_FORWARDING_INDICATOR                = 0x00000008;
 
     /**
@@ -141,6 +143,7 @@
      * @deprecated Use {@link TelephonyCallback.CellLocationListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     public static final int LISTEN_CELL_LOCATION                            = 0x00000010;
 
     /**
@@ -194,7 +197,7 @@
      * @see #onSignalStrengthsChanged
      *
      * @hide
-     * @deprecated Use {@link TelephonyCallback.AlwaysReportedSignalStrengthListener}
+     * @deprecated Use TelephonyManager#setSignalStrengthUpdateRequest
      * instead.
      */
     @Deprecated
@@ -204,13 +207,18 @@
     /**
      * Listen for changes to observed cell info.
      *
-     * Listening to this event requires the {@link Manifest.permission#ACCESS_FINE_LOCATION}
+     * Listening to this event requires the {@link Manifest.permission#READ_PHONE_STATE} and
+     * {@link Manifest.permission#ACCESS_FINE_LOCATION}
      * permission.
      *
      * @see #onCellInfoChanged
      * @deprecated Use {@link TelephonyCallback.CellInfoListener} instead.
      */
     @Deprecated
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     public static final int LISTEN_CELL_INFO = 0x00000400;
 
     /**
@@ -261,7 +269,7 @@
      *
      * <p>Requires permission {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
      *
-     * @see #onServiceStateChanged(ServiceState)
+     * @see #onSrvccStateChanged
      * @hide
      * @deprecated Use {@link TelephonyCallback.SrvccStateListener} instead.
      */
@@ -376,6 +384,7 @@
      *  @deprecated Use {@link TelephonyCallback.ActiveDataSubscriptionIdListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 0x00400000;
 
     /**
@@ -399,6 +408,7 @@
      * @deprecated Use {@link TelephonyCallback.EmergencyNumberListListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public static final int LISTEN_EMERGENCY_NUMBER_LIST                   = 0x01000000;
 
     /**
@@ -487,7 +497,10 @@
      * @deprecated Use {@link TelephonyCallback.RegistrationFailedListener} instead.
      */
     @Deprecated
-    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     public static final int LISTEN_REGISTRATION_FAILURE = 0x40000000;
 
     /**
@@ -503,7 +516,10 @@
      * @deprecated Use {@link TelephonyCallback.BarringInfoListener} instead.
      */
     @Deprecated
-    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     public static final int LISTEN_BARRING_INFO = 0x80000000;
 
     /*
@@ -650,6 +666,7 @@
      * @deprecated Use {@link TelephonyCallback.MessageWaitingIndicatorListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public void onMessageWaitingIndicatorChanged(boolean mwi) {
         // default implementation empty
     }
@@ -666,6 +683,7 @@
      * @deprecated Use {@link TelephonyCallback.CallForwardingIndicatorListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public void onCallForwardingIndicatorChanged(boolean cfi) {
         // default implementation empty
     }
@@ -682,6 +700,7 @@
      * @deprecated Use {@link TelephonyCallback.CellLocationListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     public void onCellLocationChanged(CellLocation location) {
         // default implementation empty
     }
@@ -801,6 +820,10 @@
      * @param cellInfo is the list of currently visible cells.
      * @deprecated Use {@link TelephonyCallback.CellInfoListener} instead.
      */
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     @Deprecated
     public void onCellInfoChanged(List<CellInfo> cellInfo) {
         // default implementation empty
@@ -875,14 +898,14 @@
      * subId. Otherwise, this callback applies to
      * {@link SubscriptionManager#getDefaultSubscriptionId()}.
      *
-     * <p>Requires permission {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
      * or the calling app has carrier privileges
      * (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @param dataConnectionState {@link PreciseDataConnectionState}
      * @deprecated Use {@link TelephonyCallback.PreciseDataConnectionStateListener} instead.
      */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
     @Deprecated
     public void onPreciseDataConnectionStateChanged(
             @NonNull PreciseDataConnectionState dataConnectionState) {
@@ -924,6 +947,7 @@
      */
     @SystemApi
     @Deprecated
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void onSrvccStateChanged(@SrvccState int srvccState) {
         // default implementation empty
     }
@@ -944,6 +968,7 @@
      */
     @SystemApi
     @Deprecated
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void onVoiceActivationStateChanged(@SimActivationState int state) {
         // default implementation empty
     }
@@ -1026,6 +1051,7 @@
      * @deprecated Use {@link TelephonyCallback.EmergencyNumberListListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public void onEmergencyNumberListChanged(
             @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList) {
         // default implementation empty
@@ -1043,6 +1069,7 @@
      */
     @SystemApi
     @Deprecated
+    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber) {
         // default implementation empty
     }
@@ -1068,6 +1095,7 @@
      */
     @SystemApi
     @Deprecated
+    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
             int subscriptionId) {
         // Default implementation for backwards compatibility
@@ -1086,6 +1114,7 @@
      */
     @SystemApi
     @Deprecated
+    @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
         // default implementation empty
     }
@@ -1158,6 +1187,7 @@
      * @deprecated Use {@link TelephonyCallback.ActiveDataSubscriptionIdListener} instead.
      */
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public void onActiveDataSubscriptionIdChanged(int subId) {
         // default implementation empty
     }
@@ -1178,6 +1208,7 @@
      */
     @SystemApi
     @Deprecated
+    @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
     public void onCallAttributesChanged(@NonNull CallAttributes callAttributes) {
         // default implementation empty
     }
@@ -1199,6 +1230,7 @@
      */
     @SystemApi
     @Deprecated
+    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public void onRadioPowerStateChanged(@RadioPowerState int state) {
         // default implementation empty
     }
@@ -1253,6 +1285,10 @@
      * @deprecated Use {@link TelephonyCallback.RegistrationFailedListener} instead.
      */
     @Deprecated
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
             int domain, int causeCode, int additionalCauseCode) {
         // default implementation empty
@@ -1269,6 +1305,10 @@
      * @deprecated Use {@link TelephonyCallback.BarringInfoListener} instead.
      */
     @Deprecated
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PRECISE_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })
     public void onBarringInfoChanged(@NonNull BarringInfo barringInfo) {
         // default implementation empty
     }
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index 1ab6e0f..1a25c8b 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -218,10 +218,9 @@
      * even in some situations such as the screen of the device is off.
      *
      * @hide
-     * @see AlwaysReportedSignalStrengthListener#onSignalStrengthsChanged
+     * @see TelephonyManager#setSignalStrengthUpdateRequest
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
     public static final int EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED = 10;
 
     /**
@@ -231,8 +230,10 @@
      * @see CellInfoListener#onCellInfoChanged
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
-    public static final int EVENT_CELL_INFO_CHANGED = 11;
+    @RequiresPermission(allOf = {
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ACCESS_FINE_LOCATION
+    })    public static final int EVENT_CELL_INFO_CHANGED = 11;
 
     /**
      * Event for {@link android.telephony.Annotation.PreciseCallStates} of ringing,
@@ -369,9 +370,10 @@
 
     /**
      * Event for changes to active data subscription ID. Active data subscription is
-     * the current subscription used to setup Cellular Internet data. For example,
-     * it could be the current active opportunistic subscription in use, or the
-     * subscription user selected as default data subscription in DSDS mode.
+     * the current subscription used to setup Cellular Internet data. The data is only active on the
+     * subscription at a time, even it is multi-SIM mode. For example, it could be the current
+     * active opportunistic subscription in use, or the subscription user selected as default data
+     * subscription in DSDS mode.
      *
      * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
      * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
@@ -668,8 +670,7 @@
          * @see ServiceState#STATE_OUT_OF_SERVICE
          * @see ServiceState#STATE_POWER_OFF
          */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onServiceStateChanged(@NonNull ServiceState serviceState);
+        void onServiceStateChanged(@NonNull ServiceState serviceState);
     }
 
     /**
@@ -687,7 +688,7 @@
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          */
         @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onMessageWaitingIndicatorChanged(boolean mwi);
+        void onMessageWaitingIndicatorChanged(boolean mwi);
     }
 
     /**
@@ -706,7 +707,7 @@
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          */
         @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onCallForwardingIndicatorChanged(boolean cfi);
+        void onCallForwardingIndicatorChanged(boolean cfi);
     }
 
     /**
@@ -724,7 +725,7 @@
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          */
         @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
-        public void onCellLocationChanged(@NonNull CellLocation location);
+        void onCellLocationChanged(@NonNull CellLocation location);
     }
 
     /**
@@ -753,7 +754,7 @@
          * @param state the current call state
          */
         @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onCallStateChanged(@Annotation.CallState int state);
+        void onCallStateChanged(@Annotation.CallState int state);
     }
 
     /**
@@ -777,9 +778,8 @@
          * @see TelephonyManager#DATA_CONNECTED
          * @see TelephonyManager#DATA_SUSPENDED
          */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onDataConnectionStateChanged(@TelephonyManager.DataState int state,
-            @Annotation.NetworkType int networkType);
+        void onDataConnectionStateChanged(@TelephonyManager.DataState int state,
+                @Annotation.NetworkType int networkType);
     }
 
     /**
@@ -802,8 +802,7 @@
          * @see TelephonyManager#DATA_ACTIVITY_INOUT
          * @see TelephonyManager#DATA_ACTIVITY_DORMANT
          */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onDataActivity(@Annotation.DataActivityType int direction);
+        void onDataActivity(@Annotation.DataActivityType int direction);
     }
 
     /**
@@ -820,27 +819,7 @@
          * subscription ID. Otherwise, this callback applies to
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
-    }
-
-    /**
-     * Interface for network signal strengths callback which always reported from modem.
-     */
-    public interface AlwaysReportedSignalStrengthListener {
-        /**
-         * Callback always invoked from modem when network signal strengths changes on the
-         * registered subscription.
-         * Note, the registration subscription ID comes from {@link TelephonyManager} object
-         * which registers TelephonyCallback by
-         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}.
-         * If this TelephonyManager object was created with
-         * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-         * subscription ID. Otherwise, this callback applies to
-         * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-         */
-        @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
-        public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
+        void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength);
     }
 
     /**
@@ -860,8 +839,11 @@
          *
          * @param cellInfo is the list of currently visible cells.
          */
-        @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
-        public void onCellInfoChanged(@NonNull List<CellInfo> cellInfo);
+        @RequiresPermission(allOf = {
+                Manifest.permission.READ_PHONE_STATE,
+                Manifest.permission.ACCESS_FINE_LOCATION
+        })
+        void onCellInfoChanged(@NonNull List<CellInfo> cellInfo);
     }
 
     /**
@@ -884,7 +866,7 @@
          * @param callState {@link PreciseCallState}
          */
         @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onPreciseCallStateChanged(@NonNull PreciseCallState callState);
+        void onPreciseCallStateChanged(@NonNull PreciseCallState callState);
     }
 
     /**
@@ -905,8 +887,8 @@
          * @param preciseDisconnectCause {@link PreciseDisconnectCause}.
          */
         @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onCallDisconnectCauseChanged(@Annotation.DisconnectCauses int disconnectCause,
-            @Annotation.PreciseDisconnectCauses int preciseDisconnectCause);
+        void onCallDisconnectCauseChanged(@Annotation.DisconnectCauses int disconnectCause,
+                @Annotation.PreciseDisconnectCauses int preciseDisconnectCause);
     }
 
     /**
@@ -926,7 +908,7 @@
          * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
          */
         @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo);
+        void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo);
     }
 
     /**
@@ -952,7 +934,7 @@
          * @param dataConnectionState {@link PreciseDataConnectionState}
          */
         @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onPreciseDataConnectionStateChanged(
+        void onPreciseDataConnectionStateChanged(
             @NonNull PreciseDataConnectionState dataConnectionState);
     }
 
@@ -976,7 +958,7 @@
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          */
         @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        public void onSrvccStateChanged(@Annotation.SrvccState int srvccState);
+        void onSrvccStateChanged(@Annotation.SrvccState int srvccState);
     }
 
     /**
@@ -1000,7 +982,7 @@
          * @param state is the current SIM voice activation state
          */
         @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        public void onVoiceActivationStateChanged(@Annotation.SimActivationState int state);
+        void onVoiceActivationStateChanged(@Annotation.SimActivationState int state);
 
     }
 
@@ -1021,8 +1003,7 @@
          *
          * @param state is the current SIM data activation state
          */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onDataActivationStateChanged(@Annotation.SimActivationState int state);
+        void onDataActivationStateChanged(@Annotation.SimActivationState int state);
     }
 
     /**
@@ -1043,8 +1024,7 @@
          * @param enabled indicates whether the current user mobile data state is enabled or
          *                disabled.
          */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onUserMobileDataStateChanged(boolean enabled);
+        void onUserMobileDataStateChanged(boolean enabled);
     }
 
     /**
@@ -1058,7 +1038,7 @@
          *
          * @param telephonyDisplayInfo The display information.
          */
-        public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo);
+        void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo);
     }
 
     /**
@@ -1089,8 +1069,8 @@
          *                            empty.
          */
         @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onEmergencyNumberListChanged(
-            @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList);
+        void onEmergencyNumberListChanged(@NonNull Map<Integer,
+                List<EmergencyNumber>> emergencyNumberList);
     }
 
     /**
@@ -1118,8 +1098,8 @@
          *                              {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
          */
         @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
-        public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
-            int subscriptionId);
+        void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
+                int subscriptionId);
     }
 
     /**
@@ -1139,8 +1119,8 @@
          * @param subscriptionId      The subscription ID used to send the emergency sms.
          */
         @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
-        public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
-            int subscriptionId);
+        void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber,
+                int subscriptionId);
     }
 
     /**
@@ -1156,8 +1136,7 @@
          *
          * @param capability the new phone capability
          */
-        @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-        public void onPhoneCapabilityChanged(@NonNull PhoneCapability capability);
+        void onPhoneCapabilityChanged(@NonNull PhoneCapability capability);
     }
 
     /**
@@ -1168,13 +1147,14 @@
          * Callback invoked when active data subscription ID changes.
          * Note, this callback triggers regardless of registered subscription.
          *
-         * @param subId current subscription used to setup Cellular Internet data.
+         * @param subId current subscription used to setup Cellular Internet data. The data is
+         *              only active on the subscription at a time, even it is multi-SIM mode.
          *              For example, it could be the current active opportunistic subscription
          *              in use, or the subscription user selected as default data subscription in
          *              DSDS mode.
          */
         @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-        public void onActiveDataSubscriptionIdChanged(int subId);
+        void onActiveDataSubscriptionIdChanged(int subId);
     }
 
     /**
@@ -1197,7 +1177,7 @@
          * @param state the modem radio power state
          */
         @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        public void onRadioPowerStateChanged(@Annotation.RadioPowerState int state);
+        void onRadioPowerStateChanged(@Annotation.RadioPowerState int state);
     }
 
     /**
@@ -1221,7 +1201,7 @@
          * @param active If the carrier network change is or shortly will be active,
          *               {@code true} indicate that showing alternative UI, {@code false} otherwise.
          */
-        public void onCarrierNetworkChange(boolean active);
+        void onCarrierNetworkChange(boolean active);
     }
 
     /**
@@ -1263,9 +1243,8 @@
                 Manifest.permission.READ_PRECISE_PHONE_STATE,
                 Manifest.permission.ACCESS_FINE_LOCATION
         })
-        public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
-            @NonNull String chosenPlmn, @NetworkRegistrationInfo.Domain int domain, int causeCode,
-            int additionalCauseCode);
+        void onRegistrationFailed(@NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
+                @NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode);
     }
 
     /**
@@ -1303,8 +1282,7 @@
          * long type value}.
          */
         @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        void onAllowedNetworkTypesChanged(
-                @TelephonyManager.AllowedNetworkTypesReason int reason,
+        void onAllowedNetworkTypesChanged(@TelephonyManager.AllowedNetworkTypesReason int reason,
                 @TelephonyManager.NetworkTypeBitMask long allowedNetworkType);
     }
 
@@ -1348,7 +1326,7 @@
                 Manifest.permission.READ_PRECISE_PHONE_STATE,
                 Manifest.permission.ACCESS_FINE_LOCATION
         })
-        public void onBarringInfoChanged(@NonNull BarringInfo barringInfo);
+        void onBarringInfoChanged(@NonNull BarringInfo barringInfo);
     }
 
     /**
@@ -1361,7 +1339,7 @@
          * @param configs List of the current {@link PhysicalChannelConfig}s
          */
         @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs);
+        void onPhysicalChannelConfigChanged(@NonNull List<PhysicalChannelConfig> configs);
     }
 
     /**
@@ -1379,8 +1357,7 @@
          *                See {@link TelephonyManager.DataEnabledReason}.
          */
         @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
-        public void onDataEnabledChanged(boolean enabled,
-            @TelephonyManager.DataEnabledReason int reason);
+        void onDataEnabledChanged(boolean enabled, @TelephonyManager.DataEnabledReason int reason);
     }
 
     /**
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 1ec12fe..340fa40 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -894,10 +894,6 @@
             eventList.add(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED);
         }
 
-        if (telephonyCallback instanceof TelephonyCallback.AlwaysReportedSignalStrengthListener) {
-            eventList.add(TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED);
-        }
-
         if (telephonyCallback instanceof TelephonyCallback.CellInfoListener) {
             eventList.add(TelephonyCallback.EVENT_CELL_INFO_CHANGED);
         }
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 36be9f8..8e5f905 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -377,7 +377,7 @@
                 && ownerUid == other.ownerUid
                 && Objects.equals(ownerPackageName, other.ownerPackageName)
                 && removeMode == other.removeMode
-                && refreshRateOverride == other.refreshRateOverride
+                && getRefreshRate() == other.getRefreshRate()
                 && brightnessMinimum == other.brightnessMinimum
                 && brightnessMaximum == other.brightnessMaximum
                 && brightnessDefault == other.brightnessDefault
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 82106b0..2b96a14 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -33,7 +33,6 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.RenderNode;
@@ -670,10 +669,16 @@
             if (mClipSurfaceToBounds && mClipBounds != null) {
                 mTmpRect.intersect(mClipBounds);
             }
-            canvas.drawRoundRect(mTmpRect.left, mTmpRect.top, mTmpRect.right, mTmpRect.bottom,
-                    mCornerRadius, mCornerRadius, mRoundedViewportPaint);
+            canvas.punchHole(
+                    mTmpRect.left,
+                    mTmpRect.top,
+                    mTmpRect.right,
+                    mTmpRect.bottom,
+                    mCornerRadius,
+                    mCornerRadius
+            );
         } else {
-            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+            canvas.punchHole(0f, 0f, getWidth(), getHeight(), 0f, 0f);
         }
     }
 
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index ab46170..1c15bbc 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -627,7 +627,7 @@
 
     /**
      * Integer argument specifying the end index of the requested text location data. Must be
-     * positive.
+     * positive and no larger than {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH}.
      *
      * @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
      */
@@ -635,6 +635,11 @@
             "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
 
     /**
+     * The maximum allowed length of the requested text location data.
+     */
+    public static final int EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH = 20000;
+
+    /**
      * Key used to request extra data for the rendering information.
      * The key requests that a {@link AccessibilityNodeInfo.ExtraRenderingInfo} be added to this
      * info. This request is made with {@link #refreshWithExtraData(String, Bundle)} without
@@ -1038,6 +1043,14 @@
      * recycled).
      */
     public boolean refreshWithExtraData(String extraDataKey, Bundle args) {
+        // limits the text location length to make sure the rectangle array allocation avoids
+        // the binder transaction failure and OOM crash.
+        if (args.getInt(EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH, -1)
+                > EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH) {
+            args.putInt(EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH,
+                    EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_MAX_LENGTH);
+        }
+
         args.putString(EXTRA_DATA_REQUESTED_KEY, extraDataKey);
         return refresh(args, true);
     }
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
index d0ab004..1cb6825 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
@@ -60,4 +60,11 @@
      */
     void onPerformScaleAction(int displayId, float scale);
 
+    /**
+     * Called when the accessibility action is performed.
+     *
+     * @param displayId The logical display id.
+     */
+    void onAccessibilityActionPerformed(int displayId);
+
 }
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 29c78b5..f49aa74 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -3263,16 +3263,14 @@
 
     /**
      * Create a new RemoteViews object that will display the views contained
-     * in the specified layout file.
+     * in the specified layout file and change the id of the root view to the specified one.
      *
-     * @param packageName Name of the package that contains the layout resource.
-     * @param userId The user under which the package is running.
-     * @param layoutId The id of the layout resource.
-     *
-     * @hide
+     * @param packageName Name of the package that contains the layout resource
+     * @param layoutId The id of the layout resource
      */
-    public RemoteViews(String packageName, int userId, @LayoutRes int layoutId) {
-        this(getApplicationInfo(packageName, userId), layoutId);
+    public RemoteViews(@NonNull String packageName, @LayoutRes int layoutId, @IdRes int viewId) {
+        this(packageName, layoutId);
+        this.mViewId = viewId;
     }
 
     /**
@@ -3727,7 +3725,7 @@
      * The {@code stableId} will be used to identify a potential view to recycled when the remote
      * view is inflated. Views can be re-used if inserted in the same order, potentially with
      * some views appearing / disappearing. To be recycled the view must not change the layout
-     * used to inflate it or its view id (see {@link RemoteViews#setViewId}).
+     * used to inflate it or its view id (see {@link RemoteViews#RemoteViews(String, int, int)}).
      *
      * Note: if a view is re-used, all the actions will be re-applied on it. However, its properties
      * are not reset, so what was applied in previous round will have an effect. As a view may be
@@ -6340,25 +6338,9 @@
     }
 
     /**
-     * Set the ID of the top-level view of the XML layout.
-     *
-     * The view's ID is changed right after inflation, before it gets added to its parent. The ID
-     * of a given view can never change after the initial inflation.
-     *
-     * Set to {@link View#NO_ID} to reset and simply keep the id defined in the XML layout.
-     *
-     * @throws UnsupportedOperationException if the method is called on a RemoteViews defined in
-     * term of other RemoteViews (e.g. {@link #RemoteViews(RemoteViews, RemoteViews)}).
+     * Get the ID of the top-level view of the XML layout, if set using
+     * {@link RemoteViews#RemoteViews(String, int, int)}.
      */
-    public void setViewId(@IdRes int viewId) {
-        if (hasMultipleLayouts()) {
-            throw new UnsupportedOperationException(
-                    "The viewId can only be set on RemoteViews defined from a XML layout.");
-        }
-        mViewId = viewId;
-    }
-
-    /** Get the ID of the top-level view of the XML layout, as set by {@link #setViewId}. */
     public @IdRes int getViewId() {
         return mViewId;
     }
diff --git a/core/java/android/window/SplashScreen.java b/core/java/android/window/SplashScreen.java
index 4b88a9b..18f29ae 100644
--- a/core/java/android/window/SplashScreen.java
+++ b/core/java/android/window/SplashScreen.java
@@ -17,8 +17,8 @@
 package android.window;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.SuppressLint;
+import android.annotation.UiThread;
 import android.app.Activity;
 import android.app.ActivityThread;
 import android.content.Context;
@@ -52,7 +52,13 @@
      * @see OnExitAnimationListener#onSplashScreenExit(SplashScreenView)
      */
     @SuppressLint("ExecutorRegistration")
-    void setOnExitAnimationListener(@Nullable SplashScreen.OnExitAnimationListener listener);
+    void setOnExitAnimationListener(@NonNull SplashScreen.OnExitAnimationListener listener);
+
+    /**
+     * Clear exist listener
+     * @see #setOnExitAnimationListener
+     */
+    void clearOnExitAnimationListener();
 
     /**
      * Listens for the splash screen exit event.
@@ -63,15 +69,14 @@
          * of the activity. The {@link SplashScreenView} represents the splash screen view
          * object, developer can make an exit animation based on this view.</p>
          *
-         * <p>If {@link SplashScreenView#remove} is not called after 5000ms, the method will be
-         * automatically called and the splash screen removed.</p>
-         *
-         * <p>This method is never invoked if your activity sets
-         * {@link #setOnExitAnimationListener} to <code>null</code>..
+         * <p>This method is never invoked if your activity clear the listener by
+         * {@link #clearOnExitAnimationListener}.
          *
          * @param view The view object which on top of this Activity.
          * @see #setOnExitAnimationListener
+         * @see #clearOnExitAnimationListener
          */
+        @UiThread
         void onSplashScreenExit(@NonNull SplashScreenView view);
     }
 
@@ -90,20 +95,30 @@
 
         @Override
         public void setOnExitAnimationListener(
-                @Nullable SplashScreen.OnExitAnimationListener listener) {
+                @NonNull SplashScreen.OnExitAnimationListener listener) {
             if (mActivityToken == null) {
                 // This is not an activity.
                 return;
             }
             synchronized (mGlobal.mGlobalLock) {
-                mExitAnimationListener = listener;
                 if (listener != null) {
+                    mExitAnimationListener = listener;
                     mGlobal.addImpl(this);
-                } else {
-                    mGlobal.removeImpl(this);
                 }
             }
         }
+
+        @Override
+        public void clearOnExitAnimationListener() {
+            if (mActivityToken == null) {
+                // This is not an activity.
+                return;
+            }
+            synchronized (mGlobal.mGlobalLock) {
+                mExitAnimationListener = null;
+                mGlobal.removeImpl(this);
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 9789d70..933760c 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -22,6 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.annotation.UiThread;
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Bitmap;
@@ -32,7 +33,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.SystemClock;
 import android.os.Trace;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -46,7 +46,8 @@
 import com.android.internal.R;
 import com.android.internal.policy.DecorView;
 
-import java.util.function.Consumer;
+import java.time.Duration;
+import java.time.Instant;
 
 /**
  * <p>The view which allows an activity to customize its splash screen exit animation.</p>
@@ -75,8 +76,8 @@
     private Bitmap mParceledIconBitmap;
     private View mBrandingImageView;
     private Bitmap mParceledBrandingBitmap;
-    private long mIconAnimationDuration;
-    private long mIconAnimationStart;
+    private Duration mIconAnimationDuration;
+    private Instant mIconAnimationStart;
 
     // The host activity when transfer view to it.
     private Activity mHostActivity;
@@ -85,6 +86,7 @@
     private boolean mDrawBarBackground;
     private int mStatusBarColor;
     private int mNavigationBarColor;
+    private boolean mHasRemoved;
 
     /**
      * Internal builder to create a SplashScreenView object.
@@ -101,8 +103,8 @@
         private int mBrandingImageHeight;
         private Drawable mBrandingDrawable;
         private Bitmap mParceledBrandingBitmap;
-        private long mIconAnimationStart;
-        private long mIconAnimationDuration;
+        private Instant mIconAnimationStart;
+        private Duration mIconAnimationDuration;
 
         public Builder(@NonNull Context context) {
             mContext = context;
@@ -126,8 +128,8 @@
                         parcelable.mBrandingHeight);
                 mParceledBrandingBitmap = parcelable.mBrandingBitmap;
             }
-            mIconAnimationStart = parcelable.mIconAnimationStart;
-            mIconAnimationDuration = parcelable.mIconAnimationDuration;
+            mIconAnimationStart = Instant.ofEpochMilli(parcelable.mIconAnimationStartMillis);
+            mIconAnimationDuration = Duration.ofMillis(parcelable.mIconAnimationDurationMillis);
             return this;
         }
 
@@ -166,8 +168,8 @@
         /**
          * Set the animation duration if icon is animatable.
          */
-        public Builder setAnimationDuration(int duration) {
-            mIconAnimationDuration = duration;
+        public Builder setAnimationDurationMillis(int duration) {
+            mIconAnimationDuration = Duration.ofMillis(duration);
             return this;
         }
 
@@ -203,7 +205,8 @@
             }
             if (mIconDrawable != null) {
                 view.mIconView.setBackground(mIconDrawable);
-                view.initIconAnimation(mIconDrawable, mIconAnimationDuration);
+                view.initIconAnimation(mIconDrawable,
+                        mIconAnimationDuration != null ? mIconAnimationDuration.toMillis() : 0);
             }
             view.mIconAnimationStart = mIconAnimationStart;
             view.mIconAnimationDuration = mIconAnimationDuration;
@@ -266,15 +269,16 @@
      * @see android.R.attr#windowSplashScreenAnimatedIcon
      * @see android.R.attr#windowSplashScreenAnimationDuration
      */
-    public long getIconAnimationDurationMillis() {
+    @Nullable
+    public Duration getIconAnimationDuration() {
         return mIconAnimationDuration;
     }
 
     /**
-     * If the replaced icon is animatable, return the animation start time in millisecond based on
-     * system. The start time is set using {@link SystemClock#uptimeMillis()}.
+     * If the replaced icon is animatable, return the animation start time based on system clock.
      */
-    public long getIconAnimationStartMillis() {
+    @Nullable
+    public Instant getIconAnimationStart() {
         return mIconAnimationStart;
     }
 
@@ -286,8 +290,8 @@
         aniDrawable.prepareAnimate(duration, this::animationStartCallback);
     }
 
-    private void animationStartCallback(long startAt) {
-        mIconAnimationStart = startAt;
+    private void animationStartCallback() {
+        mIconAnimationStart = Instant.now();
     }
 
     /**
@@ -295,7 +299,11 @@
      * <p><strong>Do not</strong> invoke this method from a drawing method
      * ({@link #onDraw(android.graphics.Canvas)} for instance).</p>
      */
+    @UiThread
     public void remove() {
+        if (mHasRemoved) {
+            return;
+        }
         setVisibility(GONE);
         if (mParceledIconBitmap != null) {
             mIconView.setBackground(null);
@@ -321,6 +329,7 @@
         if (mHostActivity != null) {
             mHostActivity.detachSplashScreenView();
         }
+        mHasRemoved = true;
     }
 
     /**
@@ -414,7 +423,7 @@
          * @param startListener The callback listener used to receive the start of the animation.
          * @return true if this drawable object can also be animated and it can be played now.
          */
-        protected boolean prepareAnimate(long duration, Consumer<Long> startListener) {
+        protected boolean prepareAnimate(long duration, Runnable startListener) {
             return false;
         }
     }
@@ -433,8 +442,8 @@
         private int mBrandingHeight;
         private Bitmap mBrandingBitmap;
 
-        private long mIconAnimationStart;
-        private long mIconAnimationDuration;
+        private long mIconAnimationStartMillis;
+        private long mIconAnimationDurationMillis;
 
         public SplashScreenViewParcelable(SplashScreenView view) {
             ViewGroup.LayoutParams params = view.getIconView().getLayoutParams();
@@ -447,8 +456,12 @@
             mBrandingWidth = params.width;
             mBrandingHeight = params.height;
 
-            mIconAnimationStart = view.getIconAnimationStartMillis();
-            mIconAnimationDuration = view.getIconAnimationDurationMillis();
+            if (view.getIconAnimationStart() != null) {
+                mIconAnimationStartMillis = view.getIconAnimationStart().toEpochMilli();
+            }
+            if (view.getIconAnimationDuration() != null) {
+                mIconAnimationDurationMillis = view.getIconAnimationDuration().toMillis();
+            }
         }
 
         private Bitmap copyDrawable(Drawable drawable) {
@@ -479,8 +492,8 @@
             mBrandingWidth = source.readInt();
             mBrandingHeight = source.readInt();
             mBrandingBitmap = source.readTypedObject(Bitmap.CREATOR);
-            mIconAnimationStart = source.readLong();
-            mIconAnimationDuration = source.readLong();
+            mIconAnimationStartMillis = source.readLong();
+            mIconAnimationDurationMillis = source.readLong();
             mIconBackground = source.readInt();
         }
 
@@ -497,8 +510,8 @@
             dest.writeInt(mBrandingWidth);
             dest.writeInt(mBrandingHeight);
             dest.writeTypedObject(mBrandingBitmap, flags);
-            dest.writeLong(mIconAnimationStart);
-            dest.writeLong(mIconAnimationDuration);
+            dest.writeLong(mIconAnimationStartMillis);
+            dest.writeLong(mIconAnimationDurationMillis);
             dest.writeInt(mIconBackground);
         }
 
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index c7672dc..08bb1a9 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -50,12 +50,19 @@
      */
     public static final int STARTING_WINDOW_TYPE_SNAPSHOT = 2;
     /**
+     * Prefer empty splash screen starting window.
+     * @hide
+     */
+    public static final int STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN = 3;
+
+    /**
      * @hide
      */
     @IntDef(flag = true, prefix = "STARTING_WINDOW_TYPE_", value = {
             STARTING_WINDOW_TYPE_NONE,
             STARTING_WINDOW_TYPE_SPLASH_SCREEN,
-            STARTING_WINDOW_TYPE_SNAPSHOT
+            STARTING_WINDOW_TYPE_SNAPSHOT,
+            STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN
     })
     public @interface StartingWindowType {}
 
@@ -95,7 +102,8 @@
             TYPE_PARAMETER_TASK_SWITCH,
             TYPE_PARAMETER_PROCESS_RUNNING,
             TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT,
-            TYPE_PARAMETER_ACTIVITY_CREATED
+            TYPE_PARAMETER_ACTIVITY_CREATED,
+            TYPE_PARAMETER_SAME_PACKAGE
     })
     public @interface StartingTypeParams {}
 
@@ -112,6 +120,8 @@
     public static final int TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT = 0x00000008;
     /** @hide */
     public static final int TYPE_PARAMETER_ACTIVITY_CREATED = 0x00000010;
+    /** @hide */
+    public static final int TYPE_PARAMETER_SAME_PACKAGE = 0x00000020;
 
     /**
      * The parameters which effect the starting window type.
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index fffeb02..5a5e745 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -116,7 +116,6 @@
     KeyphraseMetadata getEnrolledKeyphraseMetadata(String keyphrase, String bcp47Locale);
     /**
      * @return the component name for the currently active voice interaction service
-     * @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
      */
     ComponentName getActiveServiceComponentName();
 
diff --git a/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java b/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java
index 7bd7acf..1fc126e 100644
--- a/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java
+++ b/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java
@@ -24,7 +24,9 @@
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.Paint;
+import android.graphics.Path;
 import android.graphics.PixelFormat;
+import android.graphics.Rect;
 import android.graphics.Xfermode;
 import android.graphics.drawable.Drawable;
 import android.view.animation.DecelerateInterpolator;
@@ -44,6 +46,9 @@
     private int mMainColor;
     private ValueAnimator mColorAnimation;
     private int mMainColorTo;
+    private float mCornerRadius;
+    private Rect mBounds;
+    private ConcaveInfo mConcaveInfo;
 
     public ScrimDrawable() {
         mPaint = new Paint();
@@ -127,15 +132,67 @@
         return PixelFormat.TRANSLUCENT;
     }
 
+    /**
+     * Enable drawable shape to have rounded corners with provided radius
+     */
+    public void setRoundedCorners(float radius) {
+        mCornerRadius = radius;
+    }
+
+    /**
+     * Make bottom edge concave with provided corner radius
+     */
+    public void setBottomEdgeConcave(float radius) {
+        // only rounding top corners for clip out path
+        float[] cornerRadii = new float[]{radius, radius, radius, radius, 0, 0, 0, 0};
+        mConcaveInfo = new ConcaveInfo(radius, cornerRadii);
+    }
+
     @Override
     public void draw(@NonNull Canvas canvas) {
         mPaint.setColor(mMainColor);
         mPaint.setAlpha(mAlpha);
-        canvas.drawRect(getBounds(), mPaint);
+        if (mConcaveInfo != null) {
+            drawConcave(canvas);
+        }
+        canvas.drawRoundRect(getBounds().left, getBounds().top, getBounds().right,
+                getBounds().bottom + mCornerRadius,
+                /* x radius*/ mCornerRadius, /* y radius*/ mCornerRadius, mPaint);
+    }
+
+    private void drawConcave(Canvas canvas) {
+        // checking if width of clip out path needs to change
+        if (mBounds == null
+                || getBounds().right != mBounds.right
+                || getBounds().left != mBounds.left) {
+            mConcaveInfo.mPath.reset();
+            float left = getBounds().left;
+            float right = getBounds().right;
+            float top = 0f;
+            float bottom = mConcaveInfo.mPathOverlap;
+            mConcaveInfo.mPath.addRoundRect(left, top, right, bottom,
+                    mConcaveInfo.mCornerRadii, Path.Direction.CW);
+        }
+        mBounds = getBounds();
+        int translation = (int) (mBounds.bottom - mConcaveInfo.mPathOverlap);
+        canvas.translate(0, translation);
+        canvas.clipOutPath(mConcaveInfo.mPath);
+        canvas.translate(0, -translation);
     }
 
     @VisibleForTesting
     public int getMainColor() {
         return mMainColor;
     }
+
+    private static class ConcaveInfo {
+        private final float mPathOverlap;
+        private final float[] mCornerRadii;
+        private final Path mPath = new Path();
+
+        ConcaveInfo(float pathOverlap, float[] cornerRadii) {
+            mPathOverlap = pathOverlap;
+            mCornerRadii = cornerRadii;
+        }
+    }
 }
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 594a1a7..135c076 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -27,6 +27,7 @@
 import static com.android.internal.jank.InteractionJankMonitor.ACTION_METRICS_LOGGED;
 import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_BEGIN;
 import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_CANCEL;
+import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_END;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -215,7 +216,7 @@
             mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
         }
         if (mListener != null) {
-            mListener.onNotifyCujEvents(mSession, ACTION_SESSION_BEGIN);
+            mListener.onCujEvents(mSession, ACTION_SESSION_BEGIN);
         }
     }
 
@@ -240,7 +241,9 @@
             }
             Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
             mSession.setReason(reason);
-            InteractionJankMonitor.getInstance().removeTimeout(mSession.getCuj());
+            if (mListener != null) {
+                mListener.onCujEvents(mSession, ACTION_SESSION_END);
+            }
         }
         // We don't remove observer here,
         // will remove it when all the frame metrics in this duration are called back.
@@ -269,7 +272,7 @@
         // Notify the listener the session has been cancelled.
         // We don't notify the listeners if the session never begun.
         if (mListener != null) {
-            mListener.onNotifyCujEvents(mSession, ACTION_SESSION_CANCEL);
+            mListener.onCujEvents(mSession, ACTION_SESSION_CANCEL);
         }
     }
 
@@ -445,7 +448,7 @@
                     maxFrameTimeNanos,
                     missedSfFramesCounts);
             if (mListener != null) {
-                mListener.onNotifyCujEvents(mSession, ACTION_METRICS_LOGGED);
+                mListener.onCujEvents(mSession, ACTION_METRICS_LOGGED);
             }
         }
         if (DEBUG) {
@@ -587,6 +590,6 @@
          * @param session the CUJ session
          * @param action the specific action
          */
-        void onNotifyCujEvents(Session session, String action);
+        void onCujEvents(Session session, String action);
     }
 }
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 6c56d42..5ab2a82 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -19,7 +19,9 @@
 import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
 
 import static com.android.internal.jank.FrameTracker.ChoreographerWrapper;
+import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NORMAL;
 import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NOT_BEGUN;
+import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
 import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
@@ -100,6 +102,7 @@
     private static final int DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS = 64;
 
     public static final String ACTION_SESSION_BEGIN = ACTION_PREFIX + ".ACTION_SESSION_BEGIN";
+    public static final String ACTION_SESSION_END = ACTION_PREFIX + ".ACTION_SESSION_END";
     public static final String ACTION_SESSION_CANCEL = ACTION_PREFIX + ".ACTION_SESSION_CANCEL";
     public static final String ACTION_METRICS_LOGGED = ACTION_PREFIX + ".ACTION_METRICS_LOGGED";
     public static final String BUNDLE_KEY_CUJ_NAME = ACTION_PREFIX + ".CUJ_NAME";
@@ -275,10 +278,7 @@
     public FrameTracker createFrameTracker(View v, Session session) {
         final Context c = v.getContext().getApplicationContext();
         synchronized (this) {
-            boolean needListener = SystemProperties.getBoolean(PROP_NOTIFY_CUJ_EVENT, false);
-            FrameTrackerListener eventsListener =
-                    !needListener ? null : (s, act) -> notifyEvents(c, act, s);
-
+            FrameTrackerListener eventsListener = (s, act) -> handleCujEvents(c, act, s);
             return new FrameTracker(session, mWorker.getThreadHandler(),
                     new ThreadedRendererWrapper(v.getThreadedRenderer()),
                     new ViewRootWrapper(v.getViewRootImpl()), new SurfaceControlWrapper(),
@@ -287,6 +287,28 @@
         }
     }
 
+    private void handleCujEvents(Context context, String action, Session session) {
+        // Clear the running and timeout tasks if the end / cancel was fired within the tracker.
+        // Or we might have memory leaks.
+        if (needRemoveTasks(action, session)) {
+            removeTimeout(session.getCuj());
+            removeTracker(session.getCuj());
+        }
+
+        // Notify the receivers if necessary.
+        if (session.shouldNotify()) {
+            notifyEvents(context, action, session);
+        }
+    }
+
+    private boolean needRemoveTasks(String action, Session session) {
+        final boolean badEnd = action.equals(ACTION_SESSION_END)
+                && session.getReason() != REASON_END_NORMAL;
+        final boolean badCancel = action.equals(ACTION_SESSION_CANCEL)
+                && session.getReason() != REASON_CANCEL_NORMAL;
+        return badEnd || badCancel;
+    }
+
     private void notifyEvents(Context context, String action, Session session) {
         if (action.equals(ACTION_SESSION_CANCEL)
                 && session.getReason() == REASON_CANCEL_NOT_BEGUN) {
@@ -299,7 +321,7 @@
         context.sendBroadcast(intent);
     }
 
-    void removeTimeout(@CujType int cujType) {
+    private void removeTimeout(@CujType int cujType) {
         synchronized (this) {
             Runnable timeout = mTimeoutActions.get(cujType);
             if (timeout != null) {
@@ -371,7 +393,7 @@
             // Skip this call since we haven't started a trace yet.
             if (tracker == null) return false;
             tracker.end(FrameTracker.REASON_END_NORMAL);
-            mRunningTrackers.remove(cujType);
+            removeTracker(cujType);
             return true;
         }
     }
@@ -390,7 +412,7 @@
             // Skip this call since we haven't started a trace yet.
             if (tracker == null) return false;
             tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
-            mRunningTrackers.remove(cujType);
+            removeTracker(cujType);
             return true;
         }
     }
@@ -401,6 +423,12 @@
         }
     }
 
+    private void removeTracker(@CujType int cuj) {
+        synchronized (this) {
+            mRunningTrackers.remove(cuj);
+        }
+    }
+
     private void updateProperties(DeviceConfig.Properties properties) {
         synchronized (this) {
             mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
@@ -516,9 +544,11 @@
         private long mTimeStamp;
         @FrameTracker.Reasons
         private int mReason = FrameTracker.REASON_END_UNKNOWN;
+        private boolean mShouldNotify;
 
         public Session(@CujType int cujType) {
             mCujType = cujType;
+            mShouldNotify = SystemProperties.getBoolean(PROP_NOTIFY_CUJ_EVENT, false);
         }
 
         @CujType
@@ -558,5 +588,10 @@
         public int getReason() {
             return mReason;
         }
+
+        /** Determine if should notify the receivers of cuj events */
+        public boolean shouldNotify() {
+            return mShouldNotify;
+        }
     }
 }
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 0bed29b..8b70966 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1026,21 +1026,6 @@
   gUsapPoolCount = 0;
 }
 
-NO_PAC_FUNC
-static void PAuthKeyChange(JNIEnv* env) {
-#ifdef __aarch64__
-  unsigned long int hwcaps = getauxval(AT_HWCAP);
-  if (hwcaps & HWCAP_PACA) {
-    const unsigned long key_mask = PR_PAC_APIAKEY | PR_PAC_APIBKEY |
-                                   PR_PAC_APDAKEY | PR_PAC_APDBKEY | PR_PAC_APGAKEY;
-    if (prctl(PR_PAC_RESET_KEYS, key_mask, 0, 0, 0) != 0) {
-      ALOGE("Failed to change the PAC keys: %s", strerror(errno));
-      RuntimeAbort(env, __LINE__, "PAC key change failed.");
-    }
-  }
-#endif
-}
-
 // Create an app data directory over tmpfs overlayed CE / DE storage, and bind mount it
 // from the actual app data directory in data mirror.
 static bool createAndMountAppData(std::string_view package_name,
@@ -2020,7 +2005,6 @@
 }
 
 // Utility routine to fork a process from the zygote.
-NO_PAC_FUNC
 pid_t zygote::ForkCommon(JNIEnv* env, bool is_system_server,
                          const std::vector<int>& fds_to_close,
                          const std::vector<int>& fds_to_ignore,
@@ -2075,7 +2059,6 @@
     }
 
     // The child process.
-    PAuthKeyChange(env);
     PreApplicationInit();
 
     // Clean up any descriptors which must be closed immediately
@@ -2107,7 +2090,6 @@
   PreApplicationInit();
 }
 
-NO_PAC_FUNC
 static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
         JNIEnv* env, jclass, jint uid, jint gid, jintArray gids, jint runtime_flags,
         jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name,
@@ -2157,7 +2139,6 @@
     return pid;
 }
 
-NO_PAC_FUNC
 static jint com_android_internal_os_Zygote_nativeForkSystemServer(
         JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
         jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
@@ -2229,7 +2210,6 @@
  * @param is_priority_fork  Controls the nice level assigned to the newly created process
  * @return child pid in the parent, 0 in the child
  */
-NO_PAC_FUNC
 static jint com_android_internal_os_Zygote_nativeForkApp(JNIEnv* env,
                                                          jclass,
                                                          jint read_pipe_fd,
@@ -2244,7 +2224,6 @@
                             args_known == JNI_TRUE, is_priority_fork == JNI_TRUE, true);
 }
 
-NO_PAC_FUNC
 int zygote::forkApp(JNIEnv* env,
                     int read_pipe_fd,
                     int write_pipe_fd,
diff --git a/core/jni/com_android_internal_os_Zygote.h b/core/jni/com_android_internal_os_Zygote.h
index d2da914..b87396c 100644
--- a/core/jni/com_android_internal_os_Zygote.h
+++ b/core/jni/com_android_internal_os_Zygote.h
@@ -20,18 +20,6 @@
 #define LOG_TAG "Zygote"
 #define ATRACE_TAG ATRACE_TAG_DALVIK
 
-/* Functions in the callchain during the fork shall not be protected with
-   Armv8.3-A Pointer Authentication, otherwise child will not be able to return. */
-#ifdef __ARM_FEATURE_PAC_DEFAULT
-#ifdef __ARM_FEATURE_BTI_DEFAULT
-#define NO_PAC_FUNC __attribute__((target("branch-protection=bti")))
-#else
-#define NO_PAC_FUNC __attribute__((target("branch-protection=none")))
-#endif /* __ARM_FEATURE_BTI_DEFAULT */
-#else /* !__ARM_FEATURE_PAC_DEFAULT */
-#define NO_PAC_FUNC
-#endif /* __ARM_FEATURE_PAC_DEFAULT */
-
 #include <jni.h>
 #include <vector>
 #include <android-base/stringprintf.h>
@@ -42,7 +30,6 @@
 namespace android {
 namespace zygote {
 
-NO_PAC_FUNC
 pid_t ForkCommon(JNIEnv* env,bool is_system_server,
                  const std::vector<int>& fds_to_close,
                  const std::vector<int>& fds_to_ignore,
@@ -57,7 +44,6 @@
  * communication is required. Is_priority_fork should be true if this is on the app startup
  * critical path. Purge specifies that unused pages should be purged before the fork.
  */
-NO_PAC_FUNC
 int forkApp(JNIEnv* env,
             int read_pipe_fd,
             int write_pipe_fd,
diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
index 011e8f8..24fef48 100644
--- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
+++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
@@ -365,7 +365,6 @@
 // We only process fork commands if the peer uid matches expected_uid.
 // For every fork command after the first, we check that the requested uid is at
 // least minUid.
-NO_PAC_FUNC
 jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly(
             JNIEnv* env,
             jclass,
diff --git a/core/proto/android/app/activitymanager.proto b/core/proto/android/app/activitymanager.proto
index ead7e16..a21d1d4 100644
--- a/core/proto/android/app/activitymanager.proto
+++ b/core/proto/android/app/activitymanager.proto
@@ -33,4 +33,6 @@
     UID_OBSERVER_FLAG_ACTIVE = 4;
     // report uid cached state has changed, original value is 1 << 4
     UID_OBSERVER_FLAG_CACHED = 5;
+    // report uid capability has changed, original value is 1 << 5
+    UID_OBSERVER_FLAG_CAPABILITY = 6;
 }
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 74a37ca..17dc4589 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -778,6 +778,7 @@
         CHANGE_ACTIVE = 2;
         CHANGE_CACHED = 3;
         CHANGE_UNCACHED = 4;
+        CHANGE_CAPABILITY = 5;
     }
     repeated Change last_reported_changes = 8;
     optional int32 num_procs = 9;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 83e6c4b..46f2f3e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -326,7 +326,6 @@
     <protected-broadcast android:name="android.se.omapi.action.SECURE_ELEMENT_STATE_CHANGED" />
 
     <protected-broadcast android:name="android.nfc.action.ADAPTER_STATE_CHANGED" />
-    <protected-broadcast android:name="android.nfc.action.ALWAYS_ON_STATE_CHANGED" />
     <protected-broadcast android:name="android.nfc.action.PREFERRED_PAYMENT_CHANGED" />
     <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" />
     <protected-broadcast android:name="android.nfc.action.REQUIRE_UNLOCK_FOR_NFC" />
@@ -4258,6 +4257,7 @@
                 android:protectionLevel="normal" />
 
     <!-- Allows an application to create new companion device associations.
+         @SystemApi
          @hide -->
     <permission android:name="android.permission.ASSOCIATE_COMPANION_DEVICES"
         android:protectionLevel="internal|role" />
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index f47fa39..4dbdc60 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -531,7 +531,7 @@
         }
 
         @Override
-        public void scheduleCrash(String s) throws RemoteException {
+        public void scheduleCrash(String s, int i) throws RemoteException {
         }
 
         @Override
diff --git a/core/tests/coretests/src/android/view/OWNERS b/core/tests/coretests/src/android/view/OWNERS
index fa1aa5e..74cdd21 100644
--- a/core/tests/coretests/src/android/view/OWNERS
+++ b/core/tests/coretests/src/android/view/OWNERS
@@ -1,3 +1,6 @@
+# Accessibility
+per-file WindowInfoTest.java = file:/services/accessibility/OWNERS
+
 # Input
 per-file *MotionEventTest.* = file:/services/core/java/com/android/server/input/OWNERS
 per-file *KeyEventTest.* = file:/services/core/java/com/android/server/input/OWNERS
@@ -9,6 +12,7 @@
 per-file *Insets* = file:/services/core/java/com/android/server/wm/OWNERS
 per-file *View* = file:/services/core/java/com/android/server/wm/OWNERS
 per-file *Visibility* = file:/services/core/java/com/android/server/wm/OWNERS
+per-file *Window*  = file:/services/core/java/com/android/server/wm/OWNERS
 
 # Scroll Capture
 per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 54f9fa8..a612265 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -673,6 +673,13 @@
     /**
      * @hide
      */
+    public void punchHole(float left, float top, float right, float bottom, float rx, float ry) {
+        nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry);
+    }
+
+    /**
+     * @hide
+     */
     public void setHwBitmapsInSwModeEnabled(boolean enabled) {
         mAllowHwBitmapsInSwMode = enabled;
     }
@@ -815,4 +822,7 @@
 
     private static native void nDrawTextOnPath(long nativeCanvas, String text, long nativePath,
             float hOffset, float vOffset, int flags, long nativePaint);
+
+    private static native void nPunchHole(long renderer, float left, float top, float right,
+            float bottom, float rx, float ry);
 }
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index a8922e8..a998ba8 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -610,6 +610,14 @@
                 indices, indexOffset, indexCount, paint.getNativeInstance());
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public void punchHole(float left, float top, float right, float bottom, float rx, float ry) {
+        nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry);
+    }
+
     @FastNative
     private static native void nDrawBitmap(long nativeCanvas, long bitmapHandle, float left,
             float top, long nativePaintOrZero, int canvasDensity, int screenDensity,
@@ -735,4 +743,8 @@
     @FastNative
     private static native void nDrawTextOnPath(long nativeCanvas, String text, long nativePath,
             float hOffset, float vOffset, int flags, long nativePaint);
+
+    @FastNative
+    private static native void nPunchHole(long renderer, float left, float top, float right,
+            float bottom, float rx, float ry);
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index f6b63eb..e152633 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -1011,6 +1011,9 @@
 
     /** Moves the PiP menu to the destination bounds. */
     public void finishResizeForMenu(Rect destinationBounds) {
+        if (!isInPip()) {
+            return;
+        }
         mPipMenuController.movePipMenu(null, null, destinationBounds);
         mPipMenuController.updateMenuBounds(destinationBounds);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 4cf8ab4..17cde73 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -16,13 +16,9 @@
 
 package com.android.wm.shell.pip.phone;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP;
 
 import android.annotation.Nullable;
-import android.app.ActivityTaskManager;
-import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.RemoteAction;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
@@ -43,6 +39,7 @@
 
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.pip.PipBoundsState;
 import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipMediaController.ActionListener;
 import com.android.wm.shell.pip.PipMenuController;
@@ -59,7 +56,7 @@
  */
 public class PhonePipMenuController implements PipMenuController {
 
-    private static final String TAG = "PipMenuActController";
+    private static final String TAG = "PhonePipMenuController";
     private static final boolean DEBUG = false;
 
     public static final int MENU_STATE_NONE = 0;
@@ -99,6 +96,7 @@
     private final RectF mTmpSourceRectF = new RectF();
     private final RectF mTmpDestinationRectF = new RectF();
     private final Context mContext;
+    private final PipBoundsState mPipBoundsState;
     private final PipMediaController mMediaController;
     private final ShellExecutor mMainExecutor;
     private final Handler mMainHandler;
@@ -134,10 +132,11 @@
         }
     };
 
-    public PhonePipMenuController(Context context, PipMediaController mediaController,
-            SystemWindows systemWindows, ShellExecutor mainExecutor,
-            Handler mainHandler) {
+    public PhonePipMenuController(Context context, PipBoundsState pipBoundsState,
+            PipMediaController mediaController, SystemWindows systemWindows,
+            ShellExecutor mainExecutor, Handler mainHandler) {
         mContext = context;
+        mPipBoundsState = pipBoundsState;
         mMediaController = mediaController;
         mSystemWindows = systemWindows;
         mMainExecutor = mainExecutor;
@@ -466,20 +465,11 @@
      * Updates the PiP menu with the best set of actions provided.
      */
     private void updateMenuActions() {
-        if (isMenuVisible()) {
-            // Fetch the pinned stack bounds
-            Rect stackBounds = null;
-            try {
-                RootTaskInfo pinnedTaskInfo = ActivityTaskManager.getService().getRootTaskInfo(
-                        WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
-                if (pinnedTaskInfo != null) {
-                    stackBounds = pinnedTaskInfo.bounds;
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error showing PIP menu", e);
+        if (mPipMenuView != null) {
+            final ParceledListSlice<RemoteAction> menuActions = resolveMenuActions();
+            if (menuActions != null) {
+                mPipMenuView.setActions(mPipBoundsState.getBounds(), menuActions.getList());
             }
-
-            mPipMenuView.setActions(stackBounds, resolveMenuActions().getList());
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 1bfae53..7e594a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -374,7 +374,9 @@
     void setActions(Rect stackBounds, List<RemoteAction> actions) {
         mActions.clear();
         mActions.addAll(actions);
-        updateActionViews(stackBounds);
+        if (mMenuState == MENU_STATE_FULL) {
+            updateActionViews(stackBounds);
+        }
     }
 
     private void updateActionViews(Rect stackBounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 855faaa..516dfd0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -357,7 +357,7 @@
             if (iconDrawable != null) {
                 builder.setCenterViewDrawable(iconDrawable);
             }
-            builder.setAnimationDuration(mIconAnimationDuration);
+            builder.setAnimationDurationMillis(mIconAnimationDuration);
             if (mBrandingDrawable != null) {
                 builder.setBrandingDrawable(mBrandingDrawable, mBrandingImageWidth,
                         mBrandingImageHeight);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
index a4a83eb..4196d68 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
@@ -37,15 +37,12 @@
 import android.graphics.drawable.Animatable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.os.SystemClock;
 import android.os.Trace;
 import android.util.PathParser;
 import android.window.SplashScreenView;
 
 import com.android.internal.R;
 
-import java.util.function.Consumer;
-
 /**
  * Creating a lightweight Drawable object used for splash screen.
  * @hide
@@ -133,7 +130,6 @@
         private Animatable mAnimatableIcon;
         private Animator mIconAnimator;
         private boolean mAnimationTriggered;
-        private long mIconAnimationStart;
 
         AnimatableIconDrawable(@ColorInt int backgroundColor, Drawable foregroundDrawable) {
             mForegroundDrawable = foregroundDrawable;
@@ -150,16 +146,15 @@
         }
 
         @Override
-        protected boolean prepareAnimate(long duration, Consumer<Long> startListener) {
+        protected boolean prepareAnimate(long duration, Runnable startListener) {
             mAnimatableIcon = (Animatable) mForegroundDrawable;
             mIconAnimator = ValueAnimator.ofInt(0, 1);
             mIconAnimator.setDuration(duration);
             mIconAnimator.addListener(new Animator.AnimatorListener() {
                 @Override
                 public void onAnimationStart(Animator animation) {
-                    mIconAnimationStart = SystemClock.uptimeMillis();
                     if (startListener != null) {
-                        startListener.accept(mIconAnimationStart);
+                        startListener.run();
                     }
                     mAnimatableIcon.start();
                 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 29a144f..e95135a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -105,8 +105,10 @@
 
     /**
      * Called when a task need a splash screen starting window.
+     * @param emptyView Whether drawing an empty frame without anything on it.
      */
-    public void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
+    void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken,
+            boolean emptyView) {
         final RunningTaskInfo taskInfo = windowInfo.taskInfo;
         final ActivityInfo activityInfo = taskInfo.topActivityInfo;
         if (activityInfo == null) {
@@ -203,7 +205,6 @@
         }
 
         final PhoneWindow win = new PhoneWindow(context);
-        win.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
         win.setIsStartingWindow(true);
 
         CharSequence label = context.getResources().getText(labelRes, null);
@@ -270,17 +271,20 @@
         try {
             final View view = win.getDecorView();
             final WindowManager wm = mContext.getSystemService(WindowManager.class);
-            // splash screen content will be deprecated after S.
-            sView = SplashscreenContentDrawer.makeSplashscreenContent(
-                    context, splashscreenContentResId[0]);
-            final boolean splashscreenContentCompatible = sView != null;
-            if (splashscreenContentCompatible) {
-                win.setContentView(sView);
-            } else {
-                sView = mSplashscreenContentDrawer
-                        .makeSplashScreenContentView(context, activityInfo);
-                win.setContentView(sView);
-                sView.cacheRootWindow(win);
+            if (!emptyView) {
+                // splash screen content will be deprecated after S.
+                sView = SplashscreenContentDrawer.makeSplashscreenContent(
+                        context, splashscreenContentResId[0]);
+                final boolean splashscreenContentCompatible = sView != null;
+                if (splashscreenContentCompatible) {
+                    win.setContentView(sView);
+                } else {
+                    sView = mSplashscreenContentDrawer
+                            .makeSplashScreenContentView(context, activityInfo);
+                    win.setContentView(sView);
+                    win.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+                    sView.cacheRootWindow(win);
+                }
             }
             postAddWindow(taskId, appToken, view, wm, params);
         } catch (RuntimeException e) {
@@ -393,6 +397,9 @@
                         // animation
                         removeWindowInner(record.mDecorView, false);
                     }
+                } else {
+                    // no animation will be applied
+                    removeWindowInner(record.mDecorView, false);
                 }
             }
             if (record.mTaskSnapshotWindow != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index a06068d6..8a629bc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -16,6 +16,7 @@
 package com.android.wm.shell.startingsurface;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN;
 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
@@ -23,6 +24,7 @@
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_SAME_PACKAGE;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
 
 import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
@@ -65,7 +67,8 @@
  */
 public class StartingWindowController implements RemoteCallable<StartingWindowController> {
     private static final String TAG = StartingWindowController.class.getSimpleName();
-    static final boolean DEBUG_SPLASH_SCREEN = false;
+    // TODO b/183150443 Keep this flag open for a while, several things might need to adjust.
+    static final boolean DEBUG_SPLASH_SCREEN = true;
     static final boolean DEBUG_TASK_SNAPSHOT = false;
 
     private final StartingSurfaceDrawer mStartingSurfaceDrawer;
@@ -123,24 +126,37 @@
             final boolean processRunning = (parameter & TYPE_PARAMETER_PROCESS_RUNNING) != 0;
             final boolean allowTaskSnapshot = (parameter & TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT) != 0;
             final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0;
+            final boolean samePackage = (parameter & TYPE_PARAMETER_SAME_PACKAGE) != 0;
             return estimateStartingWindowType(windowInfo, newTask, taskSwitch,
-                    processRunning, allowTaskSnapshot, activityCreated);
+                    processRunning, allowTaskSnapshot, activityCreated, samePackage);
         }
 
         // reference from ActivityRecord#getStartingWindowType
         private int estimateStartingWindowType(StartingWindowInfo windowInfo,
                 boolean newTask, boolean taskSwitch, boolean processRunning,
-                boolean allowTaskSnapshot, boolean activityCreated) {
+                boolean allowTaskSnapshot, boolean activityCreated, boolean samePackage) {
             if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
                 Slog.d(TAG, "preferredStartingWindowType newTask " + newTask
                         + " taskSwitch " + taskSwitch
                         + " processRunning " + processRunning
                         + " allowTaskSnapshot " + allowTaskSnapshot
-                        + " activityCreated " + activityCreated);
+                        + " activityCreated " + activityCreated
+                        + " samePackage " + samePackage);
             }
-            if ((newTask || !processRunning || (taskSwitch && !activityCreated))
-                    && windowInfo.taskInfo.topActivityType != ACTIVITY_TYPE_HOME) {
-                return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+            if (windowInfo.taskInfo.topActivityType != ACTIVITY_TYPE_HOME) {
+                if (!processRunning) {
+                    return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+                }
+                if (newTask) {
+                    if (samePackage) {
+                        return STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN;
+                    } else {
+                        return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+                    }
+                }
+                if (taskSwitch && !activityCreated) {
+                    return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+                }
             }
             if (taskSwitch && allowTaskSnapshot) {
                 final TaskSnapshot snapshot = getTaskSnapshot(windowInfo.taskInfo.taskId);
@@ -207,6 +223,10 @@
         mTaskLaunchingCallback = listener;
     }
 
+    private boolean shouldSendToListener(int suggestionType) {
+        return suggestionType != STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN;
+    }
+
     /**
      * Called when a task need a starting window.
      */
@@ -214,11 +234,15 @@
         mSplashScreenExecutor.execute(() -> {
             final int suggestionType = mStartingTypeChecker.estimateStartingWindowType(windowInfo);
             final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo;
-            if (mTaskLaunchingCallback != null) {
+            if (mTaskLaunchingCallback != null && shouldSendToListener(suggestionType)) {
                 mTaskLaunchingCallback.accept(runningTaskInfo.taskId, suggestionType);
             }
             if (suggestionType == STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
-                mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken);
+                mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken,
+                        false /* emptyView */);
+            } else if (suggestionType == STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN) {
+                mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken,
+                        true /* emptyView */);
             } else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) {
                 final TaskSnapshot snapshot = mStartingTypeChecker.mSnapshot;
                 mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, snapshot);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index 78b3d4e..d1d1313 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -131,7 +131,7 @@
         final Handler mainLoop = new Handler(Looper.getMainLooper());
         final StartingWindowInfo windowInfo =
                 createWindowInfo(taskId, android.R.style.Theme);
-        mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder);
+        mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, false);
         waitHandlerIdle(mainLoop);
         verify(mStartingSurfaceDrawer).postAddWindow(eq(taskId), eq(mBinder), any(), any(), any());
         assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId);
@@ -148,7 +148,7 @@
         final Handler mainLoop = new Handler(Looper.getMainLooper());
         final StartingWindowInfo windowInfo =
                 createWindowInfo(taskId, 0);
-        mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder);
+        mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, false);
         waitHandlerIdle(mainLoop);
         verify(mStartingSurfaceDrawer).postAddWindow(eq(taskId), eq(mBinder), any(), any(), any());
         assertNotEquals(mStartingSurfaceDrawer.mViewThemeResId, 0);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 607ef72..c75b21f 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -464,10 +464,12 @@
         "canvas/CanvasOpBuffer.cpp",
         "canvas/CanvasOpRasterizer.cpp",
         "effects/StretchEffect.cpp",
+        "pipeline/skia/HolePunch.cpp",
         "pipeline/skia/SkiaDisplayList.cpp",
         "pipeline/skia/SkiaRecordingCanvas.cpp",
         "pipeline/skia/RenderNodeDrawable.cpp",
         "pipeline/skia/ReorderBarrierDrawables.cpp",
+        "pipeline/skia/TransformCanvas.cpp",
         "renderthread/Frame.cpp",
         "renderthread/RenderTask.cpp",
         "renderthread/TimeLord.cpp",
@@ -647,6 +649,7 @@
         "tests/unit/CommonPoolTests.cpp",
         "tests/unit/DamageAccumulatorTests.cpp",
         "tests/unit/DeferredLayerUpdaterTests.cpp",
+        "tests/unit/EglManagerTests.cpp",
         "tests/unit/FatVectorTests.cpp",
         "tests/unit/GraphicsStatsServiceTests.cpp",
         "tests/unit/LayerUpdateQueueTests.cpp",
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 3aa5b4b..894b479 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -17,8 +17,10 @@
 #pragma once
 
 #include "pipeline/skia/SkiaDisplayList.h"
+#include "canvas/CanvasOpBuffer.h"
 
 #include <memory>
+#include <variant>
 
 namespace android {
 namespace uirenderer {
@@ -28,29 +30,25 @@
 };
 typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot;
 
-/**
- * Data structure that holds the list of commands used in display list stream
- */
-//using DisplayList = skiapipeline::SkiaDisplayList;
-class DisplayList {
+class SkiaDisplayListWrapper {
 public:
     // Constructs an empty (invalid) DisplayList
-    explicit DisplayList() {}
+    explicit SkiaDisplayListWrapper() {}
 
     // Constructs a DisplayList from a SkiaDisplayList
-    explicit DisplayList(std::unique_ptr<skiapipeline::SkiaDisplayList> impl)
+    explicit SkiaDisplayListWrapper(std::unique_ptr<skiapipeline::SkiaDisplayList> impl)
         : mImpl(std::move(impl)) {}
 
     // Move support
-    DisplayList(DisplayList&& other) : mImpl(std::move(other.mImpl)) {}
-    DisplayList& operator=(DisplayList&& other) {
+    SkiaDisplayListWrapper(SkiaDisplayListWrapper&& other) : mImpl(std::move(other.mImpl)) {}
+    SkiaDisplayListWrapper& operator=(SkiaDisplayListWrapper&& other) {
         mImpl = std::move(other.mImpl);
         return *this;
     }
 
     // No copy support
-    DisplayList(const DisplayList& other) = delete;
-    DisplayList& operator=(const DisplayList&) = delete;
+    SkiaDisplayListWrapper(const SkiaDisplayListWrapper& other) = delete;
+    SkiaDisplayListWrapper& operator=(const SkiaDisplayListWrapper&) = delete;
 
     void updateChildren(std::function<void(RenderNode*)> updateFn) {
         mImpl->updateChildren(std::move(updateFn));
@@ -74,6 +72,10 @@
         return mImpl && !(mImpl->isEmpty());
     }
 
+    [[nodiscard]] bool hasHolePunches() const {
+        return mImpl && mImpl->hasHolePunches();
+    }
+
     [[nodiscard]] bool containsProjectionReceiver() const {
         return mImpl && mImpl->containsProjectionReceiver();
     }
@@ -137,7 +139,7 @@
 
     void applyColorTransform(ColorTransform transform) {
         if (mImpl) {
-            mImpl->mDisplayList.applyColorTransform(transform);
+            mImpl->applyColorTransform(transform);
         }
     }
 
@@ -145,5 +147,172 @@
     std::unique_ptr<skiapipeline::SkiaDisplayList> mImpl;
 };
 
+
+/**
+ * Data structure that holds the list of commands used in display list stream
+ */
+//using DisplayList = skiapipeline::SkiaDisplayList;
+class MultiDisplayList {
+private:
+    using SkiaDisplayList = skiapipeline::SkiaDisplayList;
+
+    struct EmptyList {
+        bool hasText() const { return false; }
+        void updateChildren(std::function<void(RenderNode*)> updateFn) {}
+        bool isEmpty() const { return true; }
+        bool containsProjectionReceiver() const { return false; }
+        bool hasVectorDrawables() const { return false; }
+        size_t getUsedSize() const { return 0; }
+        size_t getAllocatedSize() const { return 0; }
+        void output(std::ostream& output, uint32_t level) const { }
+        bool hasFunctor() const { return false; }
+        bool prepareListAndChildren(
+                TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+                std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
+            return false;
+        }
+        void syncContents(const WebViewSyncData& data) { }
+        void applyColorTransform(ColorTransform transform) { }
+    };
+
+    std::variant<EmptyList, std::unique_ptr<SkiaDisplayList>, CanvasOpBuffer> mImpls;
+
+    template <typename T>
+    static constexpr T& get(T& t) { return t; }
+    template <typename T>
+    static constexpr const T& get(const T& t) { return t; }
+
+    template <typename T>
+    static constexpr T& get(std::unique_ptr<T>& t) { return *t; }
+    template <typename T>
+    static constexpr const T& get(const std::unique_ptr<T>& t) { return *t; }
+
+    template <typename T>
+    auto apply(T&& t) {
+        return std::visit([&t](auto& it) -> auto {
+            return t(get(it));
+        }, mImpls);
+    }
+
+    template <typename T>
+    auto apply(T&& t) const {
+        return std::visit([&t](const auto& it) -> auto {
+            return t(get(it));
+        }, mImpls);
+    }
+
+public:
+    // Constructs an empty (invalid) DisplayList
+    explicit MultiDisplayList() {}
+
+    // Constructs a DisplayList from a SkiaDisplayList
+    explicit MultiDisplayList(std::unique_ptr<SkiaDisplayList> impl)
+        : mImpls(std::move(impl)) {}
+
+    explicit MultiDisplayList(CanvasOpBuffer&& opBuffer) : mImpls(std::move(opBuffer)) {}
+
+    // Move support
+    MultiDisplayList(MultiDisplayList&& other) : mImpls(std::move(other.mImpls)) {}
+    MultiDisplayList& operator=(MultiDisplayList&& other) {
+        mImpls = std::move(other.mImpls);
+        return *this;
+    }
+
+    // No copy support
+    MultiDisplayList(const MultiDisplayList& other) = delete;
+    MultiDisplayList& operator=(const MultiDisplayList&) = delete;
+
+    void updateChildren(std::function<void(RenderNode*)> updateFn) {
+        apply([&](auto& it) { it.updateChildren(std::move(updateFn)); });
+    }
+
+    [[nodiscard]] explicit operator bool() const {
+        return isValid();
+    }
+
+    // If true this DisplayList contains a backing content, even if that content is empty
+    // If false, there this DisplayList is in an "empty" state
+    [[nodiscard]] bool isValid() const {
+        return mImpls.index() != 0;
+    }
+
+    [[nodiscard]] bool isEmpty() const {
+        return apply([](const auto& it) -> auto { return it.isEmpty(); });
+    }
+
+    [[nodiscard]] bool hasContent() const {
+        return !isEmpty();
+    }
+
+    [[nodiscard]] bool containsProjectionReceiver() const {
+        return apply([](const auto& it) -> auto { return it.containsProjectionReceiver(); });
+    }
+
+    [[nodiscard]] SkiaDisplayList* asSkiaDl() {
+        return std::get<1>(mImpls).get();
+    }
+
+    [[nodiscard]] const SkiaDisplayList* asSkiaDl() const {
+        return std::get<1>(mImpls).get();
+    }
+
+    [[nodiscard]] bool hasVectorDrawables() const {
+        return apply([](const auto& it) -> auto { return it.hasVectorDrawables(); });
+    }
+
+    void clear(RenderNode* owningNode = nullptr) {
+        if (owningNode && mImpls.index() == 1) {
+            auto& skiaDl = std::get<1>(mImpls);
+            if (skiaDl->reuseDisplayList(owningNode)) {
+                skiaDl.release();
+            }
+        }
+        mImpls = EmptyList{};
+    }
+
+    [[nodiscard]] size_t getUsedSize() const {
+        return apply([](const auto& it) -> auto { return it.getUsedSize(); });
+    }
+
+    [[nodiscard]] size_t getAllocatedSize() const {
+        return apply([](const auto& it) -> auto { return it.getAllocatedSize(); });
+    }
+
+    void output(std::ostream& output, uint32_t level) const {
+        apply([&](const auto& it) { it.output(output, level); });
+    }
+
+    [[nodiscard]] bool hasFunctor() const {
+        return apply([](const auto& it) -> auto { return it.hasFunctor(); });
+    }
+
+    bool prepareListAndChildren(
+            TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+            std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
+        return apply([&](auto& it) -> auto {
+            return it.prepareListAndChildren(observer, info, functorsNeedLayer, std::move(childFn));
+        });
+    }
+
+    void syncContents(const WebViewSyncData& data) {
+        apply([&](auto& it) { it.syncContents(data); });
+    }
+
+    [[nodiscard]] bool hasText() const {
+        return apply([](const auto& it) -> auto { return it.hasText(); });
+    }
+
+    void applyColorTransform(ColorTransform transform) {
+        apply([=](auto& it) { it.applyColorTransform(transform); });
+    }
+
+    [[nodiscard]] CanvasOpBuffer& asOpBuffer() {
+        return std::get<CanvasOpBuffer>(mImpls);
+    }
+};
+
+// For now stick to the original single-type container to avoid any regressions
+using DisplayList = SkiaDisplayListWrapper;
+
 }  // namespace uirenderer
 }  // namespace android
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index f5b2675..e9eae3d 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -255,15 +255,19 @@
 
     if (mDisplayList) {
         info.out.hasFunctors |= mDisplayList.hasFunctor();
+        mHasHolePunches = mDisplayList.hasHolePunches();
         bool isDirty = mDisplayList.prepareListAndChildren(
                 observer, info, childFunctorsNeedLayer,
-                [](RenderNode* child, TreeObserver& observer, TreeInfo& info,
-                   bool functorsNeedLayer) {
+                [this](RenderNode* child, TreeObserver& observer, TreeInfo& info,
+                       bool functorsNeedLayer) {
                     child->prepareTreeImpl(observer, info, functorsNeedLayer);
+                    mHasHolePunches |= child->hasHolePunches();
                 });
         if (isDirty) {
             damageSelf(info);
         }
+    } else {
+        mHasHolePunches = false;
     }
     pushLayerUpdate(info);
 
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 39ea53b..988141f 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -35,6 +35,7 @@
 #include "DisplayList.h"
 #include "Matrix.h"
 #include "RenderProperties.h"
+#include "pipeline/skia/HolePunch.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #include "pipeline/skia/SkiaLayer.h"
 
@@ -284,6 +285,8 @@
 
     UsageHint mUsageHint = UsageHint::Unknown;
 
+    bool mHasHolePunches;
+
     // METHODS & FIELDS ONLY USED BY THE SKIA RENDERER
 public:
     /**
@@ -294,6 +297,8 @@
         return std::move(mAvailableDisplayList);
     }
 
+    bool hasHolePunches() { return mHasHolePunches; }
+
     /**
      * Attach unused displayList to this node for potential future reuse.
      */
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 28d2b4c..4c4a152 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -23,6 +23,7 @@
 #include "hwui/MinikinUtils.h"
 #include "hwui/PaintFilter.h"
 #include "pipeline/skia/AnimatedDrawables.h"
+#include "pipeline/skia/HolePunch.h"
 
 #include <SkAndroidFrameworkUtils.h>
 #include <SkAnimatedImage.h>
@@ -244,6 +245,13 @@
     return (rec && rec->saveCount == currentSaveCount) ? rec : nullptr;
 }
 
+void SkiaCanvas::punchHole(const SkRRect& rect) {
+    SkPaint paint = SkPaint();
+    paint.setColor(0);
+    paint.setBlendMode(SkBlendMode::kClear);
+    mCanvas->drawRRect(rect, paint);
+}
+
 // ----------------------------------------------------------------------------
 // functions to emulate legacy SaveFlags (i.e. independent matrix/clip flags)
 // ----------------------------------------------------------------------------
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 9ab2b10..e0a0be5 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -60,6 +60,8 @@
         LOG_ALWAYS_FATAL("SkiaCanvas does not support enableZ");
     }
 
+    virtual void punchHole(const SkRRect& rect) override;
+
     virtual void setBitmap(const SkBitmap& bitmap) override;
 
     virtual bool isOpaque() override;
diff --git a/libs/hwui/canvas/CanvasFrontend.h b/libs/hwui/canvas/CanvasFrontend.h
index d749d2f..f9a6101 100644
--- a/libs/hwui/canvas/CanvasFrontend.h
+++ b/libs/hwui/canvas/CanvasFrontend.h
@@ -147,8 +147,7 @@
 public:
     template<class... Args>
     CanvasFrontend(int width, int height, Args&&... args) : CanvasStateHelper(width, height),
-            mReceiver(std::forward<Args>(args)...) { }
-    ~CanvasFrontend() = default;
+            mReceiver(std::in_place, std::forward<Args>(args)...) { }
 
     void save(SaveFlags::Flags flags = SaveFlags::MatrixClip) {
         if (internalSave(flagsToSaveEntry(flags))) {
@@ -186,7 +185,10 @@
         submit(std::move(op));
     }
 
-    const CanvasOpReceiver& receiver() const { return *mReceiver; }
+    const CanvasOpReceiver& receiver() const {
+        LOG_ALWAYS_FATAL_IF(!mReceiver.has_value());
+        return *mReceiver;
+    }
 
     CanvasOpReceiver finish() {
         auto ret = std::move(mReceiver.value());
@@ -205,6 +207,7 @@
 
     template <CanvasOpType T>
     void submit(CanvasOp<T>&& op) {
+        LOG_ALWAYS_FATAL_IF(!mReceiver.has_value());
         mReceiver->push_container(CanvasOpContainer(std::move(op), transform()));
     }
 };
diff --git a/libs/hwui/canvas/CanvasOpBuffer.cpp b/libs/hwui/canvas/CanvasOpBuffer.cpp
index 7054e47e..6089c572 100644
--- a/libs/hwui/canvas/CanvasOpBuffer.cpp
+++ b/libs/hwui/canvas/CanvasOpBuffer.cpp
@@ -22,4 +22,32 @@
 
 template class OpBuffer<CanvasOpType, CanvasOpContainer>;
 
+void CanvasOpBuffer::updateChildren(std::function<void(RenderNode*)> updateFn) {
+    // TODO: Do we need a fast-path for finding children?
+    if (mHas.children) {
+        for (auto& iter : filter<CanvasOpType::DrawRenderNode>()) {
+            updateFn(iter->renderNode.get());
+        }
+    }
+}
+
+void CanvasOpBuffer::output(std::ostream& output, uint32_t level) const {
+    LOG_ALWAYS_FATAL("TODO");
+}
+
+bool CanvasOpBuffer::prepareListAndChildren(
+            TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+            std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn) {
+    LOG_ALWAYS_FATAL("TODO");
+    return false;
+}
+
+void CanvasOpBuffer::syncContents(const WebViewSyncData& data) {
+    LOG_ALWAYS_FATAL("TODO");
+}
+
+void CanvasOpBuffer::applyColorTransform(ColorTransform transform) {
+    LOG_ALWAYS_FATAL("TODO");
+}
+
 }  // namespace android::uirenderer
diff --git a/libs/hwui/canvas/CanvasOpBuffer.h b/libs/hwui/canvas/CanvasOpBuffer.h
index 07e079a..af797ca 100644
--- a/libs/hwui/canvas/CanvasOpBuffer.h
+++ b/libs/hwui/canvas/CanvasOpBuffer.h
@@ -19,10 +19,17 @@
 #include <SkMatrix.h>
 
 #include "CanvasOpTypes.h"
+#include "CanvasTransform.h"
 #include "OpBuffer.h"
+#include "TreeInfo.h"
+#include "private/hwui/WebViewFunctor.h"
+
+#include <functional>
 
 namespace android::uirenderer {
 
+class RenderNode;
+
 template <CanvasOpType T>
 struct CanvasOp;
 
@@ -53,12 +60,74 @@
 };
 
 extern template class OpBuffer<CanvasOpType, CanvasOpContainer>;
-class CanvasOpBuffer final : public OpBuffer<CanvasOpType, CanvasOpContainer> {
+class CanvasOpBuffer final : private OpBuffer<CanvasOpType, CanvasOpContainer> {
+private:
+    using SUPER = OpBuffer<CanvasOpType, CanvasOpContainer>;
+
 public:
+    // Expose select superclass methods publicly
+    using SUPER::for_each;
+    using SUPER::size;
+    using SUPER::resize;
+
     template <CanvasOpType T>
     void push(CanvasOp<T>&& op) {
         push_container(CanvasOpContainer<T>(std::move(op)));
     }
+
+    template <CanvasOpType T>
+    void push_container(CanvasOpContainer<T>&& op) {
+        if constexpr (IsDrawOp(T)) {
+            mHas.content = true;
+        }
+        if constexpr (T == CanvasOpType::DrawRenderNode) {
+            mHas.children = true;
+            // use staging property, since recording on UI thread
+            if (op->renderNode->stagingProperties().isProjectionReceiver()) {
+                mHas.projectionReceiver = true;
+            }
+        }
+        SUPER::push_container(std::move(op));
+    }
+
+    void clear() {
+        mHas = Contains{};
+        SUPER::clear();
+    }
+
+    void updateChildren(std::function<void(RenderNode*)> updateFn);
+    bool prepareListAndChildren(
+            TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer,
+            std::function<void(RenderNode*, TreeObserver&, TreeInfo&, bool)> childFn);
+    void syncContents(const WebViewSyncData& data);
+    void applyColorTransform(ColorTransform transform);
+
+    [[nodiscard]] bool isEmpty() const { return !mHas.content; }
+    [[nodiscard]] bool hasText() const { return mHas.text; }
+    [[nodiscard]] bool hasVectorDrawables() const { return mHas.vectorDrawable; }
+    [[nodiscard]] bool containsProjectionReceiver() const { return mHas.projectionReceiver; }
+    [[nodiscard]] bool hasFunctor() const { return mHas.functor; }
+
+    [[nodiscard]] size_t getUsedSize() const {
+        return size();
+    }
+
+    [[nodiscard]] size_t getAllocatedSize() const {
+        return capacity();
+    }
+
+    void output(std::ostream& output, uint32_t level) const;
+
+private:
+    struct Contains {
+        bool content : 1 = false;
+        bool children : 1 = false;
+        bool projectionReceiver : 1 = false;
+        bool text : 1 = false;
+        bool vectorDrawable : 1 = false;
+        bool functor : 1 = false;
+    };
+    Contains mHas;
 };
 
 }  // namespace android::uirenderer
diff --git a/libs/hwui/canvas/CanvasOpRasterizer.cpp b/libs/hwui/canvas/CanvasOpRasterizer.cpp
index 0093c38..9297604 100644
--- a/libs/hwui/canvas/CanvasOpRasterizer.cpp
+++ b/libs/hwui/canvas/CanvasOpRasterizer.cpp
@@ -33,21 +33,15 @@
     SkMatrix& currentGlobalTransform = globalMatrixStack.emplace_back(SkMatrix::I());
 
     source.for_each([&]<CanvasOpType T>(const CanvasOpContainer<T> * op) {
-        if constexpr (
-            T == CanvasOpType::BeginZ ||
-            T == CanvasOpType::EndZ   ||
-            T == CanvasOpType::DrawLayer
-        ) {
-            // Do beginZ or endZ
-            LOG_ALWAYS_FATAL("TODO");
-            return;
-        } else {
+        if constexpr (CanvasOpTraits::can_draw<CanvasOp<T>>) {
             // Generic OP
             // First apply the current transformation
             destination->setMatrix(SkMatrix::Concat(currentGlobalTransform, op->transform()));
             // Now draw it
             (*op)->draw(destination);
+            return;
         }
+        LOG_ALWAYS_FATAL("TODO, unable to rasterize %d", static_cast<int>(T));
     });
 }
 
diff --git a/libs/hwui/canvas/CanvasOpTypes.h b/libs/hwui/canvas/CanvasOpTypes.h
index cde50bd..b55ef9d 100644
--- a/libs/hwui/canvas/CanvasOpTypes.h
+++ b/libs/hwui/canvas/CanvasOpTypes.h
@@ -35,7 +35,8 @@
     ClipPath,
 
     // Drawing ops
-    DrawColor,
+    DRAW_OP_BEGIN,
+    DrawColor = DRAW_OP_BEGIN,
     DrawRect,
     DrawRegion,
     DrawRoundRect,
@@ -59,10 +60,16 @@
     DrawImageLattice,
     DrawPicture,
     DrawLayer,
+    DrawRenderNode,
+    DRAW_OP_END = DrawRenderNode,
 
     // TODO: Rest
 
     COUNT  // must be last
 };
 
+static constexpr bool IsDrawOp(CanvasOpType t) {
+    return CanvasOpType::DRAW_OP_BEGIN <= t && t <= CanvasOpType::DRAW_OP_END;
+}
+
 }  // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
index 86b1ac7..855cd0d 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -24,13 +24,15 @@
 #include <SkImage.h>
 #include <SkPicture.h>
 #include <SkRuntimeEffect.h>
-#include <hwui/Bitmap.h>
-#include <log/log.h>
-#include "CanvasProperty.h"
-#include "Points.h"
 
+#include <log/log.h>
+
+#include "hwui/Bitmap.h"
+#include "CanvasProperty.h"
 #include "CanvasOpTypes.h"
 #include "Layer.h"
+#include "Points.h"
+#include "RenderNode.h"
 
 #include <experimental/type_traits>
 #include <utility>
@@ -450,6 +452,11 @@
     sp<Layer> layer;
 };
 
+template<>
+struct CanvasOp<CanvasOpType::DrawRenderNode> {
+    sp<RenderNode> renderNode;
+};
+
 // cleanup our macros
 #undef ASSERT_DRAWABLE
 
diff --git a/libs/hwui/canvas/OpBuffer.h b/libs/hwui/canvas/OpBuffer.h
index 1237d69..8b5cdbb 100644
--- a/libs/hwui/canvas/OpBuffer.h
+++ b/libs/hwui/canvas/OpBuffer.h
@@ -64,7 +64,7 @@
     static constexpr auto STARTING_SIZE = PadAlign(sizeof(BufferHeader));
     using ItemHeader = OpBufferItemHeader<ItemTypes>;
 
-    OpBuffer() = default;
+    explicit OpBuffer() = default;
 
     // Prevent copying by default
     OpBuffer(const OpBuffer&) = delete;
@@ -135,7 +135,7 @@
 
     template <typename F>
     void for_each(F&& f) const {
-        for_each(std::forward<F>(f), ItemTypesSequence{});
+        do_for_each(std::forward<F>(f), ItemTypesSequence{});
     }
 
     void clear();
@@ -225,7 +225,7 @@
     }
 
     template <typename F, std::size_t... I>
-    void for_each(F&& f, std::index_sequence<I...>) const {
+    void do_for_each(F&& f, std::index_sequence<I...>) const {
         // Validate we're not empty
         if (isEmpty()) return;
 
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index d1bdb71..c1feb76 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -155,6 +155,8 @@
         LOG_ALWAYS_FATAL("Not supported");
     }
 
+    virtual void punchHole(const SkRRect& rect) = 0;
+
     // ----------------------------------------------------------------------------
     // Canvas state operations
     // ----------------------------------------------------------------------------
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index 89fb8bb..a611f7c 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -35,6 +35,7 @@
 #include "SkGraphics.h"
 #include "SkRegion.h"
 #include "SkVertices.h"
+#include "SkRRect.h"
 
 namespace minikin {
 class MeasuredText;
@@ -667,6 +668,11 @@
     Canvas::setCompatibilityVersion(apiLevel);
 }
 
+static void punchHole(JNIEnv* env, jobject, jlong canvasPtr, jfloat left, jfloat top, jfloat right,
+        jfloat bottom, jfloat rx, jfloat ry) {
+    auto canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    canvas->punchHole(SkRRect::MakeRectXY(SkRect::MakeLTRB(left, top, right, bottom), rx, ry));
+}
 
 }; // namespace CanvasJNI
 
@@ -740,6 +746,7 @@
     {"nDrawTextRun","(JLjava/lang/String;IIIIFFZJ)V", (void*) CanvasJNI::drawTextRunString},
     {"nDrawTextOnPath","(J[CIIJFFIJ)V", (void*) CanvasJNI::drawTextOnPathChars},
     {"nDrawTextOnPath","(JLjava/lang/String;JFFIJ)V", (void*) CanvasJNI::drawTextOnPathString},
+    {"nPunchHole", "(JFFFFFF)V", (void*) CanvasJNI::punchHole}
 };
 
 int register_android_graphics_Canvas(JNIEnv* env) {
diff --git a/libs/hwui/pipeline/skia/HolePunch.cpp b/libs/hwui/pipeline/skia/HolePunch.cpp
new file mode 100644
index 0000000..2b2bca6
--- /dev/null
+++ b/libs/hwui/pipeline/skia/HolePunch.cpp
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+#include "HolePunch.h"
+#include <string>
+
+using namespace android::uirenderer::skiapipeline;
+
+const std::string HOLE_PUNCH_ANNOTATION = "surface_hole_punch";
diff --git a/libs/hwui/pipeline/skia/HolePunch.h b/libs/hwui/pipeline/skia/HolePunch.h
new file mode 100644
index 0000000..92c6f77
--- /dev/null
+++ b/libs/hwui/pipeline/skia/HolePunch.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <string>
+#include "SkRRect.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+const static std::string HOLE_PUNCH_ANNOTATION;
+
+}  // namespace skiapipeline
+}  // namespace uirenderer
+}  // namespace android
\ No newline at end of file
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index cb0ff8d..5627a7e 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -18,6 +18,7 @@
 #include <SkPaintFilterCanvas.h>
 #include "RenderNode.h"
 #include "SkiaDisplayList.h"
+#include "TransformCanvas.h"
 #include "utils/TraceUtils.h"
 
 #include <include/effects/SkImageFilters.h>
@@ -256,6 +257,11 @@
                 canvas->drawAnnotation(bounds, String8::format(
                     "SurfaceID|%" PRId64, renderNode->uniqueId()).c_str(), nullptr);
             }
+
+            if (renderNode->hasHolePunches()) {
+                TransformCanvas transformCanvas(canvas);
+                displayList->draw(&transformCanvas);
+            }
             canvas->drawImageRect(snapshotImage, bounds, bounds, sampling, &paint,
                                   SkCanvas::kStrict_SrcRectConstraint);
 
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 483264f..90e9bc6 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <deque>
+
 #include "RecordingCanvas.h"
 #include "RenderNodeDrawable.h"
 #include "TreeInfo.h"
@@ -23,8 +25,6 @@
 #include "utils/LinearAllocator.h"
 #include "utils/Pair.h"
 
-#include <deque>
-
 namespace android {
 namespace uirenderer {
 
@@ -46,8 +46,10 @@
 
 class SkiaDisplayList {
 public:
-    size_t getUsedSize() { return allocator.usedSize() + mDisplayList.usedSize(); }
-    size_t getAllocatedSize() { return allocator.allocatedSize() + mDisplayList.allocatedSize(); }
+    size_t getUsedSize() const { return allocator.usedSize() + mDisplayList.usedSize(); }
+    size_t getAllocatedSize() const {
+        return allocator.allocatedSize() + mDisplayList.allocatedSize();
+    }
 
     ~SkiaDisplayList() {
         /* Given that we are using a LinearStdAllocator to store some of the
@@ -109,6 +111,10 @@
      */
     void syncContents(const WebViewSyncData& data);
 
+    void applyColorTransform(ColorTransform transform) {
+        mDisplayList.applyColorTransform(transform);
+    }
+
     /**
      * ONLY to be called by RenderNode::prepareTree in order to prepare this
      * list while the UI thread is blocked.  Here we can upload mutable bitmaps
@@ -154,17 +160,25 @@
     std::deque<RenderNodeDrawable> mChildNodes;
     std::deque<FunctorDrawable*> mChildFunctors;
     std::vector<SkImage*> mMutableImages;
+
 private:
     std::vector<Pair<VectorDrawableRoot*, SkMatrix>> mVectorDrawables;
+    bool mHasHolePunches;
 public:
-    void appendVD(VectorDrawableRoot* r) {
-        appendVD(r, SkMatrix::I());
-    }
+    void appendVD(VectorDrawableRoot* r) { appendVD(r, SkMatrix::I()); }
 
     void appendVD(VectorDrawableRoot* r, const SkMatrix& mat) {
         mVectorDrawables.push_back(Pair<VectorDrawableRoot*, SkMatrix>(r, mat));
     }
 
+    void setHasHolePunches(bool hasHolePunches) {
+        mHasHolePunches = hasHolePunches;
+    }
+
+    bool hasHolePunches() {
+        return mHasHolePunches;
+    }
+
     std::vector<AnimatedImageDrawable*> mAnimatedImages;
     DisplayListData mDisplayList;
 
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 61f9960..82814de 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -53,6 +53,27 @@
 
     mDisplayList->attachRecorder(&mRecorder, SkIRect::MakeWH(width, height));
     SkiaCanvas::reset(&mRecorder);
+    mDisplayList->setHasHolePunches(false);
+}
+
+void SkiaRecordingCanvas::punchHole(const SkRRect& rect) {
+    // Add the marker annotation to allow HWUI to determine where the current
+    // clip/transformation should be applied
+    SkVector vector = rect.getSimpleRadii();
+    const int dataSize = 2;
+    float data[dataSize];
+    data[0] = vector.x();
+    data[1] = vector.y();
+    mRecorder.drawAnnotation(rect.rect(), HOLE_PUNCH_ANNOTATION.c_str(),
+                             SkData::MakeWithCopy(data, dataSize));
+
+    // Clear the current rect within the layer itself
+    SkPaint paint = SkPaint();
+    paint.setColor(0);
+    paint.setBlendMode(SkBlendMode::kClear);
+    mRecorder.drawRRect(rect, paint);
+
+    mDisplayList->setHasHolePunches(true);
 }
 
 std::unique_ptr<SkiaDisplayList> SkiaRecordingCanvas::finishRecording() {
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index ff03e0c..06f2a27 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -15,6 +15,7 @@
  */
 #pragma once
 
+#include "HolePunch.h"
 #include "RecordingCanvas.h"
 #include "ReorderBarrierDrawables.h"
 #include "SkiaCanvas.h"
@@ -43,6 +44,8 @@
         initDisplayList(renderNode, width, height);
     }
 
+    virtual void punchHole(const SkRRect& rect) override;
+
     virtual void finishRecording(uirenderer::RenderNode* destination) override;
     std::unique_ptr<SkiaDisplayList> finishRecording();
 
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp
new file mode 100644
index 0000000..6bfbb0d
--- /dev/null
+++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+#include "TransformCanvas.h"
+#include "HolePunch.h"
+#include "SkData.h"
+#include "SkDrawable.h"
+
+using namespace android::uirenderer::skiapipeline;
+
+void TransformCanvas::onDrawAnnotation(const SkRect& rect, const char* key, SkData* value) {
+    if (HOLE_PUNCH_ANNOTATION == key) {
+        auto* rectParams = static_cast<const float*>(value->data());
+        float radiusX = rectParams[0];
+        float radiusY = rectParams[1];
+        SkRRect roundRect = SkRRect::MakeRectXY(rect, radiusX, radiusY);
+
+        SkPaint paint;
+        paint.setColor(0);
+        paint.setBlendMode(SkBlendMode::kClear);
+        mWrappedCanvas->drawRRect(roundRect, paint);
+    }
+}
+
+void TransformCanvas::onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) {
+    drawable->draw(this, matrix);
+}
+
+bool TransformCanvas::onFilter(SkPaint& paint) const {
+    return false;
+}
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.h b/libs/hwui/pipeline/skia/TransformCanvas.h
new file mode 100644
index 0000000..47f77f1
--- /dev/null
+++ b/libs/hwui/pipeline/skia/TransformCanvas.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <include/core/SkCanvas.h>
+#include "SkPaintFilterCanvas.h"
+
+class TransformCanvas : public SkPaintFilterCanvas {
+public:
+    TransformCanvas(SkCanvas* target) : SkPaintFilterCanvas(target), mWrappedCanvas(target) {}
+
+protected:
+    bool onFilter(SkPaint& paint) const override;
+
+protected:
+    void onDrawAnnotation(const SkRect& rect, const char* key, SkData* value) override;
+    void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override;
+
+private:
+    // We don't own the canvas so just maintain a raw pointer to it
+    SkCanvas* mWrappedCanvas;
+};
diff --git a/libs/hwui/tests/unit/CanvasFrontendTests.cpp b/libs/hwui/tests/unit/CanvasFrontendTests.cpp
index 05b1179..4ddcf6f 100644
--- a/libs/hwui/tests/unit/CanvasFrontendTests.cpp
+++ b/libs/hwui/tests/unit/CanvasFrontendTests.cpp
@@ -124,12 +124,12 @@
 
 TEST(CanvasFrontend, drawOpTransform) {
     CanvasFrontend<CanvasOpBuffer> opCanvas(100, 100);
-    const auto& receiver = opCanvas.receiver();
+    const auto &receiver = opCanvas.receiver();
 
     auto makeDrawRect = [] {
         return CanvasOp<CanvasOpType::DrawRect>{
-            .rect = SkRect::MakeWH(50, 50),
-            .paint = SkPaint(SkColors::kBlack),
+                .rect = SkRect::MakeWH(50, 50),
+                .paint = SkPaint(SkColors::kBlack),
         };
     };
 
@@ -167,14 +167,14 @@
 
     {
         // First result should be identity
-        const auto& result = transforms[0];
+        const auto &result = transforms[0];
         EXPECT_EQ(SkMatrix::kIdentity_Mask, result.getType());
         EXPECT_EQ(SkMatrix::I(), result);
     }
 
     {
         // Should be translate 10, 10
-        const auto& result = transforms[1];
+        const auto &result = transforms[1];
         EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType());
         SkMatrix m;
         m.setTranslate(10, 10);
@@ -183,7 +183,7 @@
 
     {
         // Should be translate 10, 10 + scale 2, 4
-        const auto& result = transforms[2];
+        const auto &result = transforms[2];
         EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask, result.getType());
         SkMatrix m;
         m.setTranslate(10, 10);
@@ -193,7 +193,7 @@
 
     {
         // Should be translate 10, 10 + translate 20, 15
-        const auto& result = transforms[3];
+        const auto &result = transforms[3];
         EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType());
         SkMatrix m;
         m.setTranslate(30, 25);
@@ -202,9 +202,9 @@
 
     {
         // Should be translate 10, 10 + translate 20, 15 + rotate 90
-        const auto& result = transforms[4];
+        const auto &result = transforms[4];
         EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask,
-                result.getType());
+                  result.getType());
         SkMatrix m;
         m.setTranslate(30, 25);
         m.preRotate(90.f);
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index 54970df..a718d46 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -149,7 +149,7 @@
     CanvasOpBuffer buffer;
     EXPECT_EQ(buffer.size(), 0);
     size_t numPts = 3;
-    auto pts = sk_ref_sp(
+    auto pts = sk_sp<Points>(
           new Points({
               {32, 16},
               {48, 48},
@@ -192,7 +192,7 @@
     CanvasOpBuffer buffer;
     EXPECT_EQ(buffer.size(), 0);
     size_t numPts = 3;
-    auto pts = sk_ref_sp(
+    auto pts = sk_sp<Points>(
         new Points({
                {32, 16},
                {48, 48},
diff --git a/libs/hwui/tests/unit/EglManagerTests.cpp b/libs/hwui/tests/unit/EglManagerTests.cpp
new file mode 100644
index 0000000..f7f2406
--- /dev/null
+++ b/libs/hwui/tests/unit/EglManagerTests.cpp
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "renderthread/EglManager.h"
+#include "tests/common/TestContext.h"
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::test;
+
+TEST(EglManager, doesSurfaceLeak) {
+    EglManager eglManager;
+    eglManager.initialize();
+
+    ASSERT_TRUE(eglManager.hasEglContext());
+
+    auto colorSpace = SkColorSpace::MakeSRGB();
+    for (int i = 0; i < 100; i++) {
+        TestContext context;
+        auto result =
+                eglManager.createSurface(context.surface().get(), ColorMode::Default, colorSpace);
+        EXPECT_TRUE(result);
+        EGLSurface surface = result.unwrap();
+        eglManager.destroySurface(surface);
+    }
+
+    eglManager.destroy();
+}
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 6dd57b1..8c999c4 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -404,6 +404,7 @@
     EXPECT_TRUE(pipeline->isSurfaceReady());
     renderThread.destroyRenderingContext();
     EXPECT_FALSE(pipeline->isSurfaceReady());
+    LOG_ALWAYS_FATAL_IF(pipeline->isSurfaceReady());
 }
 
 RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) {
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index e0f6379..7433cf9 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -432,14 +432,27 @@
                                      const ARect& destination, int32_t transform) {
     CHECK_NOT_NULL(aSurfaceTransaction);
     CHECK_NOT_NULL(aSurfaceControl);
-    CHECK_VALID_RECT(source);
     CHECK_VALID_RECT(destination);
 
+    Rect sourceRect = static_cast<const Rect&>(source);
+    // Adjust the source so its top and left are not negative
+    sourceRect.left = std::max(sourceRect.left, 0);
+    sourceRect.top = std::max(sourceRect.top, 0);
+    LOG_ALWAYS_FATAL_IF(sourceRect.isEmpty(), "invalid arg passed as source argument");
+
     sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
     Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
 
-    transaction->setCrop(surfaceControl, static_cast<const Rect&>(source));
-    transaction->setFrame(surfaceControl, static_cast<const Rect&>(destination));
+    transaction->setCrop(surfaceControl, sourceRect);
+
+    float dsdx = (destination.right - destination.left) /
+            static_cast<float>(sourceRect.right - sourceRect.left);
+    float dsdy = (destination.bottom - destination.top) /
+            static_cast<float>(sourceRect.bottom - sourceRect.top);
+
+    transaction->setPosition(surfaceControl, destination.left - (sourceRect.left * dsdx),
+                             destination.top - (sourceRect.top * dsdy));
+    transaction->setMatrix(surfaceControl, dsdx, 0, 0, dsdy);
     transaction->setTransform(surfaceControl, transform);
     bool transformToInverseDisplay = (NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY & transform) ==
             NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
@@ -458,16 +471,18 @@
     transaction->setCrop(surfaceControl, static_cast<const Rect&>(source));
 }
 
-void ASurfaceTransaction_setPosition(ASurfaceTransaction* aSurfaceTransaction,
-                                     ASurfaceControl* aSurfaceControl, const ARect& destination) {
-    CHECK_NOT_NULL(aSurfaceTransaction);
+void ASurfaceTransaction_setPosition(ASurfaceTransaction* /* aSurfaceTransaction */,
+                                     ASurfaceControl* /* aSurfaceControl */,
+                                     const ARect& /* destination */) {
+    // TODO: Fix this function
+    /* CHECK_NOT_NULL(aSurfaceTransaction);
     CHECK_NOT_NULL(aSurfaceControl);
     CHECK_VALID_RECT(destination);
 
     sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
     Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
 
-    transaction->setFrame(surfaceControl, static_cast<const Rect&>(destination));
+    transaction->setFrame(surfaceControl, static_cast<const Rect&>(destination));*/
 }
 
 void ASurfaceTransaction_setTransform(ASurfaceTransaction* aSurfaceTransaction,
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index b77d821..e2db2d6 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -3475,6 +3475,8 @@
          * not include location sensitive info.
          * </p>
          */
+        // Note: Some existing fields which are location sensitive may still be included without
+        // this flag if the app targets SDK < S (to maintain backwards compatibility).
         public static final int FLAG_INCLUDE_LOCATION_INFO = 1 << 0;
 
         /** @hide */
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 9c4c0e2..c4277c3 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -600,8 +600,9 @@
         // TODO: Consider adding unwanted capabilities to the public API and mention this
         // in the documentation.
         checkValidCapability(capability);
-        mNetworkCapabilities |= 1 << capability;
-        mUnwantedNetworkCapabilities &= ~(1 << capability);  // remove from unwanted capability list
+        mNetworkCapabilities |= 1L << capability;
+        // remove from unwanted capability list
+        mUnwantedNetworkCapabilities &= ~(1L << capability);
         return this;
     }
 
@@ -620,8 +621,8 @@
      */
     public void addUnwantedCapability(@NetCapability int capability) {
         checkValidCapability(capability);
-        mUnwantedNetworkCapabilities |= 1 << capability;
-        mNetworkCapabilities &= ~(1 << capability);  // remove from requested capabilities
+        mUnwantedNetworkCapabilities |= 1L << capability;
+        mNetworkCapabilities &= ~(1L << capability);  // remove from requested capabilities
     }
 
     /**
@@ -634,7 +635,7 @@
      */
     public @NonNull NetworkCapabilities removeCapability(@NetCapability int capability) {
         checkValidCapability(capability);
-        final long mask = ~(1 << capability);
+        final long mask = ~(1L << capability);
         mNetworkCapabilities &= mask;
         return this;
     }
@@ -649,7 +650,7 @@
      */
     public @NonNull NetworkCapabilities removeUnwantedCapability(@NetCapability int capability) {
         checkValidCapability(capability);
-        mUnwantedNetworkCapabilities &= ~(1 << capability);
+        mUnwantedNetworkCapabilities &= ~(1L << capability);
         return this;
     }
 
@@ -717,14 +718,14 @@
      */
     public boolean hasCapability(@NetCapability int capability) {
         return isValidCapability(capability)
-                && ((mNetworkCapabilities & (1 << capability)) != 0);
+                && ((mNetworkCapabilities & (1L << capability)) != 0);
     }
 
     /** @hide */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public boolean hasUnwantedCapability(@NetCapability int capability) {
         return isValidCapability(capability)
-                && ((mUnwantedNetworkCapabilities & (1 << capability)) != 0);
+                && ((mUnwantedNetworkCapabilities & (1L << capability)) != 0);
     }
 
     /**
@@ -1126,7 +1127,9 @@
      * app needs to hold {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission. If the
      * app targets SDK version greater than or equal to {@link Build.VERSION_CODES#S}, then they
      * also need to use {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} to get the info in their
-     * callback. The app will be blamed for location access if this field is included.
+     * callback. If the apps targets SDK version equal to {{@link Build.VERSION_CODES#R}, this field
+     * will always be included. The app will be blamed for location access if this field is
+     * included.
      * </p>
      */
     public int getOwnerUid() {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 4578597..95f180a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -16,6 +16,8 @@
 */
 package com.android.packageinstaller;
 
+import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY;
+import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
 import android.Manifest;
@@ -50,6 +52,8 @@
 import com.android.internal.app.AlertActivity;
 
 import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * This activity is launched when a new application is installed via side loading
@@ -92,6 +96,12 @@
     private String mCallingAttributionTag;
     ApplicationInfo mSourceInfo;
 
+    /**
+     * A collection of unknown sources listeners that are actively listening for app ops mode
+     * changes
+     */
+    private List<UnknownSourcesListener> mActiveUnknownSourcesListeners = new ArrayList<>(1);
+
     // ApplicationInfo object primarily used for already existing applications
     private ApplicationInfo mAppInfo = null;
 
@@ -381,6 +391,14 @@
         outState.putBoolean(ALLOW_UNKNOWN_SOURCES_KEY, mAllowUnknownSources);
     }
 
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        while (!mActiveUnknownSourcesListeners.isEmpty()) {
+            unregister(mActiveUnknownSourcesListeners.get(0));
+        }
+    }
+
     private void bindUi() {
         mAlert.setIcon(mAppSnippet.icon);
         mAlert.setTitle(mAppSnippet.label);
@@ -707,24 +725,61 @@
         }
     }
 
+    private class UnknownSourcesListener implements AppOpsManager.OnOpChangedListener {
+
+        @Override
+        public void onOpChanged(String op, String packageName) {
+            if (!mOriginatingPackage.equals(packageName)) {
+                return;
+            }
+            unregister(this);
+            mActiveUnknownSourcesListeners.remove(this);
+            if (isDestroyed()) {
+                return;
+            }
+            getMainThreadHandler().postDelayed(() -> {
+                if (!isDestroyed()) {
+                    startActivity(getIntent().addFlags(FLAG_ACTIVITY_REORDER_TO_FRONT));
+                }
+            }, 500);
+
+        }
+
+    }
+
+    private void register(UnknownSourcesListener listener) {
+        mAppOpsManager.startWatchingMode(
+                AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES, mOriginatingPackage,
+                listener);
+        mActiveUnknownSourcesListeners.add(listener);
+    }
+
+    private void unregister(UnknownSourcesListener listener) {
+        mAppOpsManager.stopWatchingMode(listener);
+        mActiveUnknownSourcesListeners.remove(listener);
+    }
+
     /**
      * An error dialog shown when external sources are not allowed
      */
     public static class ExternalSourcesBlockedDialog extends AppErrorDialog {
         static AppErrorDialog newInstance(@NonNull String originationPkg) {
-            ExternalSourcesBlockedDialog dialog = new ExternalSourcesBlockedDialog();
+            ExternalSourcesBlockedDialog dialog =
+                    new ExternalSourcesBlockedDialog();
             dialog.setArgument(originationPkg);
             return dialog;
         }
 
         @Override
         protected Dialog createDialog(@NonNull CharSequence argument) {
+
+            final PackageInstallerActivity activity = (PackageInstallerActivity)getActivity();
             try {
-                PackageManager pm = getActivity().getPackageManager();
+                PackageManager pm = activity.getPackageManager();
 
                 ApplicationInfo sourceInfo = pm.getApplicationInfo(argument.toString(), 0);
 
-                return new AlertDialog.Builder(getActivity())
+                return new AlertDialog.Builder(activity)
                         .setTitle(pm.getApplicationLabel(sourceInfo))
                         .setIcon(pm.getApplicationIcon(sourceInfo))
                         .setMessage(R.string.untrusted_external_source_warning)
@@ -735,8 +790,10 @@
                                             Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
                                     final Uri packageUri = Uri.parse("package:" + argument);
                                     settingsIntent.setData(packageUri);
+                                    settingsIntent.setFlags(FLAG_ACTIVITY_NO_HISTORY);
                                     try {
-                                        getActivity().startActivityForResult(settingsIntent,
+                                        activity.register(activity.new UnknownSourcesListener());
+                                        activity.startActivityForResult(settingsIntent,
                                                 REQUEST_TRUST_EXTERNAL_SOURCE);
                                     } catch (ActivityNotFoundException exc) {
                                         Log.e(TAG, "Settings activity not found for action: "
@@ -744,11 +801,11 @@
                                     }
                                 })
                         .setNegativeButton(R.string.cancel,
-                                (dialog, which) -> getActivity().finish())
+                                (dialog, which) -> activity.finish())
                         .create();
             } catch (NameNotFoundException e) {
                 Log.e(TAG, "Did not find app info for " + argument);
-                getActivity().finish();
+                activity.finish();
                 return null;
             }
         }
diff --git a/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml b/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
new file mode 100644
index 0000000..a2bbd2b
--- /dev/null
+++ b/packages/SettingsLib/SettingsTransition/res/interpolator/fast_out_extra_slow_in.xml
@@ -0,0 +1,19 @@
+<?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
+  -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1"/>
diff --git a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java
index f99fda0..6560a18 100644
--- a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java
+++ b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java
@@ -23,6 +23,8 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 
+import androidx.core.os.BuildCompat;
+
 import com.google.android.material.transition.platform.MaterialSharedAxis;
 import com.google.android.material.transition.platform.SlideDistanceProvider;
 
@@ -47,8 +49,7 @@
         transition.setDuration(DURATION);
 
         final Interpolator interpolator =
-                AnimationUtils.loadInterpolator(context,
-                        android.R.interpolator.fast_out_extra_slow_in);
+                AnimationUtils.loadInterpolator(context, R.interpolator.fast_out_extra_slow_in);
         transition.setInterpolator(interpolator);
 
         // TODO(b/177480673): Update fade through threshold once (cl/362065364) is released
@@ -64,6 +65,9 @@
      * triggered when the page is launched/entering.
      */
     public static void applyForwardTransition(Activity activity) {
+        if (!BuildCompat.isAtLeastS()) {
+            return;
+        }
         if (activity == null) {
             Log.w(TAG, "applyForwardTransition: Invalid activity!");
             return;
@@ -87,6 +91,9 @@
      * previously-started Activity.
      */
     public static void applyBackwardTransition(Activity activity) {
+        if (!BuildCompat.isAtLeastS()) {
+            return;
+        }
         if (activity == null) {
             Log.w(TAG, "applyBackwardTransition: Invalid activity!");
             return;
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
index 0a70f72..673f243 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
@@ -41,6 +41,7 @@
     private static final long ONE_DAY_MILLIS = TimeUnit.DAYS.toMillis(1);
     private static final long TWO_DAYS_MILLIS = TimeUnit.DAYS.toMillis(2);
     private static final long ONE_HOUR_MILLIS = TimeUnit.HOURS.toMillis(1);
+    private static final long ONE_MIN_MILLIS = TimeUnit.MINUTES.toMillis(1);
 
     /**
      * This method produces the text used in various places throughout the system to describe the
@@ -63,7 +64,7 @@
                 // show a less than 15 min remaining warning if appropriate
                 CharSequence timeString = StringUtil.formatElapsedTime(context,
                         FIFTEEN_MINUTES_MILLIS,
-                        false /* withSeconds */);
+                        false /* withSeconds */, false /* collapseTimeUnit */);
                 return getUnderFifteenString(context, timeString, percentageString);
             } else if (drainTimeMs >= TWO_DAYS_MILLIS) {
                 // just say more than two day if over 48 hours
@@ -151,7 +152,7 @@
         final long roundedTimeMs = roundTimeToNearestThreshold(drainTimeMs, ONE_HOUR_MILLIS);
         CharSequence timeString = StringUtil.formatElapsedTime(context,
                 roundedTimeMs,
-                false /* withSeconds */);
+                false /* withSeconds */, true /* collapseTimeUnit */);
 
         if (TextUtils.isEmpty(percentageString)) {
             int id = basedOnUsage
@@ -170,7 +171,7 @@
             int resId) {
         final long roundedTimeMs = roundTimeToNearestThreshold(drainTimeMs, ONE_HOUR_MILLIS);
         CharSequence timeString = StringUtil.formatElapsedTime(context, roundedTimeMs,
-                false /* withSeconds */);
+                false /* withSeconds */, false /* collapseTimeUnit */);
 
         return context.getString(resId, timeString);
     }
@@ -193,17 +194,18 @@
     private static String getRegularTimeRemainingString(Context context, long drainTimeMs,
             String percentageString, boolean basedOnUsage) {
 
-        CharSequence timeString = getDateTimeStringFromMs(context, drainTimeMs);
+        CharSequence timeString = StringUtil.formatElapsedTime(context,
+                drainTimeMs, false /* withSeconds */, true /* collapseTimeUnit */);
 
         if (TextUtils.isEmpty(percentageString)) {
             int id = basedOnUsage
-                    ? R.string.power_discharge_by_only_enhanced
-                    : R.string.power_discharge_by_only;
+                    ? R.string.power_remaining_duration_only_enhanced
+                    : R.string.power_remaining_duration_only;
             return context.getString(id, timeString);
         } else {
             int id = basedOnUsage
-                    ? R.string.power_discharge_by_enhanced
-                    : R.string.power_discharge_by;
+                    ? R.string.power_discharging_duration_enhanced
+                    : R.string.power_discharging_duration;
             return context.getString(id, timeString, percentageString);
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
index 83ef4f9..b65637f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
@@ -40,6 +40,8 @@
     public static final int SECONDS_PER_HOUR = 60 * 60;
     public static final int SECONDS_PER_DAY = 24 * 60 * 60;
 
+    private static final int LIMITED_TIME_UNIT_COUNT = 2;
+
     /**
      * Returns elapsed time for the given millis, in the following format:
      * 2 days, 5 hr, 40 min, 29 sec
@@ -47,10 +49,12 @@
      * @param context     the application context
      * @param millis      the elapsed time in milli seconds
      * @param withSeconds include seconds?
+     * @param collapseTimeUnit limit the output to top 2 time unit
+     *                         e.g 2 days, 5 hr, 40 min, 29 sec will convert to 2 days, 5 hr
      * @return the formatted elapsed time
      */
     public static CharSequence formatElapsedTime(Context context, double millis,
-            boolean withSeconds) {
+            boolean withSeconds, boolean collapseTimeUnit) {
         SpannableStringBuilder sb = new SpannableStringBuilder();
         int seconds = (int) Math.floor(millis / 1000);
         if (!withSeconds) {
@@ -89,6 +93,12 @@
             // Everything addable was zero, so nothing was added. We add a zero.
             measureList.add(new Measure(0, withSeconds ? MeasureUnit.SECOND : MeasureUnit.MINUTE));
         }
+
+        if (collapseTimeUnit && measureList.size() > LIMITED_TIME_UNIT_COUNT) {
+            // Limit the output to top 2 time unit.
+            measureList.subList(LIMITED_TIME_UNIT_COUNT, measureList.size()).clear();
+        }
+
         final Measure[] measureArray = measureList.toArray(new Measure[measureList.size()]);
 
         final Locale locale = context.getResources().getConfiguration().locale;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index 4b779ac..3029736 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -35,10 +35,12 @@
 @RunWith(RobolectricTestRunner.class)
 public class PowerUtilTest {
     private static final String TEST_BATTERY_LEVEL_10 = "10%";
+    private static final long TEN_SEC_MILLIS = Duration.ofSeconds(10).toMillis();
     private static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
     private static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
     private static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
     private static final long THREE_DAYS_MILLIS = Duration.ofDays(3).toMillis();
+    private static final long TEN_HOURS_MILLIS = Duration.ofHours(10).toMillis();
     private static final long THIRTY_HOURS_MILLIS = Duration.ofHours(30).toMillis();
     private static final String NORMAL_CASE_EXPECTED_PREFIX = "Should last until about";
     private static final String ENHANCED_SUFFIX = " based on your usage";
@@ -152,11 +154,40 @@
                 THIRTY_HOURS_MILLIS,
                 TEST_BATTERY_LEVEL_10 /* percentageString */,
                 false /* basedOnUsage */);
+        String info3 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                THIRTY_HOURS_MILLIS + TEN_MINUTES_MILLIS,
+                null /* percentageString */,
+                false /* basedOnUsage */);
 
         // We only add special mention for the long string
         assertThat(info).isEqualTo("About 1 day, 6 hr left based on your usage");
         // shortened string should not have extra text
         assertThat(info2).isEqualTo("About 1 day, 6 hr left (10%)");
+        // present 2 time unit at most
+        assertThat(info3).isEqualTo("About 1 day, 6 hr left");
+    }
+
+    @Test
+    public void testGetBatteryRemainingStringFormatted_lessThanOneDay_usesCorrectString() {
+        String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                TEN_HOURS_MILLIS,
+                null /* percentageString */,
+                true /* basedOnUsage */);
+        String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                TEN_HOURS_MILLIS,
+                TEST_BATTERY_LEVEL_10 /* percentageString */,
+                false /* basedOnUsage */);
+        String info3 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+                TEN_HOURS_MILLIS + TEN_MINUTES_MILLIS + TEN_SEC_MILLIS,
+                null /* percentageString */,
+                false /* basedOnUsage */);
+
+        // We only add special mention for the long string
+        assertThat(info).isEqualTo("About 10 hr left based on your usage");
+        // shortened string should not have extra text
+        assertThat(info2).isEqualTo("About 10 hr left (10%)");
+        // present 2 time unit at most
+        assertThat(info3).isEqualTo("About 10 hr, 10 min left");
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
index b503972..6a1d326 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
@@ -46,7 +46,7 @@
         final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
         final String expectedTime = "5 min, 30 sec";
 
-        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true).toString())
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true, false).toString())
                 .isEqualTo(expectedTime);
     }
 
@@ -55,7 +55,7 @@
         final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
         final String expectedTime = "6 min";
 
-        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false, false).toString())
                 .isEqualTo(expectedTime);
     }
 
@@ -65,7 +65,17 @@
                 + 4 * DateUtils.HOUR_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
         final String expectedTime = "2 days, 4 hr, 15 min";
 
-        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false, false).toString())
+                .isEqualTo(expectedTime);
+    }
+
+    @Test
+    public void testFormatElapsedTime_TimeMoreThanOneDayAndCollapseTimeUnit_ShowCorrectly() {
+        final double testMillis = 2 * DateUtils.DAY_IN_MILLIS
+                + 4 * DateUtils.HOUR_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
+        final String expectedTime = "2 days, 4 hr";
+
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false, true).toString())
                 .isEqualTo(expectedTime);
     }
 
@@ -74,7 +84,7 @@
         final double testMillis = 2 * DateUtils.DAY_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
         final String expectedTime = "2 days, 15 min";
 
-        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false, false).toString())
                 .isEqualTo(expectedTime);
     }
 
@@ -83,7 +93,7 @@
         final double testMillis = 0;
         final String expectedTime = "0 sec";
 
-        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true).toString())
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true, false).toString())
                 .isEqualTo(expectedTime);
     }
 
@@ -92,7 +102,7 @@
         final double testMillis = 0;
         final String expectedTime = "0 min";
 
-        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+        assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false, false).toString())
                 .isEqualTo(expectedTime);
     }
 
@@ -101,7 +111,7 @@
         final double testMillis = 15 * DateUtils.MINUTE_IN_MILLIS;
 
         final CharSequence charSequence =
-                StringUtil.formatElapsedTime(mContext, testMillis, false);
+                StringUtil.formatElapsedTime(mContext, testMillis, false, false);
         assertThat(charSequence).isInstanceOf(SpannableStringBuilder.class);
 
         final SpannableStringBuilder expectedString = (SpannableStringBuilder) charSequence;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 4ff3c55..5a3298d 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -405,6 +405,9 @@
     <!-- Permission required for CTS test - CtsHdmiCecHostTestCases -->
     <uses-permission android:name="android.permission.HDMI_CEC" />
 
+    <!-- Permission needed for CTS test - MediaPlayerTest -->
+    <uses-permission android:name="android.permission.BIND_IMS_SERVICE" />
+
     <!-- Permission needed for CTS test - WifiManagerTest -->
     <uses-permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" />
     <uses-permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index a235935..6574353 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -70,7 +70,6 @@
         "res",
     ],
     static_libs: [
-        "bcsmartspace",
         "WindowManager-Shell",
         "SystemUIPluginLib",
         "SystemUISharedLib",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e97d279..88d7710 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -275,8 +275,7 @@
     <!-- Permission to make accessibility service access Bubbles -->
     <uses-permission android:name="android.permission.ADD_TRUSTED_DISPLAY" />
 
-    <!-- Permission for Smartspace. -->
-    <uses-permission android:name="android.permission.MANAGE_SMARTSPACE"/>
+    <uses-permission android:name="android.permission.MANAGE_SMARTSPACE" />
 
 
     <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 6c7a5b8..0d18b8d 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -24,6 +24,30 @@
             "exclude-annotation": "android.platform.test.annotations.Postsubmit"
         }
       ]
+    },
+    {
+      // Permission indicators
+      "name": "CtsPermission4TestCases",
+      "options": [
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      // Permission indicators
+      "name": "CtsVoiceRecognitionTestCases",
+      "options": [
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
     }
   ],
 
diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp
index d6204db..b3aba22 100644
--- a/packages/SystemUI/plugin/Android.bp
+++ b/packages/SystemUI/plugin/Android.bp
@@ -27,6 +27,7 @@
 
     srcs: [
         "src/**/*.java",
+        "src/**/*.kt",
         "bcsmartspace/src/**/*.java",
     ],
 
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index f8a9a045..35423a9 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -16,7 +16,9 @@
 
 package com.android.systemui.plugins;
 
+import android.app.smartspace.SmartspaceTarget;
 import android.os.Parcelable;
+import android.view.ViewGroup;
 
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
@@ -36,9 +38,25 @@
     /** Unregister a listener. */
     void unregisterListener(SmartspaceTargetListener listener);
 
+    /**
+     * Create a view to be shown within the parent. Do not add the view, as the parent
+     * will be responsible for correctly setting the LayoutParams
+     */
+    default SmartspaceView getView(ViewGroup parent) {
+        return null;
+    }
+
+    /** Updates Smartspace data and propagates it to any listeners. */
+    void onTargetsAvailable(List<SmartspaceTarget> targets);
+
     /** Provides Smartspace data to registered listeners. */
     interface SmartspaceTargetListener {
         /** Each Parcelable is a SmartspaceTarget that represents a card. */
         void onSmartspaceTargetsUpdated(List<? extends Parcelable> targets);
     }
+
+    /** View to which this plugin can be registered, in order to get updates. */
+    interface SmartspaceView {
+        void registerDataProvider(BcSmartspaceDataPlugin plugin);
+    }
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 25a3fa2..055fe37 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -19,6 +19,7 @@
 import android.content.Intent;
 import android.view.View;
 
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
 /**
@@ -44,7 +45,15 @@
      * specifies an associated view that should be used for the activity launch animation.
      */
     void startPendingIntentDismissingKeyguard(PendingIntent intent,
-            Runnable intentSentUiThreadCallback, View associatedView);
+            Runnable intentSentUiThreadCallback, @Nullable View associatedView);
+
+    /**
+     * Similar to {@link #startPendingIntentDismissingKeyguard(PendingIntent, Runnable)}, but also
+     * specifies an animation controller that should be used for the activity launch animation.
+     */
+    void startPendingIntentDismissingKeyguard(PendingIntent intent,
+            Runnable intentSentUiThreadCallback,
+            @Nullable ActivityLaunchAnimator.Controller animationController);
 
     /**
      * The intent flag can be specified in startActivity().
@@ -54,7 +63,17 @@
     void startActivity(Intent intent, boolean onlyProvisioned, boolean dismissShade);
     void startActivity(Intent intent, boolean dismissShade, Callback callback);
     void postStartActivityDismissingKeyguard(Intent intent, int delay);
+    void postStartActivityDismissingKeyguard(Intent intent, int delay,
+            @Nullable ActivityLaunchAnimator.Controller animationController);
     void postStartActivityDismissingKeyguard(PendingIntent intent);
+
+    /**
+     * Similar to {@link #postStartActivityDismissingKeyguard(PendingIntent)}, but also specifies an
+     * animation controller that should be used for the activity launch animation.
+     */
+    void postStartActivityDismissingKeyguard(PendingIntent intent,
+            @Nullable ActivityLaunchAnimator.Controller animationController);
+
     void postQSRunnableDismissingKeyguard(Runnable runnable);
 
     void dismissKeyguardThenExecute(OnDismissAction action, @Nullable Runnable cancel,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/animation/ActivityLaunchAnimator.kt
new file mode 100644
index 0000000..5af8dab
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/animation/ActivityLaunchAnimator.kt
@@ -0,0 +1,479 @@
+package com.android.systemui.plugins.animation
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.app.ActivityManager
+import android.app.PendingIntent
+import android.graphics.Matrix
+import android.graphics.Rect
+import android.os.RemoteException
+import android.util.MathUtils
+import android.view.IRemoteAnimationFinishedCallback
+import android.view.IRemoteAnimationRunner
+import android.view.RemoteAnimationAdapter
+import android.view.RemoteAnimationTarget
+import android.view.SyncRtSurfaceTransactionApplier
+import android.view.View
+import android.view.WindowManager
+import android.view.animation.LinearInterpolator
+import android.view.animation.PathInterpolator
+import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.policy.ScreenDecorationsUtils
+import kotlin.math.roundToInt
+
+/**
+ * A class that allows activities to be started in a seamless way from a view that is transforming
+ * nicely into the starting window.
+ */
+class ActivityLaunchAnimator {
+    companion object {
+        const val ANIMATION_DURATION = 400L
+        const val ANIMATION_DURATION_FADE_OUT_CONTENT = 67L
+        const val ANIMATION_DURATION_FADE_IN_WINDOW = 200L
+        private const val ANIMATION_DURATION_NAV_FADE_IN = 266L
+        private const val ANIMATION_DURATION_NAV_FADE_OUT = 133L
+        private const val ANIMATION_DELAY_NAV_FADE_IN =
+                ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN
+        private const val LAUNCH_TIMEOUT = 1000L
+
+        // TODO(b/184121838): Use android.R.interpolator.fast_out_extra_slow_in instead.
+        // TODO(b/184121838): Move com.android.systemui.Interpolators in an animation library we can
+        // reuse here.
+        private val ANIMATION_INTERPOLATOR = PathInterpolator(0.4f, 0f, 0.2f, 1f)
+        private val LINEAR_INTERPOLATOR = LinearInterpolator()
+        private val ALPHA_IN_INTERPOLATOR = PathInterpolator(0.4f, 0f, 1f, 1f)
+        private val ALPHA_OUT_INTERPOLATOR = PathInterpolator(0f, 0f, 0.8f, 1f)
+        private val NAV_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0f, 1f)
+        private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
+
+        /**
+         * Given the [linearProgress] of a launch animation, return the linear progress of the
+         * sub-animation starting [delay] ms after the launch animation and that lasts [duration].
+         */
+        @JvmStatic
+        fun getProgress(linearProgress: Float, delay: Long, duration: Long): Float {
+            return MathUtils.constrain(
+                    (linearProgress * ANIMATION_DURATION - delay) / duration,
+                    0.0f,
+                    1.0f
+            )
+        }
+    }
+
+    /**
+     * Start an intent and animate the opening window. The intent will be started by running
+     * [intentStarter], which should use the provided [RemoteAnimationAdapter] and return the launch
+     * result. [controller] is responsible from animating the view from which the intent was started
+     * in [Controller.onLaunchAnimationProgress]. No animation will start if there is no window
+     * opening.
+     *
+     * If [controller] is null, then the intent will be started and no animation will run.
+     *
+     * This method will throw any exception thrown by [intentStarter].
+     */
+    inline fun startIntentWithAnimation(
+        controller: Controller?,
+        intentStarter: (RemoteAnimationAdapter?) -> Int
+    ) {
+        if (controller == null) {
+            intentStarter(null)
+            return
+        }
+
+        val runner = Runner(controller)
+        val animationAdapter = RemoteAnimationAdapter(
+            runner,
+            ANIMATION_DURATION,
+            ANIMATION_DURATION - 150 /* statusBarTransitionDelay */
+        )
+        val launchResult = intentStarter(animationAdapter)
+        val willAnimate = launchResult == ActivityManager.START_TASK_TO_FRONT ||
+            launchResult == ActivityManager.START_SUCCESS
+        runner.context.mainExecutor.execute { controller.onIntentStarted(willAnimate) }
+
+        // If we expect an animation, post a timeout to cancel it in case the remote animation is
+        // never started.
+        if (willAnimate) {
+            runner.postTimeout()
+        }
+    }
+
+    /**
+     * Same as [startIntentWithAnimation] but allows [intentStarter] to throw a
+     * [PendingIntent.CanceledException] which must then be handled by the caller. This is useful
+     * for Java caller starting a [PendingIntent].
+     */
+    @Throws(PendingIntent.CanceledException::class)
+    fun startPendingIntentWithAnimation(
+        controller: Controller?,
+        intentStarter: PendingIntentStarter
+    ) {
+        startIntentWithAnimation(controller) { intentStarter.startPendingIntent(it) }
+    }
+
+    interface PendingIntentStarter {
+        /**
+         * Start a pending intent using the provided [animationAdapter] and return the launch
+         * result.
+         */
+        @Throws(PendingIntent.CanceledException::class)
+        fun startPendingIntent(animationAdapter: RemoteAnimationAdapter?): Int
+    }
+
+    /**
+     * A controller that takes care of applying the animation to an expanding view.
+     *
+     * Note that all callbacks (onXXX methods) are all called on the main thread.
+     */
+    interface Controller {
+        companion object {
+            /**
+             * Return a [Controller] that will animate and expand [view] into the opening window.
+             *
+             * Important: The view must be attached to the window when calling this function and
+             * during the animation.
+             */
+            @JvmStatic
+            fun fromView(view: View): Controller = GhostedViewLaunchAnimatorController(view)
+        }
+
+        /**
+         * Return the root [View] that contains the view that started the intent and will be
+         * animating together with the window.
+         *
+         * This view will be used to:
+         *  - Get the associated [Context].
+         *  - Compute whether we are expanding fully above the current window.
+         *  - Apply surface transactions in sync with RenderThread.
+         */
+        fun getRootView(): View
+
+        /**
+         * Return the [State] of the view that will be animated. We will animate from this state to
+         * the final window state.
+         *
+         * Note: This state will be mutated and passed to [onLaunchAnimationProgress] during the
+         * animation.
+         */
+        fun createAnimatorState(): State
+
+        /**
+         * The intent was started. If [willAnimate] is false, nothing else will happen and the
+         * animation will not be started.
+         */
+        fun onIntentStarted(willAnimate: Boolean) {}
+
+        /**
+         * The animation started. This is typically used to initialize any additional resource
+         * needed for the animation. [isExpandingFullyAbove] will be true if the window is expanding
+         * fully above the [root view][getRootView].
+         */
+        fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {}
+
+        /** The animation made progress and the expandable view [state] should be updated. */
+        fun onLaunchAnimationProgress(state: State, progress: Float, linearProgress: Float) {}
+
+        /**
+         * The animation ended. This will be called *if and only if* [onLaunchAnimationStart] was
+         * called previously. This is typically used to clean up the resources initialized when the
+         * animation was started.
+         */
+        fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {}
+
+        /**
+         * The animation was cancelled remotely. Note that [onLaunchAnimationEnd] will still be
+         * called after this if the animation was already started, i.e. if [onLaunchAnimationStart]
+         * was called before the cancellation.
+         */
+        fun onLaunchAnimationCancelled() {}
+
+        /**
+         * The remote animation was not started within the expected time. It timed out and will
+         * never [start][onLaunchAnimationStart].
+         */
+        fun onLaunchAnimationTimedOut() {}
+
+        /**
+         * The animation was aborted because the opening window was not found. It will never
+         * [start][onLaunchAnimationStart].
+         */
+        fun onLaunchAnimationAborted() {}
+    }
+
+    /** The state of an expandable view during an [ActivityLaunchAnimator] animation. */
+    open class State(
+        /** The position of the view in screen space coordinates. */
+        var top: Int,
+        var bottom: Int,
+        var left: Int,
+        var right: Int,
+
+        var topCornerRadius: Float = 0f,
+        var bottomCornerRadius: Float = 0f,
+
+        var contentAlpha: Float = 1f,
+        var backgroundAlpha: Float = 1f
+    ) {
+        private val startTop = top
+        private val startLeft = left
+        private val startRight = right
+
+        val width: Int
+            get() = right - left
+
+        val height: Int
+            get() = bottom - top
+
+        open val topChange: Int
+            get() = top - startTop
+
+        val leftChange: Int
+            get() = left - startLeft
+
+        val rightChange: Int
+            get() = right - startRight
+    }
+
+    @VisibleForTesting
+    class Runner(private val controller: Controller) : IRemoteAnimationRunner.Stub() {
+        private val rootView = controller.getRootView()
+        @PublishedApi internal val context = rootView.context
+        private val transactionApplier = SyncRtSurfaceTransactionApplier(rootView)
+        private var animator: ValueAnimator? = null
+
+        private var windowCrop = Rect()
+        private var timedOut = false
+        private var cancelled = false
+
+        // A timeout to cancel the remote animation if it is not started within X milliseconds after
+        // the intent was started.
+        //
+        // Note that this is important to keep this a Runnable (and not a Kotlin lambda), otherwise
+        // it will be automatically converted when posted and we wouldn't be able to remove it after
+        // posting it.
+        private var onTimeout = Runnable { onAnimationTimedOut() }
+
+        @PublishedApi
+        internal fun postTimeout() {
+            rootView.postDelayed(onTimeout, LAUNCH_TIMEOUT)
+        }
+
+        private fun removeTimeout() {
+            rootView.removeCallbacks(onTimeout)
+        }
+
+        override fun onAnimationStart(
+            @WindowManager.TransitionOldType transit: Int,
+            remoteAnimationTargets: Array<out RemoteAnimationTarget>,
+            remoteAnimationWallpaperTargets: Array<out RemoteAnimationTarget>,
+            remoteAnimationNonAppTargets: Array<out RemoteAnimationTarget>,
+            iRemoteAnimationFinishedCallback: IRemoteAnimationFinishedCallback
+        ) {
+            removeTimeout()
+
+            // The animation was started too late and we already notified the controller that it
+            // timed out.
+            if (timedOut) {
+                invokeCallback(iRemoteAnimationFinishedCallback)
+                return
+            }
+
+            // This should not happen, but let's make sure we don't start the animation if it was
+            // cancelled before and we already notified the controller.
+            if (cancelled) {
+                return
+            }
+
+            context.mainExecutor.execute {
+                startAnimation(remoteAnimationTargets, iRemoteAnimationFinishedCallback)
+            }
+        }
+
+        private fun startAnimation(
+            remoteAnimationTargets: Array<out RemoteAnimationTarget>,
+            iCallback: IRemoteAnimationFinishedCallback
+        ) {
+            val window = remoteAnimationTargets.firstOrNull {
+                it.mode == RemoteAnimationTarget.MODE_OPENING
+            }
+
+            if (window == null) {
+                removeTimeout()
+                invokeCallback(iCallback)
+                controller.onLaunchAnimationAborted()
+                return
+            }
+
+            val navigationBar = remoteAnimationTargets.firstOrNull {
+                it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
+            }
+
+            // Start state.
+            val state = controller.createAnimatorState()
+
+            val startTop = state.top
+            val startBottom = state.bottom
+            val startLeft = state.left
+            val startRight = state.right
+
+            val startTopCornerRadius = state.topCornerRadius
+            val startBottomCornerRadius = state.bottomCornerRadius
+
+            // End state.
+            val windowBounds = window.screenSpaceBounds
+            val endTop = windowBounds.top
+            val endBottom = windowBounds.bottom
+            val endLeft = windowBounds.left
+            val endRight = windowBounds.right
+
+            // TODO(b/184121838): Ensure that we are launching on the same screen.
+            val rootViewLocation = rootView.locationOnScreen
+            val isExpandingFullyAbove = endTop <= rootViewLocation[1] &&
+                endBottom >= rootViewLocation[1] + rootView.height &&
+                endLeft <= rootViewLocation[0] &&
+                endRight >= rootViewLocation[0] + rootView.width
+
+            // TODO(b/184121838): We should somehow get the top and bottom radius of the window.
+            val endRadius = if (isExpandingFullyAbove) {
+                // Most of the time, expanding fully above the root view means expanding in full
+                // screen.
+                ScreenDecorationsUtils.getWindowCornerRadius(context.resources)
+            } else {
+                // This usually means we are in split screen mode, so 2 out of 4 corners will have
+                // a radius of 0.
+                0f
+            }
+
+            // Update state.
+            val animator = ValueAnimator.ofFloat(0f, 1f)
+            this.animator = animator
+            animator.duration = ANIMATION_DURATION
+            animator.interpolator = LINEAR_INTERPOLATOR
+
+            animator.addListener(object : AnimatorListenerAdapter() {
+                override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
+                    controller.onLaunchAnimationStart(isExpandingFullyAbove)
+                }
+
+                override fun onAnimationEnd(animation: Animator?) {
+                    invokeCallback(iCallback)
+                    controller.onLaunchAnimationEnd(isExpandingFullyAbove)
+                }
+            })
+
+            animator.addUpdateListener { animation ->
+                if (cancelled) {
+                    return@addUpdateListener
+                }
+
+                val linearProgress = animation.animatedFraction
+                val progress = ANIMATION_INTERPOLATOR.getInterpolation(linearProgress)
+
+                state.top = lerp(startTop, endTop, progress).roundToInt()
+                state.bottom = lerp(startBottom, endBottom, progress).roundToInt()
+                state.left = lerp(startLeft, endLeft, progress).roundToInt()
+                state.right = lerp(startRight, endRight, progress).roundToInt()
+
+                state.topCornerRadius = MathUtils.lerp(startTopCornerRadius, endRadius, progress)
+                state.bottomCornerRadius =
+                    MathUtils.lerp(startBottomCornerRadius, endRadius, progress)
+
+                val contentAlphaProgress = getProgress(linearProgress, 0,
+                        ANIMATION_DURATION_FADE_OUT_CONTENT)
+                state.contentAlpha =
+                        1 - ALPHA_OUT_INTERPOLATOR.getInterpolation(contentAlphaProgress)
+
+                val backgroundAlphaProgress = getProgress(linearProgress,
+                        ANIMATION_DURATION_FADE_OUT_CONTENT, ANIMATION_DURATION_FADE_IN_WINDOW)
+                state.backgroundAlpha =
+                        1 - ALPHA_IN_INTERPOLATOR.getInterpolation(backgroundAlphaProgress)
+
+                applyStateToWindow(window, state)
+                navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
+                controller.onLaunchAnimationProgress(state, progress, linearProgress)
+            }
+
+            animator.start()
+        }
+
+        private fun applyStateToWindow(window: RemoteAnimationTarget, state: State) {
+            val m = Matrix()
+            m.postTranslate(0f, (state.top - window.sourceContainerBounds.top).toFloat())
+            windowCrop.set(state.left, 0, state.right, state.height)
+
+            val cornerRadius = minOf(state.topCornerRadius, state.bottomCornerRadius)
+            val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(window.leash)
+                    .withAlpha(1f)
+                    .withMatrix(m)
+                    .withWindowCrop(windowCrop)
+                    .withLayer(window.prefixOrderIndex)
+                    .withCornerRadius(cornerRadius)
+                    .withVisibility(true)
+                    .build()
+
+            transactionApplier.scheduleApply(params)
+        }
+
+        private fun applyStateToNavigationBar(
+            navigationBar: RemoteAnimationTarget,
+            state: State,
+            linearProgress: Float
+        ) {
+            val fadeInProgress = getProgress(linearProgress, ANIMATION_DELAY_NAV_FADE_IN,
+                    ANIMATION_DURATION_NAV_FADE_OUT)
+
+            val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(navigationBar.leash)
+            if (fadeInProgress > 0) {
+                val m = Matrix()
+                m.postTranslate(0f, (state.top - navigationBar.sourceContainerBounds.top).toFloat())
+                windowCrop.set(state.left, 0, state.right, state.height)
+                params
+                        .withAlpha(NAV_FADE_IN_INTERPOLATOR.getInterpolation(fadeInProgress))
+                        .withMatrix(m)
+                        .withWindowCrop(windowCrop)
+                        .withVisibility(true)
+            } else {
+                val fadeOutProgress = getProgress(linearProgress, 0,
+                        ANIMATION_DURATION_NAV_FADE_OUT)
+                params.withAlpha(1f - NAV_FADE_OUT_INTERPOLATOR.getInterpolation(fadeOutProgress))
+            }
+
+            transactionApplier.scheduleApply(params.build())
+        }
+
+        private fun onAnimationTimedOut() {
+            if (cancelled) {
+                return
+            }
+
+            timedOut = true
+            controller.onLaunchAnimationTimedOut()
+        }
+
+        override fun onAnimationCancelled() {
+            if (timedOut) {
+                return
+            }
+
+            cancelled = true
+            removeTimeout()
+            context.mainExecutor.execute {
+                animator?.cancel()
+                controller.onLaunchAnimationCancelled()
+            }
+        }
+
+        private fun invokeCallback(iCallback: IRemoteAnimationFinishedCallback) {
+            try {
+                iCallback.onAnimationFinished()
+            } catch (e: RemoteException) {
+                e.printStackTrace()
+            }
+        }
+
+        private fun lerp(start: Int, stop: Int, amount: Float): Float {
+            return MathUtils.lerp(start.toFloat(), stop.toFloat(), amount)
+        }
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/animation/GhostedViewLaunchAnimatorController.kt
new file mode 100644
index 0000000..a5494ad
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/animation/GhostedViewLaunchAnimatorController.kt
@@ -0,0 +1,329 @@
+package com.android.systemui.plugins.animation
+
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.PixelFormat
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffXfermode
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.LayerDrawable
+import android.view.GhostView
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+
+/**
+ * A base implementation of [ActivityLaunchAnimator.Controller] which creates a [ghost][GhostView]
+ * of [ghostedView] as well as an expandable background view, which are drawn and animated instead
+ * of the ghosted view.
+ *
+ * Important: [ghostedView] must be attached to the window when calling this function and during the
+ * animation.
+ *
+ * Note: Avoid instantiating this directly and call [ActivityLaunchAnimator.Controller.fromView]
+ * whenever possible instead.
+ */
+open class GhostedViewLaunchAnimatorController(
+    /** The view that will be ghosted and from which the background will be extracted. */
+    private val ghostedView: View
+) : ActivityLaunchAnimator.Controller {
+    /** The root view to which we will add the ghost view and expanding background. */
+    private val rootView = ghostedView.rootView as ViewGroup
+    private val rootViewOverlay = rootView.overlay
+
+    /** The ghost view that is drawn and animated instead of the ghosted view. */
+    private var ghostView: View? = null
+
+    /**
+     * The expanding background view that will be added to [rootView] (below [ghostView]) and
+     * animate.
+     */
+    private var backgroundView: FrameLayout? = null
+
+    /**
+     * The drawable wrapping the [ghostedView] background and used as background for
+     * [backgroundView].
+     */
+    private var backgroundDrawable: WrappedDrawable? = null
+    private var startBackgroundAlpha: Int = 0xFF
+
+    /**
+     * Return the background of the [ghostedView]. This background will be used to draw the
+     * background of the background view that is expanding up to the final animation position. This
+     * is called at the start of the animation.
+     *
+     * Note that during the animation, the alpha value value of this background will be set to 0,
+     * then set back to its initial value at the end of the animation.
+     */
+    protected open fun getBackground(): Drawable? = ghostedView.background
+
+    /**
+     * Set the corner radius of [background]. The background is the one that was returned by
+     * [getBackground].
+     */
+    protected open fun setBackgroundCornerRadius(
+        background: Drawable,
+        topCornerRadius: Float,
+        bottomCornerRadius: Float
+    ) {
+        // By default, we rely on WrappedDrawable to set/restore the background radii before/after
+        // each draw.
+        backgroundDrawable?.setBackgroundRadius(topCornerRadius, bottomCornerRadius)
+    }
+
+    /** Return the current top corner radius of the background. */
+    protected open fun getCurrentTopCornerRadius(): Float {
+        val drawable = getBackground() ?: return 0f
+        val gradient = findGradientDrawable(drawable) ?: return 0f
+
+        // TODO(b/184121838): Support more than symmetric top & bottom radius.
+        return gradient.cornerRadii?.get(CORNER_RADIUS_TOP_INDEX) ?: gradient.cornerRadius
+    }
+
+    /** Return the current bottom corner radius of the background. */
+    protected open fun getCurrentBottomCornerRadius(): Float {
+        val drawable = getBackground() ?: return 0f
+        val gradient = findGradientDrawable(drawable) ?: return 0f
+
+        // TODO(b/184121838): Support more than symmetric top & bottom radius.
+        return gradient.cornerRadii?.get(CORNER_RADIUS_BOTTOM_INDEX) ?: gradient.cornerRadius
+    }
+
+    override fun getRootView(): View {
+        return rootView
+    }
+
+    override fun createAnimatorState(): ActivityLaunchAnimator.State {
+        val location = ghostedView.locationOnScreen
+        return ActivityLaunchAnimator.State(
+            top = location[1],
+            bottom = location[1] + ghostedView.height,
+            left = location[0],
+            right = location[0] + ghostedView.width,
+            topCornerRadius = getCurrentTopCornerRadius(),
+            bottomCornerRadius = getCurrentBottomCornerRadius()
+        )
+    }
+
+    override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
+        backgroundView = FrameLayout(rootView.context).apply {
+            forceHasOverlappingRendering(false)
+        }
+        rootViewOverlay.add(backgroundView)
+
+        // We wrap the ghosted view background and use it to draw the expandable background. Its
+        // alpha will be set to 0 as soon as we start drawing the expanding background.
+        val drawable = getBackground()
+        startBackgroundAlpha = drawable?.alpha ?: 0xFF
+        backgroundDrawable = WrappedDrawable(drawable)
+        backgroundView?.background = backgroundDrawable
+
+        // Create a ghost of the view that will be moving and fading out. This allows to fade out
+        // the content before fading out the background.
+        ghostView = GhostView.addGhost(ghostedView, rootView).apply {
+            setLayerType(View.LAYER_TYPE_HARDWARE, null)
+        }
+    }
+
+    override fun onLaunchAnimationProgress(
+        state: ActivityLaunchAnimator.State,
+        progress: Float,
+        linearProgress: Float
+    ) {
+        val ghostView = this.ghostView!!
+        ghostView.translationX = (state.leftChange + state.rightChange) / 2.toFloat()
+        ghostView.translationY = state.topChange.toFloat()
+        ghostView.alpha = state.contentAlpha
+
+        val backgroundView = this.backgroundView!!
+        backgroundView.top = state.top
+        backgroundView.bottom = state.bottom
+        backgroundView.left = state.left
+        backgroundView.right = state.right
+
+        val backgroundDrawable = backgroundDrawable!!
+        backgroundDrawable.alpha = (0xFF * state.backgroundAlpha).toInt()
+        backgroundDrawable.wrapped?.let {
+            setBackgroundCornerRadius(it, state.topCornerRadius, state.bottomCornerRadius)
+        }
+    }
+
+    override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+        backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha
+
+        GhostView.removeGhost(ghostedView)
+        rootViewOverlay.remove(backgroundView)
+        ghostedView.invalidate()
+    }
+
+    companion object {
+        private const val CORNER_RADIUS_TOP_INDEX = 0
+        private const val CORNER_RADIUS_BOTTOM_INDEX = 4
+
+        /**
+         * Return the first [GradientDrawable] found in [drawable], or null if none is found. If
+         * [drawable] is a [LayerDrawable], this will return the first layer that is a
+         * [GradientDrawable].
+         */
+        private fun findGradientDrawable(drawable: Drawable): GradientDrawable? {
+            if (drawable is GradientDrawable) {
+                return drawable
+            }
+
+            if (drawable is LayerDrawable) {
+                for (i in 0 until drawable.numberOfLayers) {
+                    val maybeGradient = drawable.getDrawable(i)
+                    if (maybeGradient is GradientDrawable) {
+                        return maybeGradient
+                    }
+                }
+            }
+
+            return null
+        }
+    }
+
+    private class WrappedDrawable(val wrapped: Drawable?) : Drawable() {
+        companion object {
+            private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC)
+        }
+
+        private var currentAlpha = 0xFF
+        private var previousBounds = Rect()
+
+        private var cornerRadii = FloatArray(8) { -1f }
+        private var previousCornerRadii = FloatArray(8)
+
+        override fun draw(canvas: Canvas) {
+            val wrapped = this.wrapped ?: return
+
+            wrapped.copyBounds(previousBounds)
+
+            wrapped.alpha = currentAlpha
+            wrapped.bounds = bounds
+            setXfermode(wrapped, SRC_MODE)
+            applyBackgroundRadii()
+
+            wrapped.draw(canvas)
+
+            // The background view (and therefore this drawable) is drawn before the ghost view, so
+            // the ghosted view background alpha should always be 0 when it is drawn above the
+            // background.
+            wrapped.alpha = 0
+            wrapped.bounds = previousBounds
+            setXfermode(wrapped, null)
+            restoreBackgroundRadii()
+        }
+
+        override fun setAlpha(alpha: Int) {
+            if (alpha != currentAlpha) {
+                currentAlpha = alpha
+                invalidateSelf()
+            }
+        }
+
+        override fun getAlpha() = currentAlpha
+
+        override fun getOpacity(): Int {
+            val wrapped = this.wrapped ?: return PixelFormat.TRANSPARENT
+
+            val previousAlpha = wrapped.alpha
+            wrapped.alpha = currentAlpha
+            val opacity = wrapped.opacity
+            wrapped.alpha = previousAlpha
+            return opacity
+        }
+
+        override fun setColorFilter(filter: ColorFilter?) {
+            wrapped?.colorFilter = filter
+        }
+
+        private fun setXfermode(background: Drawable, mode: PorterDuffXfermode?) {
+            if (background !is LayerDrawable) {
+                background.setXfermode(mode)
+                return
+            }
+
+            // We set the xfermode on the first layer that is not a mask. Most of the time it will
+            // be the "background layer".
+            for (i in 0 until background.numberOfLayers) {
+                if (background.getId(i) != android.R.id.mask) {
+                    background.getDrawable(i).setXfermode(mode)
+                    break
+                }
+            }
+        }
+
+        fun setBackgroundRadius(topCornerRadius: Float, bottomCornerRadius: Float) {
+            updateRadii(cornerRadii, topCornerRadius, bottomCornerRadius)
+            invalidateSelf()
+        }
+
+        private fun updateRadii(
+            radii: FloatArray,
+            topCornerRadius: Float,
+            bottomCornerRadius: Float
+        ) {
+            radii[0] = topCornerRadius
+            radii[1] = topCornerRadius
+            radii[2] = topCornerRadius
+            radii[3] = topCornerRadius
+
+            radii[4] = bottomCornerRadius
+            radii[5] = bottomCornerRadius
+            radii[6] = bottomCornerRadius
+            radii[7] = bottomCornerRadius
+        }
+
+        private fun applyBackgroundRadii() {
+            if (cornerRadii[0] < 0 || wrapped == null) {
+                return
+            }
+
+            savePreviousBackgroundRadii(wrapped)
+            applyBackgroundRadii(wrapped, cornerRadii)
+        }
+
+        private fun savePreviousBackgroundRadii(background: Drawable) {
+            // TODO(b/184121838): This method assumes that all GradientDrawable in background will
+            // have the same radius. Should we save/restore the radii for each layer instead?
+            val gradient = findGradientDrawable(background) ?: return
+
+            // TODO(b/184121838): GradientDrawable#getCornerRadii clones its radii array. Should we
+            // try to avoid that?
+            val radii = gradient.cornerRadii
+            if (radii != null) {
+                radii.copyInto(previousCornerRadii)
+            } else {
+                // Copy the cornerRadius into previousCornerRadii.
+                val radius = gradient.cornerRadius
+                updateRadii(previousCornerRadii, radius, radius)
+            }
+        }
+
+        private fun applyBackgroundRadii(drawable: Drawable, radii: FloatArray) {
+            if (drawable is GradientDrawable) {
+                drawable.cornerRadii = radii
+                return
+            }
+
+            if (drawable !is LayerDrawable) {
+                return
+            }
+
+            for (i in 0 until drawable.numberOfLayers) {
+                (drawable.getDrawable(i) as? GradientDrawable)?.cornerRadii = radii
+            }
+        }
+
+        private fun restoreBackgroundRadii() {
+            if (cornerRadii[0] < 0 || wrapped == null) {
+                return
+            }
+
+            applyBackgroundRadii(wrapped, previousCornerRadii)
+        }
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index c9f2401..46237148 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -15,10 +15,12 @@
 package com.android.systemui.plugins.qs;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.metrics.LogMaker;
 import android.service.quicksettings.Tile;
+import android.view.View;
 
 import com.android.internal.logging.InstanceId;
 import com.android.systemui.plugins.annotations.DependsOn;
@@ -53,10 +55,16 @@
     void removeCallbacks();
 
     QSIconView createTileView(Context context);
-    
+
     void click();
     void secondaryClick();
-    void longClick();
+
+    /**
+     * The tile was long clicked.
+     *
+     * @param view The view that was clicked.
+     */
+    void longClick(@Nullable View view);
 
     void userSwitch(int currentUser);
     int getMetricsCategory();
diff --git a/packages/SystemUI/res/drawable/circle_wallet_primary_50dp.xml b/packages/SystemUI/res/drawable/circle_wallet_primary_50dp.xml
index de4d882..41d88b4 100644
--- a/packages/SystemUI/res/drawable/circle_wallet_primary_50dp.xml
+++ b/packages/SystemUI/res/drawable/circle_wallet_primary_50dp.xml
@@ -18,5 +18,6 @@
     <size
         android:height="50dp"
         android:width="50dp" />
-        <solid android:color="#1A73E8" />
+    <solid android:color="@android:color/transparent" />
+    <stroke android:width="2dp" android:color="#AECBFA" />
 </shape>
diff --git a/packages/SystemUI/res/drawable/ic_move_magnification.xml b/packages/SystemUI/res/drawable/ic_move_magnification.xml
index ed97d0cc..642fc31 100644
--- a/packages/SystemUI/res/drawable/ic_move_magnification.xml
+++ b/packages/SystemUI/res/drawable/ic_move_magnification.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!--
   ~ Copyright (C) 2020 The Android Open Source Project
   ~
@@ -18,7 +19,7 @@
     <item>
         <shape android:shape="rectangle">
             <solid
-                android:color="@android:color/black" />
+                android:color="@color/magnification_switch_button_color" />
             <size
                 android:height="@dimen/magnification_drag_view_size"
                 android:width="@dimen/magnification_drag_view_size"/>
@@ -26,9 +27,14 @@
     </item>
     <item
         android:gravity="center">
-        <vector android:height="28dp" android:viewportHeight="512"
-            android:viewportWidth="512" android:width="28dp">
-            <path android:fillColor="#FFFFFF" android:pathData="M506.528,243.712l-96.224,-80c-4.8,-4 -11.456,-4.832 -17.056,-2.208C387.616,164.16 384,169.792 384,176v48h-96v-96h48c6.208,0 11.84,-3.616 14.496,-9.248c2.624,-5.632 1.792,-12.288 -2.208,-17.056l-80,-96.224c-6.08,-7.296 -18.496,-7.296 -24.608,0l-80,96.224c-3.968,4.8 -4.832,11.456 -2.176,17.056C164.128,124.384 169.792,128 176,128h48v96h-96v-48c0,-6.208 -3.616,-11.84 -9.248,-14.496c-5.6,-2.624 -12.256,-1.792 -17.056,2.208l-96.224,80c-7.296,6.08 -7.296,18.496 0,24.608l96.224,80c4.8,3.968 11.456,4.832 17.056,2.176C124.416,347.872 128,342.208 128,336v-48h96v96h-48c-6.208,0 -11.872,3.616 -14.496,9.248c-2.656,5.632 -1.792,12.288 2.176,17.056l80,96.224c6.08,7.296 18.496,7.296 24.608,0l80,-96.224c4,-4.8 4.832,-11.456 2.208,-17.056C347.84,387.616 342.208,384 336,384h-48v-96h96v48c0,6.208 3.616,11.872 9.248,14.496c5.632,2.656 12.288,1.792 17.056,-2.176l96.224,-80C513.824,262.208 513.824,249.792 506.528,243.712z"/>
+        <vector xmlns:android="http://schemas.android.com/apk/res/android"
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportWidth="24"
+            android:viewportHeight="24">
+            <path
+                android:pathData="M18.19,12.44l-3.24,-1.62c1.29,-1 2.12,-2.56 2.12,-4.32c0,-3.03 -2.47,-5.5 -5.5,-5.5s-5.5,2.47 -5.5,5.5c0,2.13 1.22,3.98 3,4.89v3.26c-2.11,-0.45 -2.01,-0.44 -2.26,-0.44c-0.53,0 -1.03,0.21 -1.41,0.59L4,16.22l5.09,5.09C9.52,21.75 10.12,22 10.74,22h6.3c0.98,0 1.81,-0.7 1.97,-1.67l0.8,-4.71C20.03,14.32 19.38,13.04 18.19,12.44zM17.84,15.29L17.04,20h-6.3c-0.09,0 -0.17,-0.04 -0.24,-0.1l-3.68,-3.68l4.25,0.89V6.5c0,-0.28 0.22,-0.5 0.5,-0.5c0.28,0 0.5,0.22 0.5,0.5v6h1.76l3.46,1.73C17.69,14.43 17.91,14.86 17.84,15.29zM8.07,6.5c0,-1.93 1.57,-3.5 3.5,-3.5s3.5,1.57 3.5,3.5c0,0.95 -0.38,1.81 -1,2.44V6.5c0,-1.38 -1.12,-2.5 -2.5,-2.5c-1.38,0 -2.5,1.12 -2.5,2.5v2.44C8.45,8.31 8.07,7.45 8.07,6.5z"
+                android:fillColor="#FFFFFF"/>
         </vector>
     </item>
 </layer-list>
diff --git a/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml b/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml
index 96e0193..5bb7390 100644
--- a/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml
+++ b/packages/SystemUI/res/drawable/ic_open_in_new_fullscreen.xml
@@ -30,9 +30,16 @@
             android:height="24dp"
             android:viewportWidth="24"
             android:viewportHeight="24">
-            <path
-                android:fillColor="@android:color/white"
-                android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z" />
+            <group>
+                <clip-path
+                    android:pathData="M0,0h24v24h-24z"/>
+                <path
+                    android:pathData="M11,6.05V8.05H14.59L8,14.64V11.05H6V18.05H13V16.05H9.41L16,9.46V13.05H18V6.05H11Z"
+                    android:fillColor="#ffffff"/>
+                <path
+                    android:pathData="M20,4.05V20.05H4V4.05H20ZM22,2.05H2V22.05H22V2.05Z"
+                    android:fillColor="#ffffff"/>
+            </group>
         </vector>
     </item>
 
diff --git a/packages/SystemUI/res/drawable/ic_open_in_new_window.xml b/packages/SystemUI/res/drawable/ic_open_in_new_window.xml
index 368c8df..2d8a5d9 100644
--- a/packages/SystemUI/res/drawable/ic_open_in_new_window.xml
+++ b/packages/SystemUI/res/drawable/ic_open_in_new_window.xml
@@ -26,30 +26,19 @@
 
     <item>
         <vector
-                android:width="24dp"
-                android:height="24dp"
-                android:viewportWidth="24"
-                android:viewportHeight="24">
-
-            <group
-                android:translateX="1.500000"
-                android:translateY="1.500000">
-                <path
-                    android:fillType="evenOdd"
-                    android:strokeColor="#FFFFFF"
-                    android:strokeWidth="2"
-                    android:pathData="M 3.5 1.5 L 17.5 1.5 Q 19.5 1.5 19.5 3.5 L 19.5 17.5 Q 19.5 19.5 17.5 19.5 L 3.5 19.5 Q 1.5 19.5 1.5 17.5 L 1.5 3.5 Q 1.5 1.5 3.5 1.5 Z" />
-                <path
-                    android:fillType="evenOdd"
-                    android:strokeColor="#FFFFFF"
-                    android:strokeWidth="1.5"
-                    android:pathData="M 4.25 4.25 H 8.75 V 8.75 H 4.25 V 4.25 Z" />
-                <path
-                    android:fillColor="#FFFFFF"
-                    android:fillType="evenOdd"
-                    android:strokeWidth="1"
-                    android:pathData="M 12.5 13.92 L 15.59 17 L 17 15.59 L 13.91 12.5 L 16.5 12.5 L 16.5 10.5 L 10.5 10.5 L 10.5 16.5 L 12.5 16.5 Z" />
-            </group>
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportWidth="24"
+            android:viewportHeight="24">
+            <path
+                android:pathData="M2,12.05V22.05H22V2.05H12V4.05H20V20.05H4V12.05H2Z"
+                android:fillColor="#ffffff"/>
+            <path
+                android:pathData="M10,2.05H2V10.05H10V2.05Z"
+                android:fillColor="#ffffff"/>
+            <path
+                android:pathData="M18,11.05V13.05H14.41L18.95,17.59L17.54,19L13,14.46V18.05H11V11.05H18Z"
+                android:fillColor="#ffffff"/>
         </vector>
     </item>
 
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index bbf69a9..1f90476 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -24,7 +24,7 @@
     android:minHeight="48dp"
     android:clickable="false"
     android:focusable="true"
-    android:theme="@style/QSHeaderTheme">
+    android:theme="@style/Theme.SystemUI.QuickSettings.Header">
 
     <com.android.systemui.statusbar.policy.Clock
         android:id="@+id/clock"
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 1630244..a99edb9 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -51,6 +51,14 @@
         sysui:ignoreRightInset="true"
         />
 
+    <com.android.systemui.statusbar.ScrimView
+        android:id="@+id/scrim_notifications"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="no"
+        sysui:ignoreRightInset="true"
+        />
+
     <com.android.systemui.statusbar.LightRevealScrim
             android:id="@+id/light_reveal_scrim"
             android:layout_width="match_parent"
@@ -100,4 +108,12 @@
             android:ellipsize="marquee"
             android:focusable="true" />
     </LinearLayout>
+
+    <com.android.systemui.biometrics.AuthRippleView
+        android:id="@+id/auth_ripple"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="no"
+        sysui:ignoreRightInset="true"
+    />
 </com.android.systemui.statusbar.phone.NotificationShadeWindowView>
diff --git a/packages/SystemUI/res/layout/window_magnifier_view.xml b/packages/SystemUI/res/layout/window_magnifier_view.xml
index efd24c7..96de5a9 100644
--- a/packages/SystemUI/res/layout/window_magnifier_view.xml
+++ b/packages/SystemUI/res/layout/window_magnifier_view.xml
@@ -77,6 +77,8 @@
         android:layout_height="@dimen/magnification_drag_view_size"
         android:layout_margin="@dimen/magnification_outer_border_margin"
         android:layout_gravity="right|bottom"
+        android:paddingEnd="@dimen/magnifier_drag_handle_padding"
+        android:paddingBottom="@dimen/magnifier_drag_handle_padding"
         android:scaleType="center"
         android:importantForAccessibility="no"
         android:src="@drawable/ic_move_magnification"/>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index bd92299..0125144 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -642,4 +642,11 @@
 
     <!-- Whether to use window background blur for the volume dialog. -->
     <bool name="config_volumeDialogUseBackgroundBlur">false</bool>
+
+    <!-- The properties of the face auth camera in pixels -->
+    <integer-array name="config_face_auth_props">
+        <!-- sensorLocationX -->
+        <!-- sensorLocationY -->
+        <!--sensorRadius -->
+    </integer-array>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5a21b30..237edf8 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -718,6 +718,7 @@
     <item name="keyguard_clock_line_spacing_scale_burmese" type="dimen" format="float">1</item>
 
     <item name="scrim_behind_alpha" format="float" type="dimen">0.62</item>
+    <dimen name="notification_scrim_corner_radius">32dp</dimen>
 
     <!-- The minimum amount the user needs to swipe to go to the camera / phone. -->
     <dimen name="keyguard_min_swipe_amount">110dp</dimen>
@@ -1253,11 +1254,13 @@
     <dimen name="magnification_drag_view_size">38dp</dimen>
     <dimen name="magnification_controls_size">90dp</dimen>
     <dimen name="magnification_switch_button_size">60dp</dimen>
-    <dimen name="magnification_switch_button_padding">12dp</dimen>
+    <dimen name="magnification_switch_button_padding">6dp</dimen>
     <dimen name="magnifier_left_right_controls_width">35dp</dimen>
     <dimen name="magnifier_left_right_controls_height">45dp</dimen>
     <dimen name="magnifier_up_down_controls_width">45dp</dimen>
     <dimen name="magnifier_up_down_controls_height">40dp</dimen>
+    <!-- The extra padding to show the whole outer border -->
+    <dimen name="magnifier_drag_handle_padding">3dp</dimen>
 
     <!-- Home Controls -->
     <dimen name="controls_header_side_margin">4dp</dimen>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 0763109..c39db94 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -50,4 +50,6 @@
     <bool name="flag_charging_ripple">false</bool>
 
     <bool name="flag_ongoing_call_status_bar_chip">false</bool>
+
+    <bool name="flag_smartspace">false</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 9665c89..bb9d331 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -173,5 +173,7 @@
     <item type="id" name="action_move_top_right"/>
     <item type="id" name="action_move_bottom_left"/>
     <item type="id" name="action_move_bottom_right"/>
+    <item type="id" name="action_move_to_edge_and_hide"/>
+    <item type="id" name="action_move_out_edge_and_show"/>
 </resources>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index fba6269..4a29578 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2705,6 +2705,10 @@
     <string name="accessibility_floating_button_action_move_bottom_left">Move bottom left</string>
     <!-- Action in accessibility menu to move the accessibility floating button to the bottom right of the screen. [CHAR LIMIT=30]-->
     <string name="accessibility_floating_button_action_move_bottom_right">Move bottom right</string>
+    <!-- Action in accessibility menu to move the accessibility floating button to the edge and hide it to half. [CHAR LIMIT=30]-->
+    <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half">Move to edge and hide</string>
+    <!-- Action in accessibility menu to move the accessibility floating button out the edge and show. [CHAR LIMIT=30]-->
+    <string name="accessibility_floating_button_action_move_out_edge_and_show">Move out edge and show</string>
 
     <!-- Device Controls strings -->
     <!-- Device Controls empty state, title [CHAR LIMIT=30] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e6fc332..de14dbd 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -375,9 +375,10 @@
         <item name="*android:dotColor">?android:attr/textColorSecondary</item>
     </style>
 
-    <style name="Theme.SystemUI.QuickSettings" parent="@*android:style/Theme.DeviceDefault.SystemUI">
+    <style name="Theme.SystemUI.QuickSettings" parent="@*android:style/Theme.DeviceDefault">
         <item name="lightIconTheme">@style/QSIconTheme</item>
         <item name="darkIconTheme">@style/QSIconTheme</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:colorError">@*android:color/error_color_material_dark</item>
         <item name="android:windowIsFloating">true</item>
         <item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
@@ -452,7 +453,7 @@
         <item name="singleToneColor">@color/dark_mode_qs_icon_color_single_tone</item>
     </style>
 
-    <style name="QSHeaderTheme" parent="@style/Theme.SystemUI">
+    <style name="Theme.SystemUI.QuickSettings.Header">
         <item name="lightIconTheme">@style/DualToneLightTheme</item>
         <item name="darkIconTheme">@style/QSHeaderDarkTheme</item>
     </style>
diff --git a/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java
index 15312ad..c9dea46 100644
--- a/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java
+++ b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java
@@ -18,11 +18,12 @@
 
 import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
 
+import static com.android.systemui.classifier.Classifier.DISABLED_UDFPS_AFFORDANCE;
+
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.InsetDrawable;
 import android.hardware.biometrics.BiometricSourceType;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -33,7 +34,9 @@
 import com.android.systemui.R;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.ViewController;
 
@@ -53,14 +56,16 @@
     @NonNull private final KeyguardViewController mKeyguardViewController;
     @NonNull private final StatusBarStateController mStatusBarStateController;
     @NonNull private final KeyguardStateController mKeyguardStateController;
+    @NonNull private final FalsingManager mFalsingManager;
     @NonNull private final Drawable mButton;
     @NonNull private final Drawable mUnlockIcon;
 
     private boolean mIsDozing;
     private boolean mIsBouncerShowing;
-    private boolean mIsKeyguardShowing;
     private boolean mRunningFPS;
     private boolean mCanDismissLockScreen;
+    private boolean mQsExpanded;
+    private int mStatusBarState;
 
     private boolean mShowButton;
     private boolean mShowUnlockIcon;
@@ -71,16 +76,19 @@
             @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
             @NonNull AuthController authController,
             @NonNull KeyguardViewController keyguardViewController,
-            @NonNull KeyguardStateController keyguardStateController
+            @NonNull KeyguardStateController keyguardStateController,
+            @NonNull FalsingManager falsingManager
     ) {
         super(view);
-        mView.setOnTouchListener(mOnTouchListener);
+        mView.setOnClickListener(v -> onAffordanceClick());
+        mView.setOnLongClickListener(v -> onAffordanceClick());
         mView.setSensorProperties(authController.getUdfpsProps().get(0));
 
         mStatusBarStateController = statusBarStateController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mKeyguardViewController = keyguardViewController;
         mKeyguardStateController = keyguardStateController;
+        mFalsingManager = falsingManager;
 
         final Context context = view.getContext();
         mButton = context.getResources().getDrawable(
@@ -94,10 +102,10 @@
     @Override
     protected void onViewAttached() {
         mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
-        mIsKeyguardShowing = mKeyguardStateController.isShowing();
         mIsDozing = mStatusBarStateController.isDozing();
         mRunningFPS = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
         mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
+        mStatusBarState = mStatusBarStateController.getState();
         mUnlockIcon.setTint(Utils.getColorAttrDefaultColor(mView.getContext(),
                 R.attr.wallpaperTextColorAccent));
         updateVisibility();
@@ -114,6 +122,15 @@
         mKeyguardStateController.removeCallback(mKeyguardStateCallback);
     }
 
+    private boolean onAffordanceClick() {
+        if (mFalsingManager.isFalseTouch(DISABLED_UDFPS_AFFORDANCE)) {
+            return false;
+        }
+        mView.setVisibility(View.INVISIBLE);
+        mKeyguardViewController.showBouncer(/* scrim */ true);
+        return true;
+    }
+
     /**
      * Call when this controller is no longer needed. This will remove the view from its parent.
      */
@@ -123,6 +140,14 @@
         }
     }
 
+    /**
+     * Set whether qs is expanded. When QS is expanded, don't show a DisabledUdfps affordance.
+     */
+    public void setQsExpanded(boolean expanded) {
+        mQsExpanded = expanded;
+        updateVisibility();
+    }
+
     private void updateVisibility() {
         mShowButton = !mCanDismissLockScreen && !mRunningFPS && isLockScreen();
         mShowUnlockIcon = mCanDismissLockScreen && isLockScreen();
@@ -139,7 +164,10 @@
     }
 
     private boolean isLockScreen() {
-        return mIsKeyguardShowing && !mIsDozing && !mIsBouncerShowing;
+        return !mIsDozing
+                && !mIsBouncerShowing
+                && !mQsExpanded
+                && mStatusBarState == StatusBarState.KEYGUARD;
     }
 
     @Override
@@ -148,20 +176,13 @@
         pw.println("  mShowBouncerButton: " + mShowButton);
         pw.println("  mShowUnlockIcon: " + mShowUnlockIcon);
         pw.println("  mIsDozing: " + mIsDozing);
-        pw.println("  mIsKeyguardShowing: " + mIsKeyguardShowing);
         pw.println("  mIsBouncerShowing: " + mIsBouncerShowing);
         pw.println("  mRunningFPS: " + mRunningFPS);
         pw.println("  mCanDismissLockScreen: " + mCanDismissLockScreen);
+        pw.println("  mStatusBarState: " + StatusBarState.toShortString(mStatusBarState));
+        pw.println("  mQsExpanded: " + mQsExpanded);
     }
 
-    private final View.OnTouchListener mOnTouchListener = new View.OnTouchListener() {
-        @Override
-        public boolean onTouch(View v, MotionEvent event) {
-            mKeyguardViewController.showBouncer(/* scrim */ true);
-            return true;
-        }
-    };
-
     private StatusBarStateController.StateListener mStatusBarStateListener =
             new StatusBarStateController.StateListener() {
                 @Override
@@ -169,6 +190,12 @@
                     mIsDozing = isDozing;
                     updateVisibility();
                 }
+
+                @Override
+                public void onStateChanged(int statusBarState) {
+                    mStatusBarState = statusBarState;
+                    updateVisibility();
+                }
             };
 
     private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
@@ -193,21 +220,9 @@
     private final KeyguardStateController.Callback mKeyguardStateCallback =
             new KeyguardStateController.Callback() {
         @Override
-        public void onKeyguardShowingChanged() {
-            updateIsKeyguardShowing();
-            updateVisibility();
-        }
-
-        @Override
         public void onUnlockedChanged() {
-            updateIsKeyguardShowing();
             mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
             updateVisibility();
         }
-
-        private void updateIsKeyguardShowing() {
-            mIsKeyguardShowing = mKeyguardStateController.isShowing()
-                    && !mKeyguardStateController.isKeyguardGoingAway();
-        }
     };
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 0675200..24b7cd1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -16,8 +16,15 @@
 
 package com.android.keyguard;
 
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
 import android.app.WallpaperManager;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.SmartspaceManager;
+import android.app.smartspace.SmartspaceSession;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.content.res.Resources;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -25,6 +32,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
 
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.clock.ClockManager;
@@ -32,8 +40,12 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.BcSmartspaceDataPlugin;
 import com.android.systemui.plugins.ClockPlugin;
+import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -43,6 +55,7 @@
 
 import java.util.Locale;
 import java.util.TimeZone;
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -68,6 +81,13 @@
     private AnimatableClockController mNewLockScreenLargeClockViewController;
     private FrameLayout mNewLockScreenLargeClockFrame;
 
+    private PluginManager mPluginManager;
+    private boolean mIsSmartspaceEnabled;
+    PluginListener mPluginListener;
+    private Executor mUiExecutor;
+    private SmartspaceSession mSmartspaceSession;
+    private SmartspaceSession.Callback mSmartspaceCallback;
+
     private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
 
     private final StatusBarStateController.StateListener mStateListener =
@@ -96,6 +116,9 @@
     private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
     private String mTimeFormat;
 
+    // If set, will replace keyguard_status_area
+    private BcSmartspaceDataPlugin.SmartspaceView mSmartspaceView;
+
     @Inject
     public KeyguardClockSwitchController(
             KeyguardClockSwitch keyguardClockSwitch,
@@ -105,7 +128,10 @@
             KeyguardSliceViewController keyguardSliceViewController,
             NotificationIconAreaController notificationIconAreaController,
             ContentResolver contentResolver,
-            BroadcastDispatcher broadcastDispatcher) {
+            BroadcastDispatcher broadcastDispatcher,
+            PluginManager pluginManager,
+            FeatureFlags featureFlags,
+            @Main Executor uiExecutor) {
         super(keyguardClockSwitch);
         mResources = resources;
         mStatusBarStateController = statusBarStateController;
@@ -115,6 +141,9 @@
         mNotificationIconAreaController = notificationIconAreaController;
         mBroadcastDispatcher = broadcastDispatcher;
         mTimeFormat = Settings.System.getString(contentResolver, Settings.System.TIME_12_24);
+        mPluginManager = pluginManager;
+        mIsSmartspaceEnabled = featureFlags.isSmartspaceEnabled();
+        mUiExecutor = uiExecutor;
     }
 
     /**
@@ -137,6 +166,63 @@
         updateAodIcons();
         mNewLockScreenClockFrame = mView.findViewById(R.id.new_lockscreen_clock_view);
         mNewLockScreenLargeClockFrame = mView.findViewById(R.id.new_lockscreen_clock_view_large);
+
+        // If a smartspace plugin is detected, replace the existing smartspace
+        // (keyguard_status_area), and initialize a new session
+        mPluginListener = new PluginListener<BcSmartspaceDataPlugin>() {
+
+            @Override
+            public void onPluginConnected(BcSmartspaceDataPlugin plugin, Context pluginContext) {
+                if (!mIsSmartspaceEnabled) return;
+
+                View ksa = mView.findViewById(R.id.keyguard_status_area);
+                int ksaIndex = mView.indexOfChild(ksa);
+                ksa.setVisibility(View.GONE);
+
+                mSmartspaceView = plugin.getView(mView);
+                mSmartspaceView.registerDataProvider(plugin);
+
+                RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
+                        MATCH_PARENT, WRAP_CONTENT);
+                lp.addRule(RelativeLayout.BELOW, R.id.new_lockscreen_clock_view);
+                mView.addView((View) mSmartspaceView, ksaIndex, lp);
+
+                View nic = mView.findViewById(
+                        com.android.systemui.R.id.left_aligned_notification_icon_container);
+                lp = (RelativeLayout.LayoutParams) nic.getLayoutParams();
+                lp.addRule(RelativeLayout.BELOW, ((View) mSmartspaceView).getId());
+                nic.setLayoutParams(lp);
+
+                createSmartspaceSession(plugin);
+            }
+
+            @Override
+            public void onPluginDisconnected(BcSmartspaceDataPlugin plugin) {
+                if (!mIsSmartspaceEnabled) return;
+
+                mView.removeView((View) mSmartspaceView);
+                mView.findViewById(R.id.keyguard_status_area).setVisibility(View.VISIBLE);
+
+                View nic = mView.findViewById(
+                        com.android.systemui.R.id.left_aligned_notification_icon_container);
+                RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)
+                        nic.getLayoutParams();
+                lp.addRule(RelativeLayout.BELOW, R.id.keyguard_status_area);
+                nic.setLayoutParams(lp);
+
+                mSmartspaceView = null;
+            }
+
+            private void createSmartspaceSession(BcSmartspaceDataPlugin plugin) {
+                mSmartspaceSession = getContext().getSystemService(SmartspaceManager.class)
+                        .createSmartspaceSession(
+                                new SmartspaceConfig.Builder(getContext(), "lockscreen").build());
+                mSmartspaceCallback = targets -> plugin.onTargetsAvailable(targets);
+                mSmartspaceSession.registerSmartspaceUpdates(mUiExecutor, mSmartspaceCallback);
+                mSmartspaceSession.requestSmartspaceUpdate();
+            }
+        };
+        mPluginManager.addPluginListener(mPluginListener, BcSmartspaceDataPlugin.class, false);
     }
 
     @Override
@@ -147,6 +233,13 @@
         mStatusBarStateController.removeCallback(mStateListener);
         mColorExtractor.removeOnColorsChangedListener(mColorsListener);
         mView.setClockPlugin(null, mStatusBarStateController.getState());
+
+        if (mSmartspaceSession != null) {
+            mSmartspaceSession.unregisterSmartspaceUpdates(mSmartspaceCallback);
+            mSmartspaceSession.destroy();
+            mSmartspaceSession = null;
+        }
+        mPluginManager.removePluginListener(mPluginListener);
     }
 
     /**
@@ -222,6 +315,11 @@
             PropertyAnimator.setProperty(mNewLockScreenLargeClockFrame, AnimatableProperty.SCALE_Y,
                     scale, props, animate);
         }
+
+        if (mSmartspaceView != null) {
+            PropertyAnimator.setProperty((View) mSmartspaceView, AnimatableProperty.TRANSLATION_X,
+                    x, props, animate);
+        }
         mKeyguardSliceViewController.updatePosition(x, props, animate);
         mNotificationIconAreaController.updatePosition(x, props, animate);
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index de09eaa6..2219cf4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -315,7 +315,6 @@
     private boolean mLogoutEnabled;
     // cached value to avoid IPCs
     private boolean mIsUdfpsEnrolled;
-    private boolean mKeyguardQsUserSwitchEnabled;
     // If the user long pressed the lock icon, disabling face auth for the current session.
     private boolean mLockIconPressed;
     private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -1953,7 +1952,7 @@
             return isFaceAuthEnabledForUser(KeyguardUpdateMonitor.getCurrentUser())
                     && !isUdfpsEnrolled();
         }
-        return !isKeyguardQsUserSwitchEnabled();
+        return true;
     }
 
     /**
@@ -1963,17 +1962,6 @@
         return mIsUdfpsEnrolled;
     }
 
-    /**
-     * @return true if the keyguard qs user switcher shortcut is enabled
-     */
-    public boolean isKeyguardQsUserSwitchEnabled() {
-        return mKeyguardQsUserSwitchEnabled;
-    }
-
-    public void setKeyguardQsUserSwitchEnabled(boolean enabled) {
-        mKeyguardQsUserSwitchEnabled = enabled;
-    }
-
     private final UserSwitchObserver mUserSwitchObserver = new UserSwitchObserver() {
         @Override
         public void onUserSwitching(int newUserId, IRemoteCallback reply) {
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index 3d6d381..5507ffa 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -18,8 +18,11 @@
 import android.content.Intent;
 import android.view.View;
 
+import androidx.annotation.Nullable;
+
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.phone.StatusBar;
 
 import java.util.Optional;
@@ -51,18 +54,27 @@
 
     @Override
     public void startPendingIntentDismissingKeyguard(PendingIntent intent,
-            Runnable intentSentCallback) {
+            Runnable intentSentUiThreadCallback) {
         mActualStarter.ifPresent(
                 starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
-                        intentSentCallback));
+                        intentSentUiThreadCallback));
     }
 
     @Override
     public void startPendingIntentDismissingKeyguard(PendingIntent intent,
-            Runnable intentSentCallback, View associatedView) {
+            Runnable intentSentUiThreadCallback, View associatedView) {
         mActualStarter.ifPresent(
                 starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
-                        intentSentCallback, associatedView));
+                        intentSentUiThreadCallback, associatedView));
+    }
+
+    @Override
+    public void startPendingIntentDismissingKeyguard(PendingIntent intent,
+            Runnable intentSentUiThreadCallback,
+            ActivityLaunchAnimator.Controller animationController) {
+        mActualStarter.ifPresent(
+                starter -> starter.get().startPendingIntentDismissingKeyguard(intent,
+                        intentSentUiThreadCallback, animationController));
     }
 
     @Override
@@ -97,12 +109,27 @@
     }
 
     @Override
+    public void postStartActivityDismissingKeyguard(Intent intent, int delay,
+            @Nullable ActivityLaunchAnimator.Controller animationController) {
+        mActualStarter.ifPresent(
+                starter -> starter.get().postStartActivityDismissingKeyguard(intent, delay,
+                        animationController));
+    }
+
+    @Override
     public void postStartActivityDismissingKeyguard(PendingIntent intent) {
         mActualStarter.ifPresent(
                 starter -> starter.get().postStartActivityDismissingKeyguard(intent));
     }
 
     @Override
+    public void postStartActivityDismissingKeyguard(PendingIntent intent,
+            ActivityLaunchAnimator.Controller animationController) {
+        mActualStarter.ifPresent(starter ->
+                starter.get().postStartActivityDismissingKeyguard(intent, animationController));
+    }
+
+    @Override
     public void postQSRunnableDismissingKeyguard(Runnable runnable) {
         mActualStarter.ifPresent(
                 starter -> starter.get().postQSRunnableDismissingKeyguard(runnable));
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 9d00262..cc167b9 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -46,6 +46,7 @@
     private GlobalRootComponent mRootComponent;
     private WMComponent mWMComponent;
     private SysUIComponent mSysUIComponent;
+    private boolean mInitializeComponents;
 
     public static <T extends SystemUIFactory> T getInstance() {
         return (T) mFactory;
@@ -88,13 +89,13 @@
     public void init(Context context, boolean fromTest)
             throws ExecutionException, InterruptedException {
         // Only initialize components for the main system ui process running as the primary user
-        final boolean initializeComponents = !fromTest
+        mInitializeComponents = !fromTest
                 && android.os.Process.myUserHandle().isSystem()
                 && ActivityThread.currentProcessName().equals(ActivityThread.currentPackageName());
         mRootComponent = buildGlobalRootComponent(context);
         // Stand up WMComponent
         mWMComponent = mRootComponent.getWMComponentBuilder().build();
-        if (initializeComponents) {
+        if (mInitializeComponents) {
             // Only initialize when not starting from tests since this currently initializes some
             // components that shouldn't be run in the test environment
             mWMComponent.init();
@@ -102,7 +103,7 @@
 
         // And finally, retrieve whatever SysUI needs from WMShell and build SysUI.
         SysUIComponent.Builder builder = mRootComponent.getSysUIComponent();
-        if (initializeComponents) {
+        if (mInitializeComponents) {
             // Only initialize when not starting from tests since this currently initializes some
             // components that shouldn't be run in the test environment
             builder = prepareSysUIComponentBuilder(builder, mWMComponent)
@@ -134,7 +135,7 @@
                     .setStartingSurface(Optional.ofNullable(null));
         }
         mSysUIComponent = builder.build();
-        if (initializeComponents) {
+        if (mInitializeComponents) {
             mSysUIComponent.init();
         }
 
@@ -160,6 +161,9 @@
                 .build();
     }
 
+    protected boolean shouldInitializeComponents() {
+        return mInitializeComponents;
+    }
 
     public GlobalRootComponent getRootComponent() {
         return mRootComponent;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index cdd6942..4f5fdc9 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -195,6 +195,13 @@
     }
 
     @Override
+    public void onAccessibilityActionPerformed(int displayId) {
+        if (mWindowMagnificationConnectionImpl != null) {
+            mWindowMagnificationConnectionImpl.onAccessibilityActionPerformed(displayId);
+        }
+    }
+
+    @Override
     public void requestWindowMagnificationConnection(boolean connect) {
         if (connect) {
             setWindowMagnificationConnection();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
index be7d757..2d620ab 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationConnectionImpl.java
@@ -120,4 +120,14 @@
             }
         }
     }
+
+    void onAccessibilityActionPerformed(int displayId) {
+        if (mConnectionCallback != null) {
+            try {
+                mConnectionCallback.onAccessibilityActionPerformed(displayId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to inform an accessibility action is already performed", e);
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 2b666f1..48beea3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -756,31 +756,23 @@
                 final float scale = mScale + A11Y_CHANGE_SCALE_DIFFERENCE;
                 mWindowMagnifierCallback.onPerformScaleAction(mDisplayId,
                         A11Y_ACTION_SCALE_RANGE.clamp(scale));
-                return true;
-            }
-            if (action == R.id.accessibility_action_zoom_out) {
+            } else if (action == R.id.accessibility_action_zoom_out) {
                 final float scale = mScale - A11Y_CHANGE_SCALE_DIFFERENCE;
                 mWindowMagnifierCallback.onPerformScaleAction(mDisplayId,
                         A11Y_ACTION_SCALE_RANGE.clamp(scale));
-                return true;
-            }
-            if (action == R.id.accessibility_action_move_up) {
+            } else if (action == R.id.accessibility_action_move_up) {
                 move(0, -mSourceBounds.height());
-                return true;
-            }
-            if (action == R.id.accessibility_action_move_down) {
+            } else if (action == R.id.accessibility_action_move_down) {
                 move(0, mSourceBounds.height());
-                return true;
-            }
-            if (action == R.id.accessibility_action_move_left) {
+            } else if (action == R.id.accessibility_action_move_left) {
                 move(-mSourceBounds.width(), 0);
-                return true;
-            }
-            if (action == R.id.accessibility_action_move_right) {
+            } else if (action == R.id.accessibility_action_move_right) {
                 move(mSourceBounds.width(), 0);
-                return true;
+            } else {
+                return false;
             }
-            return false;
+            mWindowMagnifierCallback.onAccessibilityActionPerformed(mDisplayId);
+            return true;
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
index fb1d1b6..628a5e8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnifierCallback.java
@@ -46,4 +46,11 @@
      * @param scale the target scale, or {@link Float#NaN} to leave unchanged
      */
     void onPerformScaleAction(int displayId, float scale);
+
+    /**
+     * Called when the accessibility action is performed.
+     *
+     * @param displayId The logical display id.
+     */
+    void onAccessibilityActionPerformed(int displayId);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
index 57be4e8..d89dff5 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
@@ -303,25 +303,39 @@
 
         final Rect bounds = getAvailableBounds();
         if (action == R.id.action_move_top_left) {
+            setShapeType(ShapeType.OVAL);
             snapToLocation(bounds.left, bounds.top);
             return true;
         }
 
         if (action == R.id.action_move_top_right) {
+            setShapeType(ShapeType.OVAL);
             snapToLocation(bounds.right, bounds.top);
             return true;
         }
 
         if (action == R.id.action_move_bottom_left) {
+            setShapeType(ShapeType.OVAL);
             snapToLocation(bounds.left, bounds.bottom);
             return true;
         }
 
         if (action == R.id.action_move_bottom_right) {
+            setShapeType(ShapeType.OVAL);
             snapToLocation(bounds.right, bounds.bottom);
             return true;
         }
 
+        if (action == R.id.action_move_to_edge_and_hide) {
+            setShapeType(ShapeType.HALF_OVAL);
+            return true;
+        }
+
+        if (action == R.id.action_move_out_edge_and_show) {
+            setShapeType(ShapeType.OVAL);
+            return true;
+        }
+
         return false;
     }
 
@@ -446,6 +460,16 @@
                         res.getString(
                                 R.string.accessibility_floating_button_action_move_bottom_right));
         info.addAction(moveBottomRight);
+
+        final int moveEdgeId = mShapeType == ShapeType.OVAL
+                ? R.id.action_move_to_edge_and_hide
+                : R.id.action_move_out_edge_and_show;
+        final int moveEdgeTextResId = mShapeType == ShapeType.OVAL
+                ? R.string.accessibility_floating_button_action_move_to_edge_and_hide_to_half
+                : R.string.accessibility_floating_button_action_move_out_edge_and_show;
+        final AccessibilityAction moveToOrOutEdge =
+                new AccessibilityAction(moveEdgeId, res.getString(moveEdgeTextResId));
+        info.addAction(moveToOrOutEdge);
     }
 
     private boolean onTouched(MotionEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 4e93f58..1c0aa06 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -589,7 +589,7 @@
 
     @Override
     public void dismissFromSystemServer() {
-        removeWindowIfAttached();
+        animateAway(false /* sendReason */, 0 /* reason */);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 94b4c5f..2802742 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -29,6 +29,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Configuration;
+import android.graphics.PointF;
 import android.graphics.RectF;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricPrompt;
@@ -82,6 +83,7 @@
     @Nullable private final List<FingerprintSensorPropertiesInternal> mFpProps;
     @Nullable private final List<FaceSensorPropertiesInternal> mFaceProps;
     @Nullable private final List<FingerprintSensorPropertiesInternal> mUdfpsProps;
+    @Nullable private final PointF mFaceAuthSensorLocation;
 
     // TODO: These should just be saved from onSaveState
     private SomeArgs mCurrentDialogArgs;
@@ -261,10 +263,34 @@
     }
 
     /**
-     * @return where the UDFPS exists on the screen in pixels.
+     * @return where the UDFPS exists on the screen in pixels in portrait mode.
      */
     public RectF getUdfpsRegion() {
-        return mUdfpsController == null ? null : mUdfpsController.getSensorLocation();
+        return mUdfpsController == null
+                ? null
+                : mUdfpsController.getSensorLocation();
+    }
+
+    /**
+     * @return where the UDFPS exists on the screen in pixels in portrait mode.
+     */
+    public PointF getUdfpsSensorLocation() {
+        if (mUdfpsController == null) {
+            return null;
+        }
+        return new PointF(mUdfpsController.getSensorLocation().centerX(),
+                mUdfpsController.getSensorLocation().centerY());
+    }
+
+    /**
+     * @return where the face authentication sensor exists relative to the screen in pixels in
+     * portrait mode.
+     */
+    public PointF getFaceAuthSensorLocation() {
+        if (mFaceProps == null || mFaceAuthSensorLocation == null) {
+            return null;
+        }
+        return new PointF(mFaceAuthSensorLocation.x, mFaceAuthSensorLocation.y);
     }
 
     /**
@@ -339,6 +365,15 @@
             }
         }
         mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
+        int[] faceAuthLocation = context.getResources().getIntArray(
+                com.android.systemui.R.array.config_face_auth_props);
+        if (faceAuthLocation == null || faceAuthLocation.length < 2) {
+            mFaceAuthSensorLocation = null;
+        } else {
+            mFaceAuthSensorLocation = new PointF(
+                    (float) faceAuthLocation[0],
+                    (float) faceAuthLocation[1]);
+        }
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index a1149fd..110351e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -17,17 +17,19 @@
 package com.android.systemui.biometrics
 
 import android.content.Context
+import android.content.res.Configuration
+import android.graphics.PointF
 import android.hardware.biometrics.BiometricSourceType
-import android.view.View
-import android.view.ViewGroup
-import com.android.internal.annotations.VisibleForTesting
+import androidx.annotation.VisibleForTesting
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.settingslib.Utils
-import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.commandline.Command
 import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
 import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.ViewController
 import java.io.PrintWriter
 import javax.inject.Inject
 
@@ -35,30 +37,82 @@
  * Controls the ripple effect that shows when authentication is successful.
  * The ripple uses the accent color of the current theme.
  */
-@SysUISingleton
+@StatusBarScope
 class AuthRippleController @Inject constructor(
-    commandRegistry: CommandRegistry,
-    configurationController: ConfigurationController,
-    private val context: Context,
-    private val keyguardUpdateMonitor: KeyguardUpdateMonitor
-) {
-    @VisibleForTesting
-    var rippleView: AuthRippleView = AuthRippleView(context, attrs = null)
+    private val sysuiContext: Context,
+    private val authController: AuthController,
+    private val configurationController: ConfigurationController,
+    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+    private val commandRegistry: CommandRegistry,
+    private val notificationShadeWindowController: NotificationShadeWindowController,
+    rippleView: AuthRippleView?
+) : ViewController<AuthRippleView>(rippleView) {
+    private var fingerprintSensorLocation: PointF? = null
+    private var faceSensorLocation: PointF? = null
 
-    val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
-        override fun onBiometricAuthenticated(
-            userId: Int,
-            biometricSourceType: BiometricSourceType?,
-            isStrongBiometric: Boolean
-        ) {
-            if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
-                rippleView.startRipple()
-            }
+    @VisibleForTesting
+    public override fun onViewAttached() {
+        updateRippleColor()
+        updateSensorLocation()
+        configurationController.addCallback(configurationChangedListener)
+        keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
+        commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() }
+    }
+
+    @VisibleForTesting
+    public override fun onViewDetached() {
+        keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
+        configurationController.removeCallback(configurationChangedListener)
+        commandRegistry.unregisterCommand("auth-ripple")
+
+        notificationShadeWindowController.setForcePluginOpen(false, this)
+    }
+
+    private fun showRipple(biometricSourceType: BiometricSourceType?) {
+        if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
+            fingerprintSensorLocation != null) {
+            mView.setSensorLocation(fingerprintSensorLocation!!)
+            showRipple()
+        } else if (biometricSourceType == BiometricSourceType.FACE &&
+            faceSensorLocation != null) {
+            mView.setSensorLocation(faceSensorLocation!!)
+            showRipple()
         }
     }
 
-    init {
-        val configurationChangedListener = object : ConfigurationController.ConfigurationListener {
+    private fun showRipple() {
+        notificationShadeWindowController.setForcePluginOpen(true, this)
+        mView.startRipple(Runnable {
+            notificationShadeWindowController.setForcePluginOpen(false, this)
+        })
+    }
+
+    private fun updateSensorLocation() {
+        fingerprintSensorLocation = authController.udfpsSensorLocation
+        faceSensorLocation = authController.faceAuthSensorLocation
+    }
+
+    private fun updateRippleColor() {
+        mView.setColor(
+            Utils.getColorAttr(sysuiContext, android.R.attr.colorAccent).defaultColor)
+    }
+
+    val keyguardUpdateMonitorCallback =
+        object : KeyguardUpdateMonitorCallback() {
+            override fun onBiometricAuthenticated(
+                userId: Int,
+                biometricSourceType: BiometricSourceType?,
+                isStrongBiometric: Boolean
+            ) {
+                showRipple(biometricSourceType)
+            }
+    }
+
+    val configurationChangedListener =
+        object : ConfigurationController.ConfigurationListener {
+            override fun onConfigChanged(newConfig: Configuration?) {
+                updateSensorLocation()
+            }
             override fun onUiModeChanged() {
                 updateRippleColor()
             }
@@ -68,43 +122,50 @@
             override fun onOverlayChanged() {
                 updateRippleColor()
             }
-        }
-        configurationController.addCallback(configurationChangedListener)
-
-        commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() }
-    }
-
-    fun setSensorLocation(x: Float, y: Float) {
-        rippleView.setSensorLocation(x, y)
-    }
-
-    fun setViewHost(viewHost: View) {
-        // Add the ripple view to its host layout
-        viewHost.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
-            override fun onViewDetachedFromWindow(view: View?) {}
-
-            override fun onViewAttachedToWindow(view: View?) {
-                (viewHost as ViewGroup).addView(rippleView)
-                keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
-                viewHost.removeOnAttachStateChangeListener(this)
-            }
-        })
-
-        updateRippleColor()
-    }
-
-    private fun updateRippleColor() {
-        rippleView.setColor(
-            Utils.getColorAttr(context, android.R.attr.colorAccent).defaultColor)
     }
 
     inner class AuthRippleCommand : Command {
         override fun execute(pw: PrintWriter, args: List<String>) {
-            rippleView.startRipple()
+            if (args.isEmpty()) {
+                invalidCommand(pw)
+            } else {
+                when (args[0]) {
+                    "fingerprint" -> {
+                        pw.println("fingerprint ripple sensorLocation=$fingerprintSensorLocation")
+                        showRipple(BiometricSourceType.FINGERPRINT)
+                    }
+                    "face" -> {
+                        pw.println("face ripple sensorLocation=$faceSensorLocation")
+                        showRipple(BiometricSourceType.FACE)
+                    }
+                    "custom" -> {
+                        if (args.size != 3 ||
+                            args[1].toFloatOrNull() == null ||
+                            args[2].toFloatOrNull() == null) {
+                            invalidCommand(pw)
+                            return
+                        }
+                        pw.println("custom ripple sensorLocation=" + args[1].toFloat() + ", " +
+                            args[2].toFloat())
+                        mView.setSensorLocation(PointF(args[1].toFloat(), args[2].toFloat()))
+                        showRipple()
+                    }
+                    else -> invalidCommand(pw)
+                }
+            }
         }
 
         override fun help(pw: PrintWriter) {
-            pw.println("Usage: adb shell cmd statusbar auth-ripple")
+            pw.println("Usage: adb shell cmd statusbar auth-ripple <command>")
+            pw.println("Available commands:")
+            pw.println("  fingerprint")
+            pw.println("  face")
+            pw.println("  custom <x-location: int> <y-location: int>")
+        }
+
+        fun invalidCommand(pw: PrintWriter) {
+            pw.println("invalid command")
+            help(pw)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index 1270677..374ddae 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -17,6 +17,7 @@
 
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
 import android.animation.ValueAnimator
 import android.content.Context
 import android.graphics.Canvas
@@ -24,9 +25,11 @@
 import android.graphics.PointF
 import android.util.AttributeSet
 import android.view.View
+import android.view.animation.PathInterpolator
+import com.android.internal.graphics.ColorUtils
 import com.android.systemui.statusbar.charging.RippleShader
 
-private const val RIPPLE_ANIMATION_DURATION: Long = 950
+private const val RIPPLE_ANIMATION_DURATION: Long = 1533
 private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.4f
 
 /**
@@ -36,42 +39,64 @@
 class AuthRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
     private var rippleInProgress: Boolean = false
     private val rippleShader = RippleShader()
-    private val defaultColor: Int = 0xffffffff.toInt()
     private val ripplePaint = Paint()
 
     init {
-        rippleShader.color = defaultColor
+        rippleShader.color = 0xffffffff.toInt() // default color
         rippleShader.progress = 0f
         rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
         ripplePaint.shader = rippleShader
-        visibility = View.GONE
+        visibility = GONE
     }
 
-    fun setSensorLocation(x: Float, y: Float) {
-        rippleShader.origin = PointF(x, y)
-        rippleShader.radius = maxOf(x, y, width - x, height - y).toFloat()
+    fun setSensorLocation(location: PointF) {
+        rippleShader.origin = location
+        rippleShader.radius = maxOf(location.x, location.y, width - location.x, height - location.y)
+            .toFloat()
     }
 
-    fun startRipple() {
+    fun startRipple(onAnimationEnd: Runnable?) {
         if (rippleInProgress) {
             return // Ignore if ripple effect is already playing
         }
+
         val animator = ValueAnimator.ofFloat(0f, 1f)
+        animator.interpolator = PathInterpolator(0.4f, 0f, 0f, 1f)
         animator.duration = RIPPLE_ANIMATION_DURATION
         animator.addUpdateListener { animator ->
             val now = animator.currentPlayTime
             rippleShader.progress = animator.animatedValue as Float
             rippleShader.time = now.toFloat()
+            rippleShader.distortionStrength = 1 - rippleShader.progress
             invalidate()
         }
-        animator.addListener(object : AnimatorListenerAdapter() {
+        val alphaInAnimator = ValueAnimator.ofInt(0, 127)
+        alphaInAnimator.duration = 167
+        alphaInAnimator.addUpdateListener { alphaInAnimator ->
+            rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color,
+                alphaInAnimator.animatedValue as Int)
+            invalidate()
+        }
+        val alphaOutAnimator = ValueAnimator.ofInt(127, 0)
+        alphaOutAnimator.startDelay = 417
+        alphaOutAnimator.duration = 1116
+        alphaOutAnimator.addUpdateListener { alphaOutAnimator ->
+            rippleShader.color = ColorUtils.setAlphaComponent(rippleShader.color,
+                alphaOutAnimator.animatedValue as Int)
+            invalidate()
+        }
+
+        val animatorSet = AnimatorSet()
+        animatorSet.playTogether(animator, alphaInAnimator, alphaOutAnimator)
+        animatorSet.addListener(object : AnimatorListenerAdapter() {
             override fun onAnimationEnd(animation: Animator?) {
+                onAnimationEnd?.run()
                 rippleInProgress = false
-                visibility = View.GONE
+                visibility = GONE
             }
         })
-        animator.start()
-        visibility = View.VISIBLE
+        animatorSet.start()
+        visibility = VISIBLE
         rippleInProgress = true
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
index f4993f4..d9e1b50 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -23,7 +23,6 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.ViewController;
 
@@ -47,7 +46,6 @@
     @NonNull final DumpManager mDumpManger;
 
     private boolean mNotificationShadeExpanded;
-    private int mStatusBarState;
 
     protected UdfpsAnimationViewController(
             T view,
@@ -86,7 +84,6 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("mStatusBarState=" + StatusBarState.toShortString(mStatusBarState));
         pw.println("mNotificationShadeExpanded=" + mNotificationShadeExpanded);
         pw.println("shouldPauseAuth()=" + shouldPauseAuth());
         pw.println("isPauseAuth=" + mView.isPauseAuth());
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 2bdbf51..405151d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -53,6 +54,7 @@
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -89,9 +91,9 @@
     @NonNull private final StatusBarStateController mStatusBarStateController;
     @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
     @NonNull private final DumpManager mDumpManager;
-    @NonNull private final AuthRippleController mAuthRippleController;
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @NonNull private final KeyguardViewMediator mKeyguardViewMediator;
+    @NonNull private FalsingManager mFalsingManager;
     // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
     // sensors, this, in addition to a lot of the code here, will be updated.
     @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -292,6 +294,8 @@
                     Log.v(TAG, "onTouch | finger up");
                     onFingerUp();
                 }
+                mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
+
                 break;
 
             default:
@@ -311,9 +315,9 @@
             @NonNull StatusBar statusBar,
             @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             @NonNull DumpManager dumpManager,
-            @NonNull AuthRippleController authRippleController,
             @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
-            @NonNull KeyguardViewMediator keyguardViewMediator) {
+            @NonNull KeyguardViewMediator keyguardViewMediator,
+            @NonNull FalsingManager falsingManager) {
         mContext = context;
         mInflater = inflater;
         // The fingerprint manager is queried for UDFPS before this class is constructed, so the
@@ -325,9 +329,9 @@
         mStatusBarStateController = statusBarStateController;
         mKeyguardViewManager = statusBarKeyguardViewManager;
         mDumpManager = dumpManager;
-        mAuthRippleController = authRippleController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mKeyguardViewMediator = keyguardViewMediator;
+        mFalsingManager = falsingManager;
 
         mSensorProps = findFirstUdfps();
         // At least one UDFPS sensor exists
@@ -353,10 +357,6 @@
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         context.registerReceiver(mBroadcastReceiver, filter);
-
-        mAuthRippleController.setViewHost(mStatusBar.getNotificationShadeWindowView());
-        mAuthRippleController.setSensorLocation(getSensorLocation().centerX(),
-                getSensorLocation().centerY());
     }
 
     @Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index dc0c685..35678e6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -91,6 +91,7 @@
         mStatusBarStateController.addCallback(mStateListener);
         mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
         mStateListener.onStateChanged(mStatusBarStateController.getState());
+        mAlternateAuthInterceptor.setQsExpanded(mKeyguardViewManager.isQsExpanded());
         mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
     }
 
@@ -117,6 +118,8 @@
         pw.println("mShowBouncer=" + mShowBouncer);
         pw.println("mFaceDetectRunning=" + mFaceDetectRunning);
         pw.println("mTransitioningFromHomeToKeyguard=" + mTransitioningFromHome);
+        pw.println("mStatusBarState" + StatusBarState.toShortString(mStatusBarState));
+        pw.println("mQsExpanded=" + mQsExpanded);
     }
 
     /**
@@ -215,6 +218,7 @@
         public void onStateChanged(int statusBarState) {
             mStatusBarState = statusBarState;
             mView.setStatusBarState(statusBarState);
+            updatePauseAuth();
         }
     };
 
@@ -283,7 +287,7 @@
 
                 @Override
                 public void dump(PrintWriter pw) {
-                    pw.print(getTag());
+                    pw.println(getTag());
                 }
             };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index f7beaf1..fb7fabb 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -114,9 +114,11 @@
             params.height = WindowManager.LayoutParams.MATCH_PARENT;
             params.width = WindowManager.LayoutParams.MATCH_PARENT;
             params.format = PixelFormat.TRANSLUCENT;
-
             params.type = WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
             params.setTitle("Charging Animation");
+            params.layoutInDisplayCutoutMode =
+                    WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+            params.setFitInsetsTypes(0 /* ignore all system bar insets */);
             params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                     | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                     | WindowManager.LayoutParams.FLAG_DIM_BEHIND;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 6812f77..5bdc7a4 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -224,7 +224,9 @@
         }
 
         FalsingClassifier.Result singleTapResult =
-                mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents());
+                mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents().isEmpty()
+                        ? mDataProvider.getPriorMotionEvents()
+                        : mDataProvider.getRecentMotionEvents());
         mPriorResults = Collections.singleton(singleTapResult);
 
         if (!singleTapResult.isFalse() && robustCheck) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index 47b41f5..4dd8780 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -38,6 +38,8 @@
     public static final int BOUNCER_UNLOCK = 8;
     public static final int PULSE_EXPAND = 9;
     public static final int BRIGHTNESS_SLIDER = 10;
+    public static final int UDFPS_AUTHENTICATION = 11;
+    public static final int DISABLED_UDFPS_AFFORDANCE = 12;
 
     @IntDef({
             QUICK_SETTINGS,
@@ -50,7 +52,9 @@
             GENERIC,
             BOUNCER_UNLOCK,
             PULSE_EXPAND,
-            BRIGHTNESS_SLIDER
+            BRIGHTNESS_SLIDER,
+            UDFPS_AUTHENTICATION,
+            DISABLED_UDFPS_AFFORDANCE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface InteractionType {}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
index 80d7863..6a70622 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
@@ -148,7 +148,9 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
-        if (interactionType == Classifier.BRIGHTNESS_SLIDER) {
+        if (interactionType == Classifier.BRIGHTNESS_SLIDER
+                || interactionType == Classifier.UDFPS_AUTHENTICATION
+                || interactionType == Classifier.DISABLED_UDFPS_AFFORDANCE) {
             return Result.passed(0);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
index f665565..50e94b3 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
@@ -42,6 +42,11 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
+        if (interactionType == Classifier.UDFPS_AUTHENTICATION
+                || interactionType == Classifier.DISABLED_UDFPS_AFFORDANCE) {
+            return Result.passed(0);
+        }
+
         boolean vertical = isVertical();
         boolean up = isUp();
         boolean right = isRight();
diff --git a/packages/SystemUI/src/com/android/systemui/media/IlluminationDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/IlluminationDrawable.kt
index 10b36e9..bc45113 100644
--- a/packages/SystemUI/src/com/android/systemui/media/IlluminationDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/IlluminationDrawable.kt
@@ -28,6 +28,7 @@
 import android.graphics.Outline
 import android.graphics.Paint
 import android.graphics.PixelFormat
+import android.graphics.Xfermode
 import android.graphics.drawable.Drawable
 import android.util.AttributeSet
 import android.util.MathUtils
@@ -48,7 +49,15 @@
 class IlluminationDrawable : Drawable() {
 
     private var themeAttrs: IntArray? = null
-    private var cornerRadius = 0f
+    private var cornerRadiusOverride = -1f
+    var cornerRadius = 0f
+    get() {
+        return if (cornerRadiusOverride >= 0) {
+            cornerRadiusOverride
+        } else {
+            field
+        }
+    }
     private var highlightColor = Color.TRANSPARENT
     private var tmpHsl = floatArrayOf(0f, 0f, 0f)
     private var paint = Paint()
@@ -122,8 +131,28 @@
         throw UnsupportedOperationException("Color filters are not supported")
     }
 
-    override fun setAlpha(value: Int) {
-        throw UnsupportedOperationException("Alpha is not supported")
+    override fun setAlpha(alpha: Int) {
+        if (alpha == paint.alpha) {
+            return
+        }
+
+        paint.alpha = alpha
+        invalidateSelf()
+
+        lightSources.forEach { it.alpha = alpha }
+    }
+
+    override fun getAlpha(): Int {
+        return paint.alpha
+    }
+
+    override fun setXfermode(mode: Xfermode?) {
+        if (mode == paint.xfermode) {
+            return
+        }
+
+        paint.xfermode = mode
+        invalidateSelf()
     }
 
     /**
@@ -171,9 +200,19 @@
 
     fun registerLightSource(lightSource: View) {
         if (lightSource.background is LightSourceDrawable) {
-            lightSources.add(lightSource.background as LightSourceDrawable)
+            registerLightSource(lightSource.background as LightSourceDrawable)
         } else if (lightSource.foreground is LightSourceDrawable) {
-            lightSources.add(lightSource.foreground as LightSourceDrawable)
+            registerLightSource(lightSource.foreground as LightSourceDrawable)
         }
     }
+
+    private fun registerLightSource(lightSource: LightSourceDrawable) {
+        lightSource.alpha = paint.alpha
+        lightSources.add(lightSource)
+    }
+
+    /** Set or remove the corner radius override. This is typically set during animations. */
+    fun setCornerRadiusOverride(cornerRadius: Float?) {
+        cornerRadiusOverride = cornerRadius ?: -1f
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/LightSourceDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/LightSourceDrawable.kt
index cee7101..e6035f3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/LightSourceDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/LightSourceDrawable.kt
@@ -184,8 +184,13 @@
         throw UnsupportedOperationException("Color filters are not supported")
     }
 
-    override fun setAlpha(value: Int) {
-        throw UnsupportedOperationException("Alpha is not supported")
+    override fun setAlpha(alpha: Int) {
+        if (alpha == paint.alpha) {
+            return
+        }
+
+        paint.alpha = alpha
+        invalidateSelf()
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 33681c8..a3ff375 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -1,7 +1,6 @@
 package com.android.systemui.media
 
 import android.animation.ArgbEvaluator
-import android.app.smartspace.SmartspaceTarget
 import android.content.Context
 import android.content.Intent
 import android.content.res.ColorStateList
@@ -202,18 +201,9 @@
                 }
             }
 
-            override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
-                Log.d(TAG, "My Smartspace media update is here")
-                addOrUpdateSmartspaceMediaRecommendations(key, data)
-            }
-
             override fun onMediaDataRemoved(key: String) {
                 removePlayer(key)
             }
-
-            override fun onSmartspaceMediaDataRemoved(key: String) {
-                Log.d(TAG, "My Smartspace media removal request is received")
-            }
         })
         mediaFrame.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
             // The pageIndicator is not laid out yet when we get the current state update,
@@ -301,10 +291,6 @@
         }
     }
 
-    private fun addOrUpdateSmartspaceMediaRecommendations(key: String, data: SmartspaceTarget) {
-        // TODO(b/182813345): Add Smartspace media recommendation view.
-    }
-
     private fun removePlayer(key: String, dismissMediaData: Boolean = true) {
         val removed = MediaPlayerData.removeMediaPlayer(key)
         removed?.apply {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index d3ae932..4fd8fe7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -49,6 +49,8 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
+import com.android.systemui.plugins.animation.GhostedViewLaunchAnimatorController;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.util.animation.TransitionLayout;
 
@@ -101,11 +103,12 @@
     // This will provide the corners for the album art.
     private final ViewOutlineProvider mViewOutlineProvider;
     private final MediaOutputDialogFactory mMediaOutputDialogFactory;
+
     /**
      * Initialize a new control panel
-     * @param context
+     *
      * @param backgroundExecutor background executor, used for processing artwork
-     * @param activityStarter activity starter
+     * @param activityStarter    activity starter
      */
     @Inject
     public MediaControlPanel(Context context, @Background Executor backgroundExecutor,
@@ -147,6 +150,7 @@
 
     /**
      * Get the view holder used to display media controls
+     *
      * @return the view holder
      */
     @Nullable
@@ -156,6 +160,7 @@
 
     /**
      * Get the view controller used to display media controls
+     *
      * @return the media view controller
      */
     @NonNull
@@ -165,7 +170,7 @@
 
     /**
      * Sets the listening state of the player.
-     *
+     * <p>
      * Should be set to true when the QS panel is open. Otherwise, false. This is a signal to avoid
      * unnecessary work when the QS panel is closed.
      *
@@ -177,6 +182,7 @@
 
     /**
      * Get the context
+     *
      * @return context
      */
     public Context getContext() {
@@ -244,7 +250,8 @@
         if (clickIntent != null) {
             mViewHolder.getPlayer().setOnClickListener(v -> {
                 if (mMediaViewController.isGutsVisible()) return;
-                mActivityStarter.postStartActivityDismissingKeyguard(clickIntent);
+                mActivityStarter.postStartActivityDismissingKeyguard(clickIntent,
+                        buildLaunchAnimatorController(mViewHolder.getPlayer()));
             });
         }
 
@@ -396,8 +403,42 @@
         mMediaViewController.refreshState();
     }
 
+    @Nullable
+    private ActivityLaunchAnimator.Controller buildLaunchAnimatorController(
+            TransitionLayout player) {
+        // TODO(b/174236650): Make sure that the carousel indicator also fades out.
+        // TODO(b/174236650): Instrument the animation to measure jank.
+        return new GhostedViewLaunchAnimatorController(player) {
+            @Override
+            protected float getCurrentTopCornerRadius() {
+                return ((IlluminationDrawable) player.getBackground()).getCornerRadius();
+            }
+
+            @Override
+            protected float getCurrentBottomCornerRadius() {
+                // TODO(b/184121838): Make IlluminationDrawable support top and bottom radius.
+                return getCurrentTopCornerRadius();
+            }
+
+            @Override
+            protected void setBackgroundCornerRadius(Drawable background, float topCornerRadius,
+                    float bottomCornerRadius) {
+                // TODO(b/184121838): Make IlluminationDrawable support top and bottom radius.
+                float radius = Math.min(topCornerRadius, bottomCornerRadius);
+                ((IlluminationDrawable) background).setCornerRadiusOverride(radius);
+            }
+
+            @Override
+            public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) {
+                super.onLaunchAnimationEnd(isExpandingFullyAbove);
+                ((IlluminationDrawable) player.getBackground()).setCornerRadiusOverride(null);
+            }
+        };
+    }
+
     /**
      * Close the guts for this player.
+     *
      * @param immediate {@code true} if it should be closed without animation
      */
     public void closeGuts(boolean immediate) {
@@ -427,7 +468,7 @@
         if (bounds.width() > mAlbumArtSize || bounds.height() > mAlbumArtSize) {
             float offsetX = (bounds.width() - mAlbumArtSize) / 2.0f;
             float offsetY = (bounds.height() - mAlbumArtSize) / 2.0f;
-            bounds.offset((int) -offsetX,(int) -offsetY);
+            bounds.offset((int) -offsetX, (int) -offsetY);
         }
         drawable.setBounds(bounds);
         return drawable;
@@ -435,6 +476,7 @@
 
     /**
      * Get the current media controller
+     *
      * @return the controller
      */
     public MediaController getController() {
@@ -443,6 +485,7 @@
 
     /**
      * Check whether the media controlled by this player is currently playing
+     *
      * @return whether it is playing, or false if no controller information
      */
     public boolean isPlaying() {
@@ -451,6 +494,7 @@
 
     /**
      * Check whether the given controller is currently playing
+     *
      * @param controller media controller to check
      * @return whether it is playing, or false if no controller information
      */
@@ -468,7 +512,7 @@
     }
 
     private void setVisibleAndAlpha(ConstraintSet set, int actionId, boolean visible) {
-        set.setVisibility(actionId, visible? ConstraintSet.VISIBLE : ConstraintSet.GONE);
+        set.setVisibility(actionId, visible ? ConstraintSet.VISIBLE : ConstraintSet.GONE);
         set.setAlpha(actionId, visible ? 1.0f : 0.0f);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
index 2c094b8..aa3699e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.media
 
-import android.app.smartspace.SmartspaceTarget
 import javax.inject.Inject
 
 /**
@@ -38,18 +37,10 @@
         }
     }
 
-    override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
-        listeners.toSet().forEach { it.onSmartspaceMediaDataLoaded(key, data) }
-    }
-
     override fun onMediaDataRemoved(key: String) {
         remove(key)
     }
 
-    override fun onSmartspaceMediaDataRemoved(key: String) {
-        listeners.toSet().forEach { it.onSmartspaceMediaDataRemoved(key) }
-    }
-
     override fun onMediaDeviceChanged(
         key: String,
         oldKey: String?,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index aab2747..1f580a9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.media
 
-import android.app.smartspace.SmartspaceTarget
 import android.util.Log
 import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.broadcast.BroadcastDispatcher
@@ -83,10 +82,6 @@
         }
     }
 
-    override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
-        listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data) }
-    }
-
     override fun onMediaDataRemoved(key: String) {
         allEntries.remove(key)
         userEntries.remove(key)?.let {
@@ -97,10 +92,6 @@
         }
     }
 
-    override fun onSmartspaceMediaDataRemoved(key: String) {
-        listeners.forEach { it.onSmartspaceMediaDataRemoved(key) }
-    }
-
     @VisibleForTesting
     internal fun handleUserSwitched(id: Int) {
         // If the user changes, remove all current MediaData objects and inform listeners
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index dfd588d..41c9dae 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -18,10 +18,6 @@
 
 import android.app.Notification
 import android.app.PendingIntent
-import android.app.smartspace.SmartspaceConfig
-import android.app.smartspace.SmartspaceManager
-import android.app.smartspace.SmartspaceSession
-import android.app.smartspace.SmartspaceTarget
 import android.content.BroadcastReceiver
 import android.content.ContentResolver
 import android.content.Context
@@ -37,7 +33,6 @@
 import android.media.session.MediaController
 import android.media.session.MediaSession
 import android.net.Uri
-import android.os.Parcelable
 import android.os.UserHandle
 import android.service.notification.StatusBarNotification
 import android.text.TextUtils
@@ -50,7 +45,6 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.BcSmartspaceDataPlugin
 import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
 import com.android.systemui.statusbar.notification.row.HybridGroupManager
 import com.android.systemui.util.Assert
@@ -60,7 +54,6 @@
 import java.io.IOException
 import java.io.PrintWriter
 import java.util.concurrent.Executor
-import java.util.concurrent.Executors
 import javax.inject.Inject
 
 // URI fields to try loading album art from
@@ -106,16 +99,9 @@
     mediaDataCombineLatest: MediaDataCombineLatest,
     private val mediaDataFilter: MediaDataFilter,
     private val activityStarter: ActivityStarter,
-    private val smartspaceMediaDataProvider: SmartspaceMediaDataProvider,
     private var useMediaResumption: Boolean,
     private val useQsMediaPlayer: Boolean
-) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener {
-
-    companion object {
-        // UI surface label for subscribing Smartspace updates.
-        @JvmField
-        val SMARTSPACE_UI_SURFACE_LABEL = "media_data_manager"
-    }
+) : Dumpable {
 
     private val themeText = com.android.settingslib.Utils.getColorAttr(context,
             com.android.internal.R.attr.textColorPrimary).defaultColor
@@ -131,8 +117,6 @@
     // TODO(b/159539991#comment5): Move internal listeners to separate package.
     private val internalListeners: MutableSet<Listener> = mutableSetOf()
     private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
-    // There should ONLY be at most one Smartspace media recommendation.
-    private var smartspaceMediaTarget: SmartspaceTarget? = null
     internal var appsBlockedFromResume: MutableSet<String> = Utils.getBlockedMediaApps(context)
         set(value) {
             // Update list
@@ -144,7 +128,6 @@
                 removeAllForPackage(it)
             }
         }
-    private var smartspaceSession: SmartspaceSession? = null
 
     @Inject
     constructor(
@@ -160,13 +143,11 @@
         mediaDeviceManager: MediaDeviceManager,
         mediaDataCombineLatest: MediaDataCombineLatest,
         mediaDataFilter: MediaDataFilter,
-        activityStarter: ActivityStarter,
-        smartspaceMediaDataProvider: SmartspaceMediaDataProvider
+        activityStarter: ActivityStarter
     ) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory,
             broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener,
             mediaSessionBasedFilter, mediaDeviceManager, mediaDataCombineLatest, mediaDataFilter,
-            activityStarter, smartspaceMediaDataProvider, Utils.useMediaResumption(context),
-            Utils.useQsMediaPlayer(context))
+            activityStarter, Utils.useMediaResumption(context), Utils.useQsMediaPlayer(context))
 
     private val appChangeReceiver = object : BroadcastReceiver() {
         override fun onReceive(context: Context, intent: Intent) {
@@ -219,31 +200,9 @@
         }
         // BroadcastDispatcher does not allow filters with data schemes
         context.registerReceiver(appChangeReceiver, uninstallFilter)
-
-        // Register for Smartspace data updates.
-        smartspaceMediaDataProvider.registerListener(this)
-        val smartspaceManager: SmartspaceManager =
-            context.getSystemService(SmartspaceManager::class.java)
-        smartspaceSession = smartspaceManager.createSmartspaceSession(
-            SmartspaceConfig.Builder(context, SMARTSPACE_UI_SURFACE_LABEL).build())
-        smartspaceSession?.let {
-            it.registerSmartspaceUpdates(
-                // Use a new thread listening to Smartspace updates instead of using the existing
-                // backgroundExecutor. SmartspaceSession has scheduled routine updates which can be
-                // unpredictable on test simulators, using the backgroundExecutor makes it's hard to
-                // test the threads numbers.
-                // Switch to use backgroundExecutor when SmartspaceSession has a good way to be
-                // mocked.
-                Executors.newCachedThreadPool(),
-                SmartspaceSession.Callback { targets ->
-                    smartspaceMediaDataProvider.onTargetsAvailable(targets)
-                })
-        }
-        smartspaceSession?.let { it.requestSmartspaceUpdate() }
     }
 
     fun destroy() {
-        smartspaceMediaDataProvider.unregisterListener(this)
         context.unregisterReceiver(appChangeReceiver)
     }
 
@@ -350,7 +309,7 @@
     private fun addInternalListener(listener: Listener) = internalListeners.add(listener)
 
     /**
-     * Notify internal listeners of media loaded event.
+     * Notify internal listeners of loaded event.
      *
      * External listeners registered with [addListener] will be notified after the event propagates
      * through the internal listener pipeline.
@@ -360,17 +319,7 @@
     }
 
     /**
-     * Notify internal listeners of Smartspace media loaded event.
-     *
-     * External listeners registered with [addListener] will be notified after the event propagates
-     * through the internal listener pipeline.
-     */
-    private fun notifySmartspaceMediaDataLoaded(key: String, info: SmartspaceTarget) {
-        internalListeners.forEach { it.onSmartspaceMediaDataLoaded(key, info) }
-    }
-
-    /**
-     * Notify internal listeners of media removed event.
+     * Notify internal listeners of removed event.
      *
      * External listeners registered with [addListener] will be notified after the event propagates
      * through the internal listener pipeline.
@@ -380,16 +329,6 @@
     }
 
     /**
-     * Notify internal listeners of Smartspace media removed event.
-     *
-     * External listeners registered with [addListener] will be notified after the event propagates
-     * through the internal listener pipeline.
-     */
-    private fun notifySmartspaceMediaDataRemoved(key: String) {
-        internalListeners.forEach { it.onSmartspaceMediaDataRemoved(key) }
-    }
-
-    /**
      * Called whenever the player has been paused or stopped for a while, or swiped from QQS.
      * This will make the player not active anymore, hiding it from QQS and Keyguard.
      * @see MediaData.active
@@ -662,49 +601,6 @@
         }
     }
 
-    override fun onSmartspaceTargetsUpdated(targets: List<Parcelable>) {
-        Log.d(TAG, "My Smartspace media updates are here")
-        val mediaTargets = targets.filterIsInstance<SmartspaceTarget>()
-        when (mediaTargets.size) {
-            0 -> {
-                Log.d(TAG, "Empty Smartspace media target")
-                smartspaceMediaTarget?.let {
-                    notifySmartspaceMediaDataRemoved(it.smartspaceTargetId)
-                }
-                smartspaceMediaTarget = null
-            }
-            1 -> {
-                // TODO(b/182811956): Reactivate the resumable media sessions whose last active
-                //  time is within 3 hours.
-                // TODO(b/182813365): Wire this up with MediaTimeoutListener so the session can be
-                //  expired after 30 seconds.
-                val newMediaTarget = mediaTargets.get(0)
-                if (smartspaceMediaTarget != null &&
-                    smartspaceMediaTarget!!.smartspaceTargetId ==
-                    newMediaTarget.smartspaceTargetId) {
-                    // The same Smartspace updates can be received. Only send the first one.
-                    Log.d(TAG, "Same Smartspace media update exists. Skip loading data.")
-                } else {
-                    smartspaceMediaTarget?.let {
-                        notifySmartspaceMediaDataRemoved(it.smartspaceTargetId)
-                    }
-                    notifySmartspaceMediaDataLoaded(
-                        newMediaTarget.smartspaceTargetId, newMediaTarget)
-                    smartspaceMediaTarget = newMediaTarget
-                }
-            }
-            else -> {
-                // There should NOT be more than 1 Smartspace media update. When it happens, it
-                // indicates a bad state or an error. Reset the status accordingly.
-                Log.wtf(TAG, "More than 1 Smartspace Media Update. Resetting the status...")
-                smartspaceMediaTarget?.let {
-                    notifySmartspaceMediaDataRemoved(it.smartspaceTargetId)
-                }
-                smartspaceMediaTarget = null
-            }
-        }
-    }
-
     fun onNotificationRemoved(key: String) {
         Assert.isMainThread()
         val removed = mediaEntries.remove(key)
@@ -789,16 +685,10 @@
          */
         fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {}
 
-        /** Called whenever there's new Smartspace media data loaded. */
-        fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {}
-
         /**
          * Called whenever a previously existing Media notification was removed
          */
         fun onMediaDataRemoved(key: String) {}
-
-        /** Called whenever a previously existing Smartspace media data was removed.  */
-        fun onSmartspaceMediaDataRemoved(key: String) {}
     }
 
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index 8c12a30..b74ca28 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -1,6 +1,5 @@
 package com.android.systemui.media
 
-import android.app.smartspace.SmartspaceTarget
 import android.graphics.Rect
 import android.util.ArraySet
 import android.view.View
@@ -54,17 +53,9 @@
             updateViewVisibility()
         }
 
-        override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
-            updateViewVisibility()
-        }
-
         override fun onMediaDataRemoved(key: String) {
             updateViewVisibility()
         }
-
-        override fun onSmartspaceMediaDataRemoved(key: String) {
-            updateViewVisibility()
-        }
     }
 
     fun addVisibilityChangeListener(listener: (Boolean) -> Unit) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
index d973478..f695622 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaSessionBasedFilter.kt
@@ -16,7 +16,6 @@
 
 package com.android.systemui.media
 
-import android.app.smartspace.SmartspaceTarget
 import android.content.ComponentName
 import android.content.Context
 import android.media.session.MediaController
@@ -135,12 +134,6 @@
         }
     }
 
-    override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) {
-        backgroundExecutor.execute {
-            dispatchSmartspaceMediaDataLoaded(key, data)
-        }
-    }
-
     override fun onMediaDataRemoved(key: String) {
         // Queue on background thread to ensure ordering of loaded and removed events is maintained.
         backgroundExecutor.execute {
@@ -149,12 +142,6 @@
         }
     }
 
-    override fun onSmartspaceMediaDataRemoved(key: String) {
-        backgroundExecutor.execute {
-            dispatchSmartspaceMediaDataRemoved(key)
-        }
-    }
-
     private fun dispatchMediaDataLoaded(key: String, oldKey: String?, info: MediaData) {
         foregroundExecutor.execute {
             listeners.toSet().forEach { it.onMediaDataLoaded(key, oldKey, info) }
@@ -167,18 +154,6 @@
         }
     }
 
-    private fun dispatchSmartspaceMediaDataLoaded(key: String, info: SmartspaceTarget) {
-        foregroundExecutor.execute {
-            listeners.toSet().forEach { it.onSmartspaceMediaDataLoaded(key, info) }
-        }
-    }
-
-    private fun dispatchSmartspaceMediaDataRemoved(key: String) {
-        foregroundExecutor.execute {
-            listeners.toSet().forEach { it.onSmartspaceMediaDataRemoved(key) }
-        }
-    }
-
     private fun handleControllersChanged(controllers: List<MediaController>) {
         packageControllers.clear()
         controllers.forEach {
diff --git a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt
deleted file mode 100644
index 0eab572..0000000
--- a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.android.systemui.media
-
-import android.app.smartspace.SmartspaceTarget
-import com.android.systemui.plugins.BcSmartspaceDataPlugin
-import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener
-import javax.inject.Inject
-
-/** Provides SmartspaceTargets of media types for SystemUI media control. */
-class SmartspaceMediaDataProvider @Inject constructor() : BcSmartspaceDataPlugin {
-
-    private val smartspaceMediaTargetListeners: MutableList<SmartspaceTargetListener> =
-        mutableListOf()
-    private var smartspaceMediaTargets: List<SmartspaceTarget> = listOf()
-
-    override fun registerListener(smartspaceTargetListener: SmartspaceTargetListener) {
-        smartspaceMediaTargetListeners.add(smartspaceTargetListener)
-    }
-
-    override fun unregisterListener(smartspaceTargetListener: SmartspaceTargetListener?) {
-        smartspaceMediaTargetListeners.remove(smartspaceTargetListener)
-    }
-
-    /** Updates Smartspace data and propagates it to any listeners.  */
-    fun onTargetsAvailable(targets: List<SmartspaceTarget>) {
-        // Filter out non-media targets.
-        val mediaTargets = mutableListOf<SmartspaceTarget>()
-        for (target in targets) {
-            val smartspaceTarget = target
-            if (smartspaceTarget.featureType == SmartspaceTarget.FEATURE_MEDIA) {
-                mediaTargets.add(smartspaceTarget)
-            }
-        }
-
-        smartspaceMediaTargets = mediaTargets
-        smartspaceMediaTargetListeners.forEach {
-            it.onSmartspaceTargetsUpdated(smartspaceMediaTargets)
-        }
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 9a889e0..980024e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -225,16 +225,7 @@
             mQsPanelCallback.onScanStateChanged(false);
         }
         sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-
-        if (mShouldAnimate) {
-            animateDetailVisibleDiff(x, y, visibleDiff, listener);
-        } else {
-            if (showingDetail) {
-                showImmediately();
-            } else {
-                hideImmediately();
-            }
-        }
+        animateDetailVisibleDiff(x, y, visibleDiff, listener);
     }
 
     protected void animateDetailVisibleDiff(int x, int y, boolean visibleDiff, AnimatorListener listener) {
@@ -242,27 +233,16 @@
             mAnimatingOpen = mDetailAdapter != null;
             if (mFullyExpanded || mDetailAdapter != null) {
                 setAlpha(1);
-                mClipper.animateCircularClip(x, y, mDetailAdapter != null, listener);
+                mClipper.updateCircularClip(mShouldAnimate, x, y, mDetailAdapter != null, listener);
             } else {
                 animate().alpha(0)
-                        .setDuration(FADE_DURATION)
+                        .setDuration(mShouldAnimate ? FADE_DURATION : 0)
                         .setListener(listener)
                         .start();
             }
         }
     }
 
-    void showImmediately() {
-        setVisibility(VISIBLE);
-        mClipper.cancelAnimator();
-        mClipper.showBackground();
-    }
-
-    public void hideImmediately() {
-        mClipper.cancelAnimator();
-        setVisibility(View.GONE);
-    }
-
     protected void setupDetailFooter(DetailAdapter adapter) {
         final Intent settingsIntent = adapter.getSettingsIntent();
         mDetailSettingsButton.setVisibility(settingsIntent != null ? VISIBLE : GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
index daf8ca3..63cedd0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
@@ -37,6 +37,19 @@
     }
 
     public void animateCircularClip(int x, int y, boolean in, AnimatorListener listener) {
+        updateCircularClip(true /* animate */, x, y, in, listener);
+    }
+
+    /**
+     * @param animate whether or not animation has a duration of 0. Either way, {@code listener}
+     *               will be called.
+     * @param x x position where animation should originate
+     * @param y y position where animation should originate
+     * @param in whether animating in or out
+     * @param listener Animation listener. Called whether or not {@code animate} is true.
+     */
+    public void updateCircularClip(boolean animate, int x, int y, boolean in,
+            AnimatorListener listener) {
         if (mAnimator != null) {
             mAnimator.cancel();
         }
@@ -58,15 +71,16 @@
         } else {
             mAnimator = ViewAnimationUtils.createCircularReveal(mDetail, x, y, r, innerR);
         }
-        mAnimator.setDuration((long)(mAnimator.getDuration() * 1.5));
+        mAnimator.setDuration(animate ? (long) (mAnimator.getDuration() * 1.5) : 0);
         if (listener != null) {
             mAnimator.addListener(listener);
         }
         if (in) {
-            mBackground.startTransition((int)(mAnimator.getDuration() * 0.6));
+            mBackground.startTransition(animate ? (int) (mAnimator.getDuration() * 0.6) : 0);
             mAnimator.addListener(mVisibleOnStart);
         } else {
-            mDetail.postDelayed(mReverseBackground, (long)(mAnimator.getDuration() * 0.65));
+            mDetail.postDelayed(mReverseBackground,
+                    animate ? (long) (mAnimator.getDuration() * 0.65) : 0);
             mAnimator.addListener(mGoneOnEnd);
         }
         mAnimator.start();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index a45b1319..abe3219 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -173,7 +173,7 @@
     @Override
     public void init(QSTile tile) {
         init(v -> tile.click(), v -> tile.secondaryClick(), view -> {
-            tile.longClick();
+            tile.longClick(this);
             return true;
         });
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index a17aeba..1cb2683 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -31,6 +31,7 @@
 
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
@@ -43,6 +44,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
+import android.view.View;
 
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleOwner;
@@ -58,6 +60,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
@@ -276,7 +279,9 @@
         mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_CLICK, 0, getMetricsSpec(),
                 getInstanceId());
         mQSLogger.logTileClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
-        mHandler.sendEmptyMessage(H.CLICK);
+        if (!mFalsingManager.isFalseTap(true, 0.1)) {
+            mHandler.sendEmptyMessage(H.CLICK);
+        }
     }
 
     public void secondaryClick() {
@@ -290,14 +295,15 @@
         mHandler.sendEmptyMessage(H.SECONDARY_CLICK);
     }
 
-    public void longClick() {
+    @Override
+    public void longClick(@Nullable View view) {
         mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)
                 .addTaggedData(FIELD_STATUS_BAR_STATE,
                         mStatusBarStateController.getState())));
         mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_LONG_PRESS, 0, getMetricsSpec(),
                 getInstanceId());
         mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
-        mHandler.sendEmptyMessage(H.LONG_CLICK);
+        mHandler.obtainMessage(H.LONG_CLICK, view).sendToTarget();
     }
 
     public LogMaker populate(LogMaker logMaker) {
@@ -372,10 +378,15 @@
 
     /**
      * Handles long click on the tile by launching the {@link Intent} defined in
-     * {@link QSTileImpl#getLongClickIntent}
+     * {@link QSTileImpl#getLongClickIntent}.
+     *
+     * @param view The view from which the opening window will be animated.
      */
-    protected void handleLongClick() {
-        mActivityStarter.postStartActivityDismissingKeyguard(getLongClickIntent(), 0);
+    protected void handleLongClick(@Nullable View view) {
+        ActivityLaunchAnimator.Controller animationController =
+                view != null ? ActivityLaunchAnimator.Controller.fromView(view) : null;
+        mActivityStarter.postStartActivityDismissingKeyguard(getLongClickIntent(), 0,
+                animationController);
     }
 
     /**
@@ -605,16 +616,14 @@
                                 mContext, mEnforcedAdmin);
                         mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
                     } else {
-                        if (!mFalsingManager.isFalseTap(true, 0.1)) {
-                            handleClick();
-                        }
+                        handleClick();
                     }
                 } else if (msg.what == SECONDARY_CLICK) {
                     name = "handleSecondaryClick";
                     handleSecondaryClick();
                 } else if (msg.what == LONG_CLICK) {
                     name = "handleLongClick";
-                    handleLongClick();
+                    handleLongClick((View) msg.obj);
                 } else if (msg.what == REFRESH_STATE) {
                     name = "handleRefreshState";
                     handleRefreshState(msg.obj);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index fa99eed..d78dbae9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -142,7 +142,7 @@
     }
 
     @Override
-    protected void handleLongClick() {
+    protected void handleLongClick(View view) {
         handleClick();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 31a98db..b7cb615 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -22,6 +22,7 @@
 import android.os.Looper;
 import android.provider.MediaStore;
 import android.service.quicksettings.Tile;
+import android.view.View;
 import android.widget.Switch;
 
 import com.android.internal.logging.MetricsLogger;
@@ -107,7 +108,7 @@
     }
 
     @Override
-    protected void handleLongClick() {
+    protected void handleLongClick(View view) {
         handleClick();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index bf96558..47212d2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -48,6 +48,7 @@
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.wallet.ui.WalletActivity;
 
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -117,8 +118,22 @@
 
     @Override
     protected void handleClick() {
-        mActivityStarter.postStartActivityDismissingKeyguard(
-                mQuickAccessWalletClient.createWalletIntent(), /* delay= */ 0);
+        mUiHandler.post(() -> {
+            mHost.collapsePanels();
+            if (mHasCard) {
+                Intent intent = new Intent(mContext, WalletActivity.class)
+                        .setAction(Intent.ACTION_VIEW)
+                        .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+                mContext.startActivity(intent);
+            } else {
+                if (mQuickAccessWalletClient.createWalletIntent() == null) {
+                    Log.w(TAG, "Could not get intent of the wallet app.");
+                    return;
+                }
+                mActivityStarter.postStartActivityDismissingKeyguard(
+                        mQuickAccessWalletClient.createWalletIntent(), /* delay= */ 0);
+            }
+        });
     }
 
     @Override
@@ -147,9 +162,7 @@
         } else {
             state.state = Tile.STATE_UNAVAILABLE;
         }
-        if (!isDeviceLocked) {
-            state.sideViewDrawable = mCardViewDrawable;
-        }
+        state.sideViewDrawable = isDeviceLocked ? null : mCardViewDrawable;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
index 8e6398f..9525975 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CrossFadeHelper.java
@@ -105,9 +105,7 @@
             }
         } else if (view.getLayerType() == View.LAYER_TYPE_HARDWARE
                 && view.getTag(R.id.cross_fade_layer_type_changed_tag) != null) {
-            if (view.getTag(R.id.cross_fade_layer_type_changed_tag) != null) {
-                view.setLayerType(View.LAYER_TYPE_NONE, null);
-            }
+            view.setLayerType(View.LAYER_TYPE_NONE, null);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index f51fbed..ec3a857 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -97,4 +97,8 @@
     public boolean isOngoingCallStatusBarChipEnabled() {
         return mFlagReader.isEnabled(R.bool.flag_ongoing_call_status_bar_chip);
     }
+
+    public boolean isSmartspaceEnabled() {
+        return mFlagReader.isEnabled(R.bool.flag_smartspace);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 5782d38..c565a271 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -24,7 +24,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
-import android.app.smartspace.SmartspaceTarget;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
@@ -249,11 +248,6 @@
             }
 
             @Override
-            public void onSmartspaceMediaDataLoaded(@NonNull String key,
-                    @NonNull SmartspaceTarget data) {
-            }
-
-            @Override
             public void onMediaDataRemoved(@NonNull String key) {
                 mNotifPipeline.getAllNotifs()
                         .stream()
@@ -266,9 +260,6 @@
                                     getDismissedByUserStats(entry));
                         });
             }
-
-            @Override
-            public void onSmartspaceMediaDataRemoved(@NonNull String key) {}
         });
     }
 
@@ -322,11 +313,6 @@
             }
 
             @Override
-            public void onSmartspaceMediaDataLoaded(@NonNull String key,
-                    @NonNull SmartspaceTarget data) {
-            }
-
-            @Override
             public void onMediaDataRemoved(@NonNull String key) {
                 NotificationEntry entry = mEntryManager.getPendingOrActiveNotif(key);
                 if (entry != null) {
@@ -337,9 +323,6 @@
                             NotificationListenerService.REASON_CANCEL);
                 }
             }
-
-            @Override
-            public void onSmartspaceMediaDataRemoved(@NonNull String key) {}
         });
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 2856ebb..23e6a9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -576,6 +576,7 @@
             mKeysKeptForRemoteInputHistory.remove(key);
         }
         if (mRemoteInputController.isRemoteInputActive(entry)) {
+            entry.mRemoteEditImeVisible = false;
             mRemoteInputController.removeRemoteInput(entry, null);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index e27c1a2..bb6165a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -35,7 +35,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator
+import com.android.systemui.statusbar.notification.ExpandAnimationParameters
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
 import com.android.systemui.statusbar.phone.DozeParameters
@@ -107,11 +107,18 @@
                 else 0)
         }
 
+    var qsPanelExpansion = 0f
+        set(value) {
+            if (field == value) return
+            field = value
+            scheduleUpdate()
+        }
+
     /**
      * When launching an app from the shade, the animations progress should affect how blurry the
      * shade is, overriding the expansion amount.
      */
-    var notificationLaunchAnimationParams: ActivityLaunchAnimator.ExpandAnimationParameters? = null
+    var notificationLaunchAnimationParams: ExpandAnimationParameters? = null
         set(value) {
             field = value
             if (value != null) {
@@ -158,8 +165,9 @@
         updateScheduled = false
         val normalizedBlurRadius = MathUtils.constrain(shadeAnimation.radius,
                 blurUtils.minBlurRadius, blurUtils.maxBlurRadius)
-        val combinedBlur = (shadeSpring.radius * INTERACTION_BLUR_FRACTION +
+        var combinedBlur = (shadeSpring.radius * INTERACTION_BLUR_FRACTION +
                 normalizedBlurRadius * ANIMATION_BLUR_FRACTION).toInt()
+        combinedBlur = max(combinedBlur, blurUtils.blurRadiusOfRatio(qsPanelExpansion))
         var shadeRadius = max(combinedBlur, wakeAndUnlockBlurRadius).toFloat()
         shadeRadius *= 1f - brightnessMirrorSpring.ratio
         val launchProgress = notificationLaunchAnimationParams?.linearProgress ?: 0f
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index 24515f7..b6357b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -90,9 +90,14 @@
     /** Sets the state of whether the user activities are forced or not. */
     default void setForceUserActivity(boolean forceUserActivity) {}
 
-    /** Sets the state of whether the user activities are forced or not. */
+    /** Sets the state of whether an activity is launching or not. */
     default void setLaunchingActivity(boolean launching) {}
 
+    /** Get whether an activity is launching or not. */
+    default boolean isLaunchingActivity() {
+        return false;
+    }
+
     /** Sets the state of whether the scrim is visible or not. */
     default void setScrimsVisibility(int scrimsVisibility) {}
 
@@ -137,7 +142,7 @@
     default void setDozing(boolean dozing) {}
 
     /** Sets the state of whether plugin open is forced or not. */
-    default void setForcePluginOpen(boolean forcePluginOpen) {}
+    default void setForcePluginOpen(boolean forcePluginOpen, Object token) {}
 
     /** Gets whether we are forcing plugin open or not. */
     default boolean getForcePluginOpen() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index 6023b7f..c811fdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -26,17 +26,18 @@
 import android.graphics.PorterDuff.Mode;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
-import android.os.Handler;
 import android.os.Looper;
 import android.util.AttributeSet;
 import android.view.View;
 
+import androidx.annotation.DimenRes;
 import androidx.core.graphics.ColorUtils;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.colorextraction.drawable.ScrimDrawable;
+import com.android.systemui.R;
 
 import java.util.concurrent.Executor;
 
@@ -47,6 +48,10 @@
  * need to be careful to synchronize when necessary.
  */
 public class ScrimView extends View {
+
+    @DimenRes
+    private static final int CORNER_RADIUS = R.dimen.notification_scrim_corner_radius;
+
     private final Object mColorLock = new Object();
 
     @GuardedBy("mColorLock")
@@ -260,4 +265,27 @@
             mExecutor.execute(r);
         }
     }
+
+    /**
+     * Make bottom edge concave so overlap between layers is not visible for alphas between 0 and 1
+     * @return height of concavity
+     */
+    public float enableBottomEdgeConcave() {
+        if (mDrawable instanceof ScrimDrawable) {
+            float radius = getResources().getDimensionPixelSize(CORNER_RADIUS);
+            ((ScrimDrawable) mDrawable).setBottomEdgeConcave(radius);
+            return radius;
+        }
+        return 0;
+    }
+
+    /**
+     * Enable view to have rounded corners with radius of {@link #CORNER_RADIUS}
+     */
+    public void enableRoundedCorners() {
+        if (mDrawable instanceof ScrimDrawable) {
+            int radius = getResources().getDimensionPixelSize(CORNER_RADIUS);
+            ((ScrimDrawable) mDrawable).setRoundedCorners(radius);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
index 77b4186..3196eba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/ChargingRippleView.kt
@@ -33,11 +33,11 @@
  * Expanding ripple effect that shows when charging begins.
  */
 class ChargingRippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
-    private var rippleInProgress: Boolean = false
     private val rippleShader = RippleShader()
     private val defaultColor: Int = 0xffffffff.toInt()
     private val ripplePaint = Paint()
 
+    var rippleInProgress: Boolean = false
     var radius: Float = 0.0f
         set(value) { rippleShader.radius = value }
     var origin: PointF = PointF()
@@ -62,7 +62,8 @@
         super.onAttachedToWindow()
     }
 
-    fun startRipple() {
+    @JvmOverloads
+    fun startRipple(onAnimationEnd: Runnable? = null) {
         if (rippleInProgress) {
             return // Ignore if ripple effect is already playing
         }
@@ -80,6 +81,7 @@
             override fun onAnimationEnd(animation: Animator?) {
                 rippleInProgress = false
                 visibility = View.GONE
+                onAnimationEnd?.run()
             }
         })
         animator.start()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
index 2900462..71af271 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt
@@ -17,11 +17,11 @@
 package com.android.systemui.statusbar.charging
 
 import android.content.Context
-import android.content.res.Configuration
+import android.graphics.PixelFormat
 import android.graphics.PointF
 import android.util.DisplayMetrics
 import android.view.View
-import android.view.ViewGroupOverlay
+import android.view.WindowManager
 import com.android.internal.annotations.VisibleForTesting
 import com.android.settingslib.Utils
 import com.android.systemui.dagger.SysUISingleton
@@ -30,9 +30,7 @@
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.KeyguardStateController
 import java.io.PrintWriter
-import java.lang.Integer.max
 import javax.inject.Inject
 
 /***
@@ -45,11 +43,22 @@
     batteryController: BatteryController,
     configurationController: ConfigurationController,
     featureFlags: FeatureFlags,
-    private val context: Context,
-    private val keyguardStateController: KeyguardStateController
+    private val context: Context
 ) {
     private var charging: Boolean? = null
     private val rippleEnabled: Boolean = featureFlags.isChargingRippleEnabled
+    private val windowLayoutParams = WindowManager.LayoutParams().apply {
+        width = WindowManager.LayoutParams.MATCH_PARENT
+        height = WindowManager.LayoutParams.MATCH_PARENT
+        layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+        format = PixelFormat.TRANSLUCENT
+        type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY
+        fitInsetsTypes = 0 // Ignore insets from all system bars
+        title = "Wired Charging Animation"
+        flags = (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
+    }
+
     @VisibleForTesting
     var rippleView: ChargingRippleView = ChargingRippleView(context, attrs = null)
 
@@ -68,8 +77,8 @@
                 val wasCharging = charging
                 charging = nowCharging
                 // Only triggers when the keyguard is active and the device is just plugged in.
-                if (wasCharging == false && nowCharging && keyguardStateController.isShowing) {
-                    rippleView.startRipple()
+                if ((wasCharging == null || !wasCharging) && nowCharging) {
+                    startRipple()
                 }
             }
         }
@@ -85,46 +94,41 @@
             override fun onOverlayChanged() {
                 updateRippleColor()
             }
-            override fun onConfigChanged(newConfig: Configuration?) {
-                layoutRippleView()
-            }
         }
         configurationController.addCallback(configurationChangedListener)
 
         commandRegistry.registerCommand("charging-ripple") { ChargingRippleCommand() }
-    }
-
-    fun setViewHost(viewHost: View) {
-        // Add the ripple view as an overlay of the root view so that it always
-        // shows on top.
-        viewHost.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
-            override fun onViewDetachedFromWindow(view: View?) {}
-
-            override fun onViewAttachedToWindow(view: View?) {
-                (viewHost.viewRootImpl.view.overlay as ViewGroupOverlay).add(rippleView)
-                layoutRippleView()
-                viewHost.removeOnAttachStateChangeListener(this)
-            }
-        })
-
         updateRippleColor()
     }
 
-    private fun layoutRippleView() {
-        // Overlays are not auto measured and laid out so we do it manually here.
+    fun startRipple() {
+        if (rippleView.rippleInProgress) {
+            return
+        }
+        val mWM = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+        windowLayoutParams.packageName = context.opPackageName
+        rippleView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
+            override fun onViewDetachedFromWindow(view: View?) {}
+
+            override fun onViewAttachedToWindow(view: View?) {
+                layoutRipple()
+                rippleView.startRipple(Runnable {
+                    mWM.removeView(rippleView)
+                })
+                rippleView.removeOnAttachStateChangeListener(this)
+            }
+        })
+        mWM.addView(rippleView, windowLayoutParams)
+    }
+
+    private fun layoutRipple() {
+        // TODO(shanh): Set origin base on phone orientation.
         val displayMetrics = DisplayMetrics()
         context.display.getRealMetrics(displayMetrics)
         val width = displayMetrics.widthPixels
         val height = displayMetrics.heightPixels
-        if (width != rippleView.width || height != rippleView.height) {
-            rippleView.apply {
-                measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
-                        View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY))
-                layout(0, 0, width, height)
-                origin = PointF(width / 2f, height.toFloat())
-                radius = max(width, height).toFloat()
-            }
-        }
+        rippleView.origin = PointF(width / 2f, height.toFloat())
+        rippleView.radius = Integer.max(width, height).toFloat()
     }
 
     private fun updateRippleColor() {
@@ -134,7 +138,7 @@
 
     inner class ChargingRippleCommand : Command {
         override fun execute(pw: PrintWriter, args: List<String>) {
-            rippleView.startRipple()
+            startRipple()
         }
 
         override fun help(pw: PrintWriter) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
deleted file mode 100644
index 23d13d3..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ /dev/null
@@ -1,490 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_APP_START;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.app.ActivityManager;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.util.MathUtils;
-import android.view.IRemoteAnimationFinishedCallback;
-import android.view.IRemoteAnimationRunner;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationTarget;
-import android.view.SyncRtSurfaceTransactionApplier;
-import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
-
-import com.android.internal.jank.InteractionJankMonitor;
-import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.systemui.Interpolators;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
-
-import java.util.concurrent.Executor;
-
-/**
- * A class that allows activities to be launched in a seamless way where the notification
- * transforms nicely into the starting window.
- */
-public class ActivityLaunchAnimator {
-
-    private static final int ANIMATION_DURATION = 400;
-    public static final long ANIMATION_DURATION_FADE_CONTENT = 67;
-    public static final long ANIMATION_DURATION_FADE_APP = 200;
-    public static final long ANIMATION_DELAY_ICON_FADE_IN = ANIMATION_DURATION -
-            CollapsedStatusBarFragment.FADE_IN_DURATION - CollapsedStatusBarFragment.FADE_IN_DELAY
-            - 16;
-    private static final int ANIMATION_DURATION_NAV_FADE_IN = 266;
-    private static final int ANIMATION_DURATION_NAV_FADE_OUT = 133;
-    private static final long ANIMATION_DELAY_NAV_FADE_IN =
-            ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN;
-    private static final Interpolator NAV_FADE_IN_INTERPOLATOR =
-            new PathInterpolator(0f, 0f, 0f, 1f);
-    private static final Interpolator NAV_FADE_OUT_INTERPOLATOR =
-            new PathInterpolator(0.2f, 0f, 1f, 1f);
-    private static final long LAUNCH_TIMEOUT = 500;
-    private final NotificationPanelViewController mNotificationPanel;
-    private final NotificationListContainer mNotificationContainer;
-    private final float mWindowCornerRadius;
-    private final NotificationShadeWindowViewController mNotificationShadeWindowViewController;
-    private final NotificationShadeDepthController mDepthController;
-    private final Executor mMainExecutor;
-    private Callback mCallback;
-    private final Runnable mTimeoutRunnable = () -> {
-        setAnimationPending(false);
-        mCallback.onExpandAnimationTimedOut();
-    };
-    private boolean mAnimationPending;
-    private boolean mAnimationRunning;
-    private boolean mIsLaunchForActivity;
-
-    public ActivityLaunchAnimator(
-            NotificationShadeWindowViewController notificationShadeWindowViewController,
-            Callback callback,
-            NotificationPanelViewController notificationPanel,
-            NotificationShadeDepthController depthController,
-            NotificationListContainer container,
-            Executor mainExecutor) {
-        mNotificationPanel = notificationPanel;
-        mNotificationContainer = container;
-        mDepthController = depthController;
-        mNotificationShadeWindowViewController = notificationShadeWindowViewController;
-        mCallback = callback;
-        mMainExecutor = mainExecutor;
-        mWindowCornerRadius = ScreenDecorationsUtils
-                .getWindowCornerRadius(mNotificationShadeWindowViewController.getView()
-                        .getResources());
-    }
-
-    public RemoteAnimationAdapter getLaunchAnimation(
-            View sourceView, boolean occluded) {
-        if (!(sourceView instanceof ExpandableNotificationRow)
-                || !mCallback.areLaunchAnimationsEnabled() || occluded) {
-            return null;
-        }
-        AnimationRunner animationRunner = new AnimationRunner(
-                (ExpandableNotificationRow) sourceView);
-        return new RemoteAnimationAdapter(animationRunner, ANIMATION_DURATION,
-                ANIMATION_DURATION - 150 /* statusBarTransitionDelay */);
-    }
-
-    public boolean isAnimationPending() {
-        return mAnimationPending;
-    }
-
-    /**
-     * Set the launch result the intent requested
-     *
-     * @param launchResult the launch result
-     * @param wasIntentActivity was this launch for an activity
-     */
-    public void setLaunchResult(int launchResult, boolean wasIntentActivity) {
-        mIsLaunchForActivity = wasIntentActivity;
-        setAnimationPending((launchResult == ActivityManager.START_TASK_TO_FRONT
-                || launchResult == ActivityManager.START_SUCCESS)
-                        && mCallback.areLaunchAnimationsEnabled());
-    }
-
-    public boolean isLaunchForActivity() {
-        return mIsLaunchForActivity;
-    }
-
-    private void setAnimationPending(boolean pending) {
-        mAnimationPending = pending;
-        mNotificationShadeWindowViewController.setExpandAnimationPending(pending);
-        if (pending) {
-            mNotificationShadeWindowViewController.getView().postDelayed(mTimeoutRunnable,
-                    LAUNCH_TIMEOUT);
-        } else {
-            mNotificationShadeWindowViewController.getView().removeCallbacks(mTimeoutRunnable);
-        }
-    }
-
-    public boolean isAnimationRunning() {
-        return mAnimationRunning;
-    }
-
-    class AnimationRunner extends IRemoteAnimationRunner.Stub {
-
-        private final ExpandableNotificationRow mSourceNotification;
-        private final ExpandAnimationParameters mParams;
-        private final Rect mWindowCrop = new Rect();
-        private boolean mIsFullScreenLaunch = true;
-        private final SyncRtSurfaceTransactionApplier mSyncRtTransactionApplier;
-
-        private final float mNotificationStartTopCornerRadius;
-        private final float mNotificationStartBottomCornerRadius;
-
-        AnimationRunner(ExpandableNotificationRow sourceNotification) {
-            mSourceNotification = sourceNotification;
-            mParams = new ExpandAnimationParameters();
-            mSyncRtTransactionApplier = new SyncRtSurfaceTransactionApplier(mSourceNotification);
-            mNotificationStartTopCornerRadius = mSourceNotification.getCurrentBackgroundRadiusTop();
-            mNotificationStartBottomCornerRadius =
-                    mSourceNotification.getCurrentBackgroundRadiusBottom();
-        }
-
-        @Override
-        public void onAnimationStart(@WindowManager.TransitionOldType int transit,
-                RemoteAnimationTarget[] remoteAnimationTargets,
-                RemoteAnimationTarget[] remoteAnimationWallpaperTargets,
-                RemoteAnimationTarget[] remoteAnimationNonAppTargets,
-                IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
-                    throws RemoteException {
-            mMainExecutor.execute(() -> {
-                RemoteAnimationTarget primary = getPrimaryRemoteAnimationTarget(
-                        remoteAnimationTargets);
-                if (primary == null) {
-                    setAnimationPending(false);
-                    invokeCallback(iRemoteAnimationFinishedCallback);
-                    mNotificationPanel.collapse(false /* delayed */, 1.0f /* speedUpFactor */);
-                    return;
-                }
-
-                setExpandAnimationRunning(true);
-                mIsFullScreenLaunch = primary.position.y == 0
-                        && primary.sourceContainerBounds.height()
-                                >= mNotificationPanel.getHeight();
-                if (!mIsFullScreenLaunch) {
-                    mNotificationPanel.collapseWithDuration(ANIMATION_DURATION);
-                }
-                ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
-                mParams.startPosition = mSourceNotification.getLocationOnScreen();
-                mParams.startTranslationZ = mSourceNotification.getTranslationZ();
-                mParams.startClipTopAmount = mSourceNotification.getClipTopAmount();
-                if (mSourceNotification.isChildInGroup()) {
-                    int parentClip = mSourceNotification
-                            .getNotificationParent().getClipTopAmount();
-                    mParams.parentStartClipTopAmount = parentClip;
-                    // We need to calculate how much the child is clipped by the parent
-                    // because children always have 0 clipTopAmount
-                    if (parentClip != 0) {
-                        float childClip = parentClip
-                                - mSourceNotification.getTranslationY();
-                        if (childClip > 0.0f) {
-                            mParams.startClipTopAmount = (int) Math.ceil(childClip);
-                        }
-                    }
-                }
-                int targetWidth = primary.sourceContainerBounds.width();
-                // If the notification panel is collapsed, the clip may be larger than the height.
-                int notificationHeight = Math.max(mSourceNotification.getActualHeight()
-                        - mSourceNotification.getClipBottomAmount(), 0);
-                int notificationWidth = mSourceNotification.getWidth();
-                final RemoteAnimationTarget navigationBarTarget =
-                        getNavBarRemoteAnimationTarget(remoteAnimationNonAppTargets);
-                anim.setDuration(ANIMATION_DURATION);
-                anim.setInterpolator(Interpolators.LINEAR);
-                anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                    @Override
-                    public void onAnimationUpdate(ValueAnimator animation) {
-                        mParams.linearProgress = animation.getAnimatedFraction();
-                        float progress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
-                                        mParams.linearProgress);
-                        int newWidth = (int) MathUtils.lerp(notificationWidth,
-                                targetWidth, progress);
-                        mParams.left = (int) ((targetWidth - newWidth) / 2.0f);
-                        mParams.right = mParams.left + newWidth;
-                        mParams.top = (int) MathUtils.lerp(mParams.startPosition[1],
-                                primary.position.y, progress);
-                        mParams.bottom = (int) MathUtils.lerp(mParams.startPosition[1]
-                                        + notificationHeight,
-                                primary.position.y + primary.sourceContainerBounds.bottom,
-                                progress);
-                        mParams.topCornerRadius = MathUtils.lerp(mNotificationStartTopCornerRadius,
-                                mWindowCornerRadius, progress);
-                        mParams.bottomCornerRadius = MathUtils.lerp(
-                                mNotificationStartBottomCornerRadius,
-                                mWindowCornerRadius, progress);
-                        applyParamsToWindow(primary);
-                        applyParamsToNotification(mParams);
-                        applyParamsToNotificationShade(mParams);
-                        applyNavigationBarParamsToWindow(navigationBarTarget);
-                    }
-                });
-                anim.addListener(new AnimatorListenerAdapter() {
-                    private boolean mWasCancelled;
-
-                    @Override
-                    public void onAnimationStart(Animator animation) {
-                        InteractionJankMonitor.getInstance().begin(mSourceNotification,
-                                CUJ_NOTIFICATION_APP_START);
-                    }
-
-                    @Override
-                    public void onAnimationCancel(Animator animation) {
-                        mWasCancelled = true;
-                    }
-
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        setExpandAnimationRunning(false);
-                        invokeCallback(iRemoteAnimationFinishedCallback);
-                        if (!mWasCancelled) {
-                            InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_APP_START);
-                        } else {
-                            InteractionJankMonitor.getInstance().cancel(CUJ_NOTIFICATION_APP_START);
-                        }
-                    }
-                });
-                anim.start();
-                setAnimationPending(false);
-            });
-        }
-
-        private void invokeCallback(IRemoteAnimationFinishedCallback callback) {
-            try {
-                callback.onAnimationFinished();
-            } catch (RemoteException e) {
-                e.printStackTrace();
-            }
-        }
-
-        private RemoteAnimationTarget getPrimaryRemoteAnimationTarget(
-                RemoteAnimationTarget[] remoteAnimationTargets) {
-            RemoteAnimationTarget primary = null;
-            for (RemoteAnimationTarget app : remoteAnimationTargets) {
-                if (app.mode == RemoteAnimationTarget.MODE_OPENING) {
-                    primary = app;
-                    break;
-                }
-            }
-            return primary;
-        }
-
-        private RemoteAnimationTarget getNavBarRemoteAnimationTarget(
-                RemoteAnimationTarget[] remoteAnimationTargets) {
-            RemoteAnimationTarget navBar = null;
-            for (RemoteAnimationTarget target : remoteAnimationTargets) {
-                if (target.windowType == TYPE_NAVIGATION_BAR) {
-                    navBar = target;
-                    break;
-                }
-            }
-            return navBar;
-        }
-
-        private void setExpandAnimationRunning(boolean running) {
-            mNotificationPanel.setLaunchingNotification(running);
-            mSourceNotification.setExpandAnimationRunning(running);
-            mNotificationShadeWindowViewController.setExpandAnimationRunning(running);
-            mNotificationContainer.setExpandingNotification(running ? mSourceNotification : null);
-            mAnimationRunning = running;
-            if (!running) {
-                mCallback.onExpandAnimationFinished(mIsFullScreenLaunch);
-                applyParamsToNotification(null);
-                applyParamsToNotificationShade(null);
-            }
-
-        }
-
-        private void applyParamsToNotificationShade(ExpandAnimationParameters params) {
-            mNotificationContainer.applyExpandAnimationParams(params);
-            mNotificationPanel.applyExpandAnimationParams(params);
-            mDepthController.setNotificationLaunchAnimationParams(params);
-        }
-
-        private void applyParamsToNotification(ExpandAnimationParameters params) {
-            mSourceNotification.applyExpandAnimationParams(params);
-        }
-
-        private void applyParamsToWindow(RemoteAnimationTarget app) {
-            Matrix m = new Matrix();
-            m.postTranslate(0, (float) (mParams.top - app.position.y));
-            mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
-            float cornerRadius = Math.min(mParams.topCornerRadius, mParams.bottomCornerRadius);
-            SurfaceParams params = new SurfaceParams.Builder(app.leash)
-                    .withAlpha(1f)
-                    .withMatrix(m)
-                    .withWindowCrop(mWindowCrop)
-                    .withLayer(app.prefixOrderIndex)
-                    .withCornerRadius(cornerRadius)
-                    .withVisibility(true)
-                    .build();
-            mSyncRtTransactionApplier.scheduleApply(params);
-        }
-
-        private void applyNavigationBarParamsToWindow(RemoteAnimationTarget navBarTarget) {
-            if (navBarTarget == null) {
-                return;
-            }
-
-            // calculate navigation bar fade-out progress
-            final float fadeOutProgress = mParams.getProgress(0,
-                    ANIMATION_DURATION_NAV_FADE_OUT);
-
-            // calculate navigation bar fade-in progress
-            final float fadeInProgress = mParams.getProgress(ANIMATION_DELAY_NAV_FADE_IN,
-                    ANIMATION_DURATION_NAV_FADE_OUT);
-
-            final SurfaceParams.Builder builder = new SurfaceParams.Builder(navBarTarget.leash);
-            if (fadeInProgress > 0) {
-                Matrix m = new Matrix();
-                m.postTranslate(0, (float) (mParams.top - navBarTarget.position.y));
-                mWindowCrop.set(mParams.left, 0, mParams.right, mParams.getHeight());
-                builder.withMatrix(m)
-                        .withWindowCrop(mWindowCrop)
-                        .withVisibility(true);
-                builder.withAlpha(NAV_FADE_IN_INTERPOLATOR.getInterpolation(fadeInProgress));
-            } else {
-                builder.withAlpha(1f - NAV_FADE_OUT_INTERPOLATOR.getInterpolation(fadeOutProgress));
-            }
-            mSyncRtTransactionApplier.scheduleApply(builder.build());
-        }
-
-        @Override
-        public void onAnimationCancelled() throws RemoteException {
-            mMainExecutor.execute(() -> {
-                setAnimationPending(false);
-                mCallback.onLaunchAnimationCancelled();
-            });
-        }
-    };
-
-    public static class ExpandAnimationParameters {
-        public float linearProgress;
-        int[] startPosition;
-        float startTranslationZ;
-        int left;
-        int top;
-        int right;
-        int bottom;
-        int startClipTopAmount;
-        int parentStartClipTopAmount;
-        float topCornerRadius;
-        float bottomCornerRadius;
-
-        public ExpandAnimationParameters() {
-        }
-
-        public int getTop() {
-            return top;
-        }
-
-        public int getBottom() {
-            return bottom;
-        }
-
-        public int getWidth() {
-            return right - left;
-        }
-
-        public int getHeight() {
-            return bottom - top;
-        }
-
-        public int getTopChange() {
-            // We need this compensation to ensure that the QS moves in sync.
-            int clipTopAmountCompensation = 0;
-            if (startClipTopAmount != 0.0f) {
-                clipTopAmountCompensation = (int) MathUtils.lerp(0, startClipTopAmount,
-                        Interpolators.FAST_OUT_SLOW_IN.getInterpolation(linearProgress));
-            }
-            return Math.min(top - startPosition[1] - clipTopAmountCompensation, 0);
-        }
-
-        public float getProgress() {
-            return linearProgress;
-        }
-
-        public float getProgress(long delay, long duration) {
-            return MathUtils.constrain((linearProgress * ANIMATION_DURATION - delay)
-                    / duration, 0.0f, 1.0f);
-        }
-
-        public int getStartClipTopAmount() {
-            return startClipTopAmount;
-        }
-
-        public int getParentStartClipTopAmount() {
-            return parentStartClipTopAmount;
-        }
-
-        public float getStartTranslationZ() {
-            return startTranslationZ;
-        }
-
-        public float getTopCornerRadius() {
-            return topCornerRadius;
-        }
-
-        public float getBottomCornerRadius() {
-            return bottomCornerRadius;
-        }
-    }
-
-    public interface Callback {
-
-        /**
-         * Called when the launch animation was cancelled.
-         */
-        void onLaunchAnimationCancelled();
-
-        /**
-         * Called when the launch animation has timed out without starting an actual animation.
-         */
-        void onExpandAnimationTimedOut();
-
-        /**
-         * Called when the expand animation has finished.
-         *
-         * @param launchIsFullScreen True if this launch was fullscreen, such that now the window
-         *                           fills the whole screen
-         */
-        void onExpandAnimationFinished(boolean launchIsFullScreen);
-
-        /**
-         * Are animations currently enabled.
-         */
-        boolean areLaunchAnimationsEnabled();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
new file mode 100644
index 0000000..d5835fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
@@ -0,0 +1,44 @@
+package com.android.systemui.statusbar.notification
+
+import android.util.MathUtils
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.Interpolators
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator
+import kotlin.math.min
+
+/** Parameters for the notifications expand animations. */
+class ExpandAnimationParameters(
+    top: Int,
+    bottom: Int,
+    left: Int,
+    right: Int,
+
+    topCornerRadius: Float = 0f,
+    bottomCornerRadius: Float = 0f
+) : ActivityLaunchAnimator.State(top, bottom, left, right, topCornerRadius, bottomCornerRadius) {
+    @VisibleForTesting
+    constructor() : this(
+        top = 0, bottom = 0, left = 0, right = 0, topCornerRadius = 0f, bottomCornerRadius = 0f
+    )
+
+    var startTranslationZ = 0f
+    var startClipTopAmount = 0
+    var parentStartClipTopAmount = 0
+    var progress = 0f
+    var linearProgress = 0f
+
+    override val topChange: Int
+        get() {
+            // We need this compensation to ensure that the QS moves in sync.
+            var clipTopAmountCompensation = 0
+            if (startClipTopAmount.toFloat() != 0.0f) {
+                clipTopAmountCompensation = MathUtils.lerp(0f, startClipTopAmount.toFloat(),
+                        Interpolators.FAST_OUT_SLOW_IN.getInterpolation(linearProgress)).toInt()
+            }
+            return min(super.topChange - clipTopAmountCompensation, 0)
+        }
+
+    fun getProgress(delay: Long, duration: Long): Float {
+        return ActivityLaunchAnimator.getProgress(linearProgress, delay, duration)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
new file mode 100644
index 0000000..2f966b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -0,0 +1,136 @@
+package com.android.systemui.statusbar.notification
+
+import android.view.View
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.statusbar.NotificationShadeDepthController
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.phone.NotificationPanelViewController
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator
+import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController
+import kotlin.math.ceil
+import kotlin.math.max
+
+/** A provider of [NotificationLaunchAnimatorController]. */
+class NotificationLaunchAnimatorControllerProvider(
+    private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
+    private val notificationPanelViewController: NotificationPanelViewController,
+    private val notificationListContainer: NotificationListContainer,
+    private val depthController: NotificationShadeDepthController
+) {
+    fun getAnimatorController(
+        notification: ExpandableNotificationRow
+    ): NotificationLaunchAnimatorController {
+        return NotificationLaunchAnimatorController(
+            notificationShadeWindowViewController,
+            notificationPanelViewController,
+            notificationListContainer,
+            depthController,
+            notification
+        )
+    }
+}
+
+/**
+ * An [ActivityLaunchAnimator.Controller] that animates an [ExpandableNotificationRow]. An instance
+ * of this class can be passed to [ActivityLaunchAnimator.startIntentWithAnimation] to animate a
+ * notification expanding into an opening window.
+ */
+class NotificationLaunchAnimatorController(
+    private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
+    private val notificationPanelViewController: NotificationPanelViewController,
+    private val notificationListContainer: NotificationListContainer,
+    private val depthController: NotificationShadeDepthController,
+    private val notification: ExpandableNotificationRow
+) : ActivityLaunchAnimator.Controller {
+    override fun getRootView(): View = notification.rootView
+
+    override fun createAnimatorState(): ActivityLaunchAnimator.State {
+        // If the notification panel is collapsed, the clip may be larger than the height.
+        val height = max(0, notification.actualHeight - notification.clipBottomAmount)
+        val location = notification.locationOnScreen
+
+        val params = ExpandAnimationParameters(
+                top = location[1],
+                bottom = location[1] + height,
+                left = location[0],
+                right = location[0] + notification.width,
+                topCornerRadius = notification.currentBackgroundRadiusTop,
+                bottomCornerRadius = notification.currentBackgroundRadiusBottom
+        )
+
+        params.startTranslationZ = notification.translationZ
+        params.startClipTopAmount = notification.clipTopAmount
+        if (notification.isChildInGroup) {
+            val parentClip = notification.notificationParent.clipTopAmount
+            params.parentStartClipTopAmount = parentClip
+
+            // We need to calculate how much the child is clipped by the parent because children
+            // always have 0 clipTopAmount
+            if (parentClip != 0) {
+                val childClip = parentClip - notification.translationY
+                if (childClip > 0) {
+                    params.startClipTopAmount = ceil(childClip.toDouble()).toInt()
+                }
+            }
+        }
+
+        return params
+    }
+
+    override fun onIntentStarted(willAnimate: Boolean) {
+        notificationShadeWindowViewController.setExpandAnimationRunning(willAnimate)
+    }
+
+    override fun onLaunchAnimationCancelled() {
+        // TODO(b/184121838): Should we call InteractionJankMonitor.cancel if the animation started
+        // here?
+        notificationShadeWindowViewController.setExpandAnimationRunning(false)
+    }
+
+    override fun onLaunchAnimationTimedOut() {
+        notificationShadeWindowViewController.setExpandAnimationRunning(false)
+    }
+
+    override fun onLaunchAnimationAborted() {
+        notificationShadeWindowViewController.setExpandAnimationRunning(false)
+    }
+
+    override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
+        notificationPanelViewController.setLaunchingNotification(true)
+        notification.isExpandAnimationRunning = true
+        notificationListContainer.setExpandingNotification(notification)
+
+        InteractionJankMonitor.getInstance().begin(notification,
+            InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
+    }
+
+    override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+        InteractionJankMonitor.getInstance().end(InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
+
+        notificationPanelViewController.setLaunchingNotification(false)
+        notification.isExpandAnimationRunning = false
+        notificationShadeWindowViewController.setExpandAnimationRunning(false)
+        notificationListContainer.setExpandingNotification(null)
+        applyParams(null)
+    }
+
+    private fun applyParams(params: ExpandAnimationParameters?) {
+        notification.applyExpandAnimationParams(params)
+        notificationListContainer.applyExpandAnimationParams(params)
+        notificationPanelViewController.applyExpandAnimationParams(params)
+        depthController.notificationLaunchAnimationParams = params
+    }
+
+    override fun onLaunchAnimationProgress(
+        state: ActivityLaunchAnimator.State,
+        progress: Float,
+        linearProgress: Float
+    ) {
+        val params = state as ExpandAnimationParameters
+        params.progress = progress
+        params.linearProgress = linearProgress
+
+        applyParams(params)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index a3a4014..207a894 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -19,7 +19,6 @@
 import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
 
@@ -79,6 +78,7 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -86,7 +86,7 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -2045,9 +2045,8 @@
         float extraWidthForClipping = params.getWidth() - getWidth();
         setExtraWidthForClipping(extraWidthForClipping);
         int top = params.getTop();
-        float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress());
         int startClipTopAmount = params.getStartClipTopAmount();
-        int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, interpolation);
+        int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, params.getProgress());
         if (mNotificationParent != null) {
             float parentY = mNotificationParent.getTranslationY();
             top -= parentY;
@@ -2096,7 +2095,7 @@
         if (expandAnimationRunning) {
             contentView.animate()
                     .alpha(0f)
-                    .setDuration(ActivityLaunchAnimator.ANIMATION_DURATION_FADE_CONTENT)
+                    .setDuration(ActivityLaunchAnimator.ANIMATION_DURATION_FADE_OUT_CONTENT)
                     .setInterpolator(Interpolators.ALPHA_OUT);
             setAboveShelf(true);
             mExpandAnimationRunning = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 9588563..07d1e68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -31,7 +31,8 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
 
 /**
  * A view that can be used for both the dimmed and normal background of an notification.
@@ -277,13 +278,14 @@
         invalidate();
     }
 
-    public void setExpandAnimationParams(ActivityLaunchAnimator.ExpandAnimationParameters params) {
+    /** Set the current expand animation parameters. */
+    public void setExpandAnimationParams(ExpandAnimationParameters params) {
         mActualHeight = params.getHeight();
         mActualWidth = params.getWidth();
         float alphaProgress = Interpolators.ALPHA_IN.getInterpolation(
                 params.getProgress(
-                        ActivityLaunchAnimator.ANIMATION_DURATION_FADE_CONTENT /* delay */,
-                        ActivityLaunchAnimator.ANIMATION_DURATION_FADE_APP /* duration */));
+                        ActivityLaunchAnimator.ANIMATION_DURATION_FADE_OUT_CONTENT /* delay */,
+                        ActivityLaunchAnimator.ANIMATION_DURATION_FADE_IN_WINDOW /* duration */));
         mBackground.setAlpha((int) (mDrawableAlpha * (1.0f - alphaProgress)));
         invalidate();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index 72f3216..2a2e733f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -16,14 +16,13 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
-
 import android.view.View;
 import android.view.ViewGroup;
 
 import androidx.annotation.Nullable;
 
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
+import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 40c0b89..ad06e7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.notification.stack;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT;
 import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
@@ -91,6 +90,7 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
 import com.android.systemui.statusbar.notification.FakeShadowView;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationUtils;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 7baad1c..ce7b397 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -75,8 +75,8 @@
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
 import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -1497,8 +1497,7 @@
         }
 
         @Override
-        public void applyExpandAnimationParams(
-                ActivityLaunchAnimator.ExpandAnimationParameters params) {
+        public void applyExpandAnimationParams(ExpandAnimationParameters params) {
             mView.applyExpandAnimationParams(params);
         }
 
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 555df5c..364b532 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -26,7 +26,6 @@
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
 
 import static java.lang.Float.isNaN;
@@ -100,6 +99,7 @@
 import com.android.systemui.media.MediaDataManager;
 import com.android.systemui.media.MediaHierarchyManager;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -111,16 +111,17 @@
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.ConversationNotificationManager;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -178,6 +179,10 @@
      * Fling until QS is completely hidden.
      */
     private static final int FLING_HIDE = 2;
+    private static final long ANIMATION_DELAY_ICON_FADE_IN =
+            ActivityLaunchAnimator.ANIMATION_DURATION - CollapsedStatusBarFragment.FADE_IN_DURATION
+                    - CollapsedStatusBarFragment.FADE_IN_DELAY - 16;
+
     private final DozeParameters mDozeParameters;
     private final OnHeightChangedListener mOnHeightChangedListener = new OnHeightChangedListener();
     private final OnClickListener mOnClickListener = new OnClickListener();
@@ -477,6 +482,7 @@
     private final UserManager mUserManager;
     private final ShadeController mShadeController;
     private final MediaDataManager mMediaDataManager;
+    private NotificationShadeDepthController mDepthController;
     private int mDisplayId;
 
     /**
@@ -576,6 +582,7 @@
             ScrimController scrimController,
             UserManager userManager,
             MediaDataManager mediaDataManager,
+            NotificationShadeDepthController notificationShadeDepthController,
             AmbientState ambientState,
             FeatureFlags featureFlags) {
         super(view, falsingManager, dozeLog, keyguardStateController,
@@ -594,6 +601,7 @@
         mNotificationIconAreaController = notificationIconAreaController;
         mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
         mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
+        mDepthController = notificationShadeDepthController;
         mFeatureFlags = featureFlags;
         mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
         mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory;
@@ -603,7 +611,6 @@
         mKeyguardQsUserSwitchEnabled =
                 mKeyguardUserSwitcherEnabled && mResources.getBoolean(
                         R.bool.config_keyguard_user_switch_opens_qs_details);
-        keyguardUpdateMonitor.setKeyguardQsUserSwitchEnabled(mKeyguardQsUserSwitchEnabled);
         mShouldUseSplitNotificationShade =
                 Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources);
         mView.setWillNotDraw(!DEBUG);
@@ -1844,6 +1851,10 @@
             mPulseExpansionHandler.setQsExpanded(expanded);
             mKeyguardBypassController.setQSExpanded(expanded);
             mStatusBarKeyguardViewManager.setQsExpanded(expanded);
+
+            if (mDisabledUdfpsController != null) {
+                mDisabledUdfpsController.setQsExpanded(expanded);
+            }
         }
     }
 
@@ -1991,8 +2002,21 @@
         float qsExpansionFraction = getQsExpansionFraction();
         mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
         mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
-        mScrimController.setQsExpansion(qsExpansionFraction);
+        mScrimController.setQsPosition(qsExpansionFraction,
+                calculateQsBottomPosition(qsExpansionFraction));
         mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
+        mDepthController.setQsPanelExpansion(qsExpansionFraction);
+    }
+
+    private int calculateQsBottomPosition(float qsExpansionFraction) {
+        int qsBottomY = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight();
+        if (qsExpansionFraction != 0.0) {
+            qsBottomY = (int) MathUtils.lerp(
+                    qsBottomY, mQs.getDesiredHeight(), qsExpansionFraction);
+        }
+        // to account for shade overshooting animation, see setSectionPadding method
+        if (mSectionPadding > 0) qsBottomY += mSectionPadding;
+        return qsBottomY;
     }
 
     private String determineAccessibilityPaneTitle() {
@@ -3208,8 +3232,7 @@
             return;
         }
 
-        boolean hideIcons = params.getProgress(
-                ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
+        boolean hideIcons = params.getProgress(ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
         if (hideIcons != mHideIconsDuringNotificationLaunch) {
             mHideIconsDuringNotificationLaunch = hideIcons;
             if (!hideIcons) {
@@ -3544,7 +3567,8 @@
                     mUpdateMonitor,
                     mAuthController,
                     mStatusBarKeyguardViewManager,
-                    mKeyguardStateController);
+                    mKeyguardStateController,
+                    mFalsingManager);
             mDisabledUdfpsController.init();
         } else if (mDisabledUdfpsController != null && !udfpsEnrolled) {
             mDisabledUdfpsController.destroy();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index d074e64..5aecb72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -527,6 +527,11 @@
     }
 
     @Override
+    public boolean isLaunchingActivity() {
+        return mCurrentState.mLaunchingActivity;
+    }
+
+    @Override
     public void setScrimsVisibility(int scrimsVisibility) {
         mCurrentState.mScrimsVisibility = scrimsVisibility;
         apply(mCurrentState);
@@ -606,12 +611,21 @@
         apply(mCurrentState);
     }
 
+    private final Set<Object> mForceOpenTokens = new HashSet<>();
     @Override
-    public void setForcePluginOpen(boolean forcePluginOpen) {
-        mCurrentState.mForcePluginOpen = forcePluginOpen;
-        apply(mCurrentState);
-        if (mForcePluginOpenListener != null) {
-            mForcePluginOpenListener.onChange(forcePluginOpen);
+    public void setForcePluginOpen(boolean forceOpen, Object token) {
+        if (forceOpen) {
+            mForceOpenTokens.add(token);
+        } else {
+            mForceOpenTokens.remove(token);
+        }
+        final boolean previousForceOpenState = mCurrentState.mForcePluginOpen;
+        mCurrentState.mForcePluginOpen = !mForceOpenTokens.isEmpty();
+        if (previousForceOpenState != mCurrentState.mForcePluginOpen) {
+            apply(mCurrentState);
+            if (mForcePluginOpenListener != null) {
+                mForcePluginOpenListener.onChange(mCurrentState.mForcePluginOpen);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 2ff7c99..72f7ff8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -92,7 +92,6 @@
     private View mBrightnessMirror;
     private boolean mTouchActive;
     private boolean mTouchCancelled;
-    private boolean mExpandAnimationPending;
     private boolean mExpandAnimationRunning;
     private NotificationStackScrollLayout mStackScrollLayout;
     private PhoneStatusBarView mStatusBarView;
@@ -235,7 +234,7 @@
                         || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
                     setTouchActive(false);
                 }
-                if (mTouchCancelled || mExpandAnimationRunning || mExpandAnimationPending) {
+                if (mTouchCancelled || mExpandAnimationRunning) {
                     return false;
                 }
                 mFalsingCollector.onTouchEvent(ev);
@@ -435,8 +434,6 @@
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.print("  mExpandAnimationPending=");
-        pw.println(mExpandAnimationPending);
         pw.print("  mExpandAnimationRunning=");
         pw.println(mExpandAnimationRunning);
         pw.print("  mTouchCancelled=");
@@ -445,19 +442,10 @@
         pw.println(mTouchActive);
     }
 
-    public void setExpandAnimationPending(boolean pending) {
-        if (mExpandAnimationPending != pending) {
-            mExpandAnimationPending = pending;
-            mNotificationShadeWindowController
-                    .setLaunchingActivity(mExpandAnimationPending | mExpandAnimationRunning);
-        }
-    }
-
     public void setExpandAnimationRunning(boolean running) {
         if (mExpandAnimationRunning != running) {
             mExpandAnimationRunning = running;
-            mNotificationShadeWindowController
-                    .setLaunchingActivity(mExpandAnimationPending | mExpandAnimationRunning);
+            mNotificationShadeWindowController.setLaunchingActivity(mExpandAnimationRunning);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 270a0f8..681f450 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -24,7 +24,6 @@
 import android.annotation.IntDef;
 import android.app.AlarmManager;
 import android.graphics.Color;
-import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Trace;
 import android.util.Log;
@@ -49,7 +48,6 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dock.DockManager;
-import com.android.systemui.statusbar.BlurUtils;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -142,6 +140,7 @@
     private ScrimState mState = ScrimState.UNINITIALIZED;
 
     private ScrimView mScrimInFront;
+    private ScrimView mNotificationsScrim;
     private ScrimView mScrimBehind;
     @Nullable
     private ScrimView mScrimForBubble;
@@ -156,7 +155,6 @@
     private final KeyguardVisibilityCallback mKeyguardVisibilityCallback;
     private final Handler mHandler;
     private final Executor mMainExecutor;
-    private final BlurUtils mBlurUtils;
 
     private GradientColors mColors;
     private boolean mNeedsDrawableColorUpdate;
@@ -180,6 +178,7 @@
 
     private float mInFrontAlpha = NOT_INITIALIZED;
     private float mBehindAlpha = NOT_INITIALIZED;
+    private float mNotificationsAlpha = NOT_INITIALIZED;
     private float mBubbleAlpha = NOT_INITIALIZED;
 
     private int mInFrontTint;
@@ -209,12 +208,11 @@
             AlarmManager alarmManager, KeyguardStateController keyguardStateController,
             DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler,
             KeyguardUpdateMonitor keyguardUpdateMonitor, DockManager dockManager,
-            BlurUtils blurUtils, ConfigurationController configurationController,
+            ConfigurationController configurationController,
             FeatureFlags featureFlags, @Main Executor mainExecutor) {
         mScrimStateListener = lightBarController::setScrimState;
         mDefaultScrimAlpha = featureFlags.isShadeOpaque() ? BUSY_SCRIM_ALPHA : GAR_SCRIM_ALPHA;
         ScrimState.BUBBLE_EXPANDED.setBubbleAlpha(BUBBLE_SCRIM_ALPHA);
-        mBlurUtils = blurUtils;
 
         mKeyguardStateController = keyguardStateController;
         mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen();
@@ -259,13 +257,16 @@
     /**
      * Attach the controller to the supplied views.
      */
-    public void attachViews(
-            ScrimView scrimBehind, ScrimView scrimInFront, @Nullable ScrimView scrimForBubble) {
-        mScrimBehind = scrimBehind;
+    public void attachViews(ScrimView behindScrim, ScrimView notificationsScrim,
+                            ScrimView scrimInFront, @Nullable ScrimView scrimForBubble) {
+        mNotificationsScrim = notificationsScrim;
+        mScrimBehind = behindScrim;
         mScrimInFront = scrimInFront;
         mScrimForBubble = scrimForBubble;
         updateThemeColors();
 
+        mNotificationsScrim.enableRoundedCorners();
+
         if (mScrimBehindChangeRunnable != null) {
             mScrimBehind.setChangeRunnable(mScrimBehindChangeRunnable, mMainExecutor);
             mScrimBehindChangeRunnable = null;
@@ -280,6 +281,7 @@
         }
 
         mScrimBehind.setDefaultFocusHighlightEnabled(false);
+        mNotificationsScrim.setDefaultFocusHighlightEnabled(false);
         mScrimInFront.setDefaultFocusHighlightEnabled(false);
         if (mScrimForBubble != null) {
             mScrimForBubble.setDefaultFocusHighlightEnabled(false);
@@ -344,6 +346,7 @@
         // We need to disable focus otherwise AOD would end up with a gray overlay.
         mScrimInFront.setFocusable(!state.isLowPowerState());
         mScrimBehind.setFocusable(!state.isLowPowerState());
+        mNotificationsScrim.setFocusable(!state.isLowPowerState());
 
         // Cancel blanking transitions that were pending before we requested a new state
         if (mPendingFrameCallback != null) {
@@ -484,18 +487,20 @@
     }
 
     /**
-     * Current state of the QuickSettings expansion when pulling it from the top.
+     * Current state of the QuickSettings when pulling it from the top.
      *
-     * @param fraction From 0 to 1 where 0 means collapsed and 1 expanded.
+     * @param expansionFraction From 0 to 1 where 0 means collapsed and 1 expanded.
+     * @param qsPanelBottomY absolute Y position of qs panel bottom
      */
-    public void setQsExpansion(float fraction) {
-        if (isNaN(fraction)) {
+    public void setQsPosition(float expansionFraction, int qsPanelBottomY) {
+        if (isNaN(expansionFraction)) {
             return;
         }
-        if (mQsExpansion != fraction) {
-            mQsExpansion = fraction;
+        shiftNotificationsScrim(qsPanelBottomY);
+        updateNotificationsScrimAlpha(qsPanelBottomY);
+        if (mQsExpansion != expansionFraction) {
+            mQsExpansion = expansionFraction;
             Log.d(TAG, "set qs fraction");
-
             boolean relevantState = (mState == ScrimState.SHADE_LOCKED
                     || mState == ScrimState.KEYGUARD
                     || mState == ScrimState.PULSING
@@ -507,6 +512,32 @@
         }
     }
 
+    private void shiftNotificationsScrim(int qsPanelBottomY) {
+        if (qsPanelBottomY > 0) {
+            mNotificationsScrim.setTranslationY(qsPanelBottomY);
+        } else {
+            mNotificationsScrim.setTranslationY(0);
+        }
+    }
+
+    private void updateNotificationsScrimAlpha(int qsPanelBottomY) {
+        float newAlpha = 0;
+        if (qsPanelBottomY > 0) {
+            float interpolator = 0;
+            if (mState == ScrimState.UNLOCKED || mState == ScrimState.SHADE_LOCKED) {
+                interpolator = getInterpolatedFraction();
+            } else {
+                interpolator = mQsExpansion;
+            }
+            newAlpha = MathUtils.lerp(0, 1, interpolator);
+        }
+        if (newAlpha != mNotificationsAlpha) {
+            mNotificationsAlpha = newAlpha;
+            // update alpha without animating
+            mNotificationsScrim.setViewAlpha(newAlpha);
+        }
+    }
+
     private void setOrAdaptCurrentAnimation(@Nullable View scrim) {
         if (scrim == null) {
             return;
@@ -575,6 +606,7 @@
             return;
         }
         setOrAdaptCurrentAnimation(mScrimBehind);
+        setOrAdaptCurrentAnimation(mNotificationsScrim);
         setOrAdaptCurrentAnimation(mScrimInFront);
         setOrAdaptCurrentAnimation(mScrimForBubble);
         dispatchScrimState(mScrimBehind.getViewAlpha());
@@ -591,14 +623,6 @@
     }
 
     /**
-     * Sets the given drawable as the background of the scrim that shows up behind the
-     * notifications.
-     */
-    public void setScrimBehindDrawable(Drawable drawable) {
-        mScrimBehind.setDrawable(drawable);
-    }
-
-    /**
      * Sets the front scrim opacity in AOD so it's not as bright.
      * <p>
      * Displays usually don't support multiple dimming settings when in low power mode.
@@ -667,10 +691,13 @@
             mNeedsDrawableColorUpdate = false;
             // Only animate scrim color if the scrim view is actually visible
             boolean animateScrimInFront = mScrimInFront.getViewAlpha() != 0 && !mBlankScreen;
-            boolean animateScrimBehind = mScrimBehind.getViewAlpha() != 0 && !mBlankScreen;
+            boolean animateBehindScrim = mScrimBehind.getViewAlpha() != 0 && !mBlankScreen;
+            boolean animateScrimNotifications = mNotificationsScrim.getViewAlpha() != 0
+                    && !mBlankScreen;
 
             mScrimInFront.setColors(mColors, animateScrimInFront);
-            mScrimBehind.setColors(mColors, animateScrimBehind);
+            mScrimBehind.setColors(mColors, animateScrimNotifications);
+            mNotificationsScrim.setColors(mColors, animateScrimNotifications);
 
             dispatchScrimState(mScrimBehind.getViewAlpha());
         }
@@ -687,6 +714,7 @@
         }
         setScrimAlpha(mScrimInFront, mInFrontAlpha);
         setScrimAlpha(mScrimBehind, mBehindAlpha);
+        setScrimAlpha(mNotificationsScrim, mNotificationsAlpha);
 
         if (mScrimForBubble != null) {
             boolean animateScrimForBubble = mScrimForBubble.getViewAlpha() != 0 && !mBlankScreen;
@@ -744,7 +772,9 @@
         if (scrim == mScrimInFront) {
             return "front_scrim";
         } else if (scrim == mScrimBehind) {
-            return "back_scrim";
+            return "behind_scrim";
+        } else if (scrim == mNotificationsScrim) {
+            return "notifications_scrim";
         } else if (scrim == mScrimForBubble) {
             return "bubble_scrim";
         }
@@ -817,6 +847,8 @@
             return mInFrontAlpha;
         } else if (scrim == mScrimBehind) {
             return mBehindAlpha;
+        } else if (scrim == mNotificationsScrim) {
+            return mNotificationsAlpha;
         } else if (scrim == mScrimForBubble) {
             return mBubbleAlpha;
         } else {
@@ -829,6 +861,8 @@
             return mInFrontTint;
         } else if (scrim == mScrimBehind) {
             return mBehindTint;
+        } else if (scrim == mNotificationsScrim) {
+            return Color.TRANSPARENT;
         } else if (scrim == mScrimForBubble) {
             return mBubbleTint;
         } else {
@@ -858,8 +892,9 @@
 
         }
         if (isAnimating(mScrimBehind)
-            || isAnimating(mScrimInFront)
-            || isAnimating(mScrimForBubble)) {
+                || isAnimating(mNotificationsScrim)
+                || isAnimating(mScrimInFront)
+                || isAnimating(mScrimForBubble)) {
             if (callback != null && callback != mCallback) {
                 // Since we only notify the callback that we're finished once everything has
                 // finished, we need to make sure that any changing callbacks are also invoked
@@ -884,7 +919,7 @@
         // At the end of the animation we need to remove the tint.
         if (mState == ScrimState.UNLOCKED) {
             mInFrontTint = Color.TRANSPARENT;
-            mBehindTint = Color.TRANSPARENT;
+            mBehindTint = mState.getBehindTint();
             mBubbleTint = Color.TRANSPARENT;
             updateScrimColor(mScrimInFront, mInFrontAlpha, mInFrontTint);
             updateScrimColor(mScrimBehind, mBehindAlpha, mBehindTint);
@@ -996,12 +1031,6 @@
         mScrimBehind.postOnAnimationDelayed(callback, 32 /* delayMillis */);
     }
 
-    public int getBackgroundColor() {
-        int color = mColors.getMainColor();
-        return Color.argb((int) (mScrimBehind.getViewAlpha() * Color.alpha(color)),
-                Color.red(color), Color.green(color), Color.blue(color));
-    }
-
     public void setScrimBehindChangeRunnable(Runnable changeRunnable) {
         // TODO: remove this. This is necessary because of an order-of-operations limitation.
         // The fix is to move more of these class into @StatusBarScope
@@ -1047,7 +1076,7 @@
         pw.print(" tint=0x");
         pw.println(Integer.toHexString(mScrimInFront.getTint()));
 
-        pw.print("  backScrim:");
+        pw.print("  behindScrim:");
         pw.print(" viewAlpha=");
         pw.print(mScrimBehind.getViewAlpha());
         pw.print(" alpha=");
@@ -1055,6 +1084,14 @@
         pw.print(" tint=0x");
         pw.println(Integer.toHexString(mScrimBehind.getTint()));
 
+        pw.print("  notificationsScrim:");
+        pw.print(" viewAlpha=");
+        pw.print(mNotificationsScrim.getViewAlpha());
+        pw.print(" alpha=");
+        pw.print(mNotificationsAlpha);
+        pw.print(" tint=0x");
+        pw.println(Integer.toHexString(mNotificationsScrim.getTint()));
+
         pw.print("  bubbleScrim:");
         pw.print(" viewAlpha=");
         pw.print(mScrimForBubble.getViewAlpha());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index b82863e..a9774d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -129,6 +129,13 @@
             mBehindAlpha = mDefaultScrimAlpha;
             mBubbleAlpha = 0f;
             mFrontAlpha = 0f;
+            mBehindTint = Color.BLACK;
+        }
+
+        // to make sure correct color is returned before "prepare" is called
+        @Override
+        public int getBehindTint() {
+            return Color.BLACK;
         }
     },
 
@@ -228,7 +235,7 @@
             mAnimateChange = !mLaunchingAffordanceWithPreview;
 
             mFrontTint = Color.TRANSPARENT;
-            mBehindTint = Color.TRANSPARENT;
+            mBehindTint = Color.BLACK;
             mBubbleTint = Color.TRANSPARENT;
             mBlankScreen = false;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 8ed9cd6..f1f34dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -173,6 +173,7 @@
 import com.android.systemui.plugins.OverlayPlugin;
 import com.android.systemui.plugins.PluginDependencyProvider;
 import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -206,11 +207,10 @@
 import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.charging.ChargingRippleView;
 import com.android.systemui.statusbar.charging.WiredChargingRippleController;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -263,7 +263,7 @@
         ActivityStarter, KeyguardStateController.Callback,
         OnHeadsUpChangedListener, CommandQueue.Callbacks,
         ColorExtractor.OnColorsChangedListener, ConfigurationListener,
-        StatusBarStateController.StateListener, ActivityLaunchAnimator.Callback,
+        StatusBarStateController.StateListener,
         LifecycleOwner, BatteryController.BatteryStateChangeCallback {
     public static final boolean MULTIUSER_DEBUG = false;
 
@@ -385,7 +385,6 @@
     private boolean mWakeUpComingFromTouch;
     private PointF mWakeUpTouchLocation;
     private LightRevealScrim mLightRevealScrim;
-    private ChargingRippleView mChargingRipple;
     private WiredChargingRippleController mChargingRippleAnimationController;
     private PowerButtonReveal mPowerButtonReveal;
     private CircleReveal mCircleReveal;
@@ -692,6 +691,7 @@
     private boolean mVibrateOnOpening;
     private final VibratorHelper mVibratorHelper;
     private ActivityLaunchAnimator mActivityLaunchAnimator;
+    private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
     protected StatusBarNotificationPresenter mPresenter;
     private NotificationActivityStarter mNotificationActivityStarter;
     private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
@@ -1058,7 +1058,7 @@
                         mMainThreadHandler.post(() -> {
                             mOverlays.remove(plugin);
                             mNotificationShadeWindowController
-                                    .setForcePluginOpen(mOverlays.size() != 0);
+                                    .setForcePluginOpen(mOverlays.size() != 0, this);
                         });
                     }
 
@@ -1081,7 +1081,7 @@
                                         .setStateListener(b -> mOverlays.forEach(
                                                 o -> o.setCollapseDesired(b)));
                                 mNotificationShadeWindowController
-                                        .setForcePluginOpen(mOverlays.size() != 0);
+                                        .setForcePluginOpen(mOverlays.size() != 0, this);
                             });
                         }
                     }
@@ -1215,6 +1215,8 @@
         });
 
         ScrimView scrimBehind = mNotificationShadeWindowView.findViewById(R.id.scrim_behind);
+        ScrimView notificationsScrim = mNotificationShadeWindowView
+                .findViewById(R.id.scrim_notifications);
         ScrimView scrimInFront = mNotificationShadeWindowView.findViewById(R.id.scrim_in_front);
         ScrimView scrimForBubble = mBubblesManagerOptional.isPresent()
                 ? mBubblesManagerOptional.get().getScrimForBubble() : null;
@@ -1223,10 +1225,9 @@
             mNotificationShadeWindowController.setScrimsVisibility(scrimsVisible);
             mLockscreenLockIconController.onScrimVisibilityChanged(scrimsVisible);
         });
-        mScrimController.attachViews(scrimBehind, scrimInFront, scrimForBubble);
+        mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront, scrimForBubble);
 
         mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
-        mChargingRippleAnimationController.setViewHost(mNotificationShadeWindowView);
         updateLightRevealScrimVisibility();
 
         mNotificationPanelViewController.initDependencies(
@@ -1370,16 +1371,18 @@
 
     private void setUpPresenter() {
         // Set up the initial notification state.
-        mActivityLaunchAnimator = new ActivityLaunchAnimator(
-                mNotificationShadeWindowViewController, this, mNotificationPanelViewController,
-                mNotificationShadeDepthControllerLazy.get(),
+        mActivityLaunchAnimator = new ActivityLaunchAnimator();
+        mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
+                mNotificationShadeWindowViewController,
+                mNotificationPanelViewController,
                 mStackScrollerController.getNotificationListContainer(),
-                mContext.getMainExecutor());
+                mNotificationShadeDepthControllerLazy.get()
+        );
 
         // TODO: inject this.
         mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
                 mHeadsUpManager, mNotificationShadeWindowView, mStackScrollerController,
-                mDozeScrimController, mScrimController, mActivityLaunchAnimator,
+                mDozeScrimController, mScrimController, mNotificationShadeWindowController,
                 mDynamicPrivacyController, mKeyguardStateController,
                 mKeyguardIndicationController,
                 this /* statusBar */, mShadeController, mCommandQueue, mInitController,
@@ -1392,6 +1395,7 @@
                 mStatusBarNotificationActivityStarterBuilder
                         .setStatusBar(this)
                         .setActivityLaunchAnimator(mActivityLaunchAnimator)
+                        .setNotificationAnimatorControllerProvider(mNotificationAnimationProvider)
                         .setNotificationPresenter(mPresenter)
                         .setNotificationPanelViewController(mNotificationPanelViewController)
                         .build();
@@ -1519,6 +1523,7 @@
         mNotificationPanelViewController = statusBarComponent.getNotificationPanelViewController();
         mLockscreenLockIconController = statusBarComponent.getLockscreenLockIconController();
         mLockscreenLockIconController.init();
+        statusBarComponent.getAuthRippleController().init();
 
         mNotificationPanelViewController.setLaunchAffordanceListener(
                 mLockscreenLockIconController::onShowingLaunchAffordanceChanged);
@@ -1798,7 +1803,8 @@
     @Override
     public void startActivity(Intent intent, boolean dismissShade, Callback callback) {
         startActivityDismissingKeyguard(intent, false, dismissShade,
-                false /* disallowEnterPictureInPictureWhileLaunching */, callback, 0);
+                false /* disallowEnterPictureInPictureWhileLaunching */, callback, 0,
+                null /* animationController */);
     }
 
     public void setQsExpanded(boolean expanded) {
@@ -1990,16 +1996,16 @@
         return mHeadsUpAppearanceController.shouldBeVisible();
     }
 
+    /** A launch animation was cancelled. */
     //TODO: These can / should probably be moved to NotificationPresenter or ShadeController
-    @Override
     public void onLaunchAnimationCancelled() {
         if (!mPresenter.isCollapsing()) {
             onClosingFinished();
         }
     }
 
-    @Override
-    public void onExpandAnimationFinished(boolean launchIsFullScreen) {
+    /** A launch animation ended. */
+    public void onLaunchAnimationEnd(boolean launchIsFullScreen) {
         if (!mPresenter.isCollapsing()) {
             onClosingFinished();
         }
@@ -2008,20 +2014,20 @@
         }
     }
 
-    @Override
-    public void onExpandAnimationTimedOut() {
+    /** A launch animation timed out. */
+    public void onLaunchAnimationTimedOut(boolean isLaunchForActivity) {
         if (mPresenter.isPresenterFullyCollapsed() && !mPresenter.isCollapsing()
-                && mActivityLaunchAnimator != null
-                && !mActivityLaunchAnimator.isLaunchForActivity()) {
+                && isLaunchForActivity) {
             onClosingFinished();
         } else {
             mShadeController.collapsePanel(true /* animate */);
         }
     }
 
-    @Override
+    /** Whether we should animate an activity launch. */
     public boolean areLaunchAnimationsEnabled() {
-        return mState == StatusBarState.SHADE;
+        // TODO(b/184121838): Support lock screen launch animations.
+        return mState == StatusBarState.SHADE && !isOccluded();
     }
 
     public boolean isDeviceInVrMode() {
@@ -2725,7 +2731,7 @@
             boolean dismissShade, int flags) {
         startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade,
                 false /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */,
-                flags);
+                flags, null /* animationController */);
     }
 
     public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
@@ -2733,55 +2739,75 @@
         startActivityDismissingKeyguard(intent, onlyProvisioned, dismissShade, 0);
     }
 
-    public void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
+    private void startActivityDismissingKeyguard(final Intent intent, boolean onlyProvisioned,
             final boolean dismissShade, final boolean disallowEnterPictureInPictureWhileLaunching,
-            final Callback callback, int flags) {
+            final Callback callback, int flags,
+            @Nullable ActivityLaunchAnimator.Controller animationController) {
         if (onlyProvisioned && !mDeviceProvisionedController.isDeviceProvisioned()) return;
 
         final boolean afterKeyguardGone = mActivityIntentHelper.wouldLaunchResolverActivity(
                 intent, mLockscreenUserManager.getCurrentUserId());
+
+        ActivityLaunchAnimator.Controller animController = null;
+        if (animationController != null && areLaunchAnimationsEnabled()) {
+            animController = dismissShade ? new StatusBarLaunchAnimatorController(
+                    animationController, this, true /* isLaunchForActivity */)
+                    : animationController;
+        }
+        final ActivityLaunchAnimator.Controller animCallbackForLambda = animController;
+
+        // If we animate, we will dismiss the shade only once the animation is done. This is taken
+        // care of by the StatusBarLaunchAnimationController.
+        boolean dismissShadeDirectly = dismissShade && animController == null;
+
         Runnable runnable = () -> {
             mAssistManagerLazy.get().hideAssist();
             intent.setFlags(
                     Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
             intent.addFlags(flags);
-            int result = ActivityManager.START_CANCELED;
-            ActivityOptions options = new ActivityOptions(getActivityOptions(mDisplayId,
-                    null /* remoteAnimation */));
-            options.setDisallowEnterPictureInPictureWhileLaunching(
-                    disallowEnterPictureInPictureWhileLaunching);
-            if (CameraIntents.isInsecureCameraIntent(intent)) {
-                // Normally an activity will set it's requested rotation
-                // animation on its window. However when launching an activity
-                // causes the orientation to change this is too late. In these cases
-                // the default animation is used. This doesn't look good for
-                // the camera (as it rotates the camera contents out of sync
-                // with physical reality). So, we ask the WindowManager to
-                // force the crossfade animation if an orientation change
-                // happens to occur during the launch.
-                options.setRotationAnimationHint(
-                        WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
-            }
-            if (intent.getAction() == Settings.Panel.ACTION_VOLUME) {
-                // Settings Panel is implemented as activity(not a dialog), so
-                // underlying app is paused and may enter picture-in-picture mode
-                // as a result.
-                // So we need to disable picture-in-picture mode here
-                // if it is volume panel.
-                options.setDisallowEnterPictureInPictureWhileLaunching(true);
-            }
-            try {
-                result = ActivityTaskManager.getService().startActivityAsUser(
-                        null, mContext.getBasePackageName(), mContext.getAttributionTag(),
-                        intent,
-                        intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                        null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
-                        options.toBundle(), UserHandle.CURRENT.getIdentifier());
-            } catch (RemoteException e) {
-                Log.w(TAG, "Unable to start activity", e);
-            }
+            int[] result = new int[] { ActivityManager.START_CANCELED };
+
+            mActivityLaunchAnimator.startIntentWithAnimation(animCallbackForLambda, (adapter) -> {
+                ActivityOptions options = new ActivityOptions(
+                        getActivityOptions(mDisplayId, adapter));
+                options.setDisallowEnterPictureInPictureWhileLaunching(
+                        disallowEnterPictureInPictureWhileLaunching);
+                if (CameraIntents.isInsecureCameraIntent(intent)) {
+                    // Normally an activity will set it's requested rotation
+                    // animation on its window. However when launching an activity
+                    // causes the orientation to change this is too late. In these cases
+                    // the default animation is used. This doesn't look good for
+                    // the camera (as it rotates the camera contents out of sync
+                    // with physical reality). So, we ask the WindowManager to
+                    // force the crossfade animation if an orientation change
+                    // happens to occur during the launch.
+                    options.setRotationAnimationHint(
+                            WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS);
+                }
+                if (intent.getAction() == Settings.Panel.ACTION_VOLUME) {
+                    // Settings Panel is implemented as activity(not a dialog), so
+                    // underlying app is paused and may enter picture-in-picture mode
+                    // as a result.
+                    // So we need to disable picture-in-picture mode here
+                    // if it is volume panel.
+                    options.setDisallowEnterPictureInPictureWhileLaunching(true);
+                }
+
+                try {
+                    result[0] = ActivityTaskManager.getService().startActivityAsUser(
+                            null, mContext.getBasePackageName(), mContext.getAttributionTag(),
+                            intent,
+                            intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                            null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
+                            options.toBundle(), UserHandle.CURRENT.getIdentifier());
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Unable to start activity", e);
+                }
+                return result[0];
+            });
+
             if (callback != null) {
-                callback.onActivityStarted(result);
+                callback.onActivityStarted(result[0]);
             }
         };
         Runnable cancelRunnable = () -> {
@@ -2789,7 +2815,7 @@
                 callback.onActivityStarted(ActivityManager.START_CANCELED);
             }
         };
-        executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShade,
+        executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShadeDirectly,
                 afterKeyguardGone, true /* deferred */);
     }
 
@@ -3134,18 +3160,34 @@
     }
 
     @Override
-    public void postStartActivityDismissingKeyguard(final PendingIntent intent) {
-        mHandler.post(() -> startPendingIntentDismissingKeyguard(intent));
+    public void postStartActivityDismissingKeyguard(PendingIntent intent) {
+        postStartActivityDismissingKeyguard(intent, null /* animationController */);
+    }
+
+    @Override
+    public void postStartActivityDismissingKeyguard(final PendingIntent intent,
+            @Nullable ActivityLaunchAnimator.Controller animationController) {
+        mHandler.post(() -> startPendingIntentDismissingKeyguard(intent,
+                null /* intentSentUiThreadCallback */, animationController));
     }
 
     @Override
     public void postStartActivityDismissingKeyguard(final Intent intent, int delay) {
-        mHandler.postDelayed(() ->
-                handleStartActivityDismissingKeyguard(intent, true /*onlyProvisioned*/), delay);
+        postStartActivityDismissingKeyguard(intent, delay, null /* animationController */);
     }
 
-    private void handleStartActivityDismissingKeyguard(Intent intent, boolean onlyProvisioned) {
-        startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */);
+    @Override
+    public void postStartActivityDismissingKeyguard(Intent intent, int delay,
+            @Nullable ActivityLaunchAnimator.Controller animationController) {
+        mHandler.postDelayed(
+                () ->
+                        startActivityDismissingKeyguard(intent, true /* onlyProvisioned */,
+                                true /* dismissShade */,
+                                false /* disallowEnterPictureInPictureWhileLaunching */,
+                                null /* callback */,
+                                0 /* flags */,
+                                animationController),
+                delay);
     }
 
     @Override
@@ -3618,6 +3660,23 @@
         mShadeController.runPostCollapseRunnables();
     }
 
+    /**
+     * Collapse the panel directly if we are on the main thread, post the collapsing on the main
+     * thread if we are not.
+     */
+    void collapsePanelOnMainThread() {
+        if (Looper.getMainLooper().isCurrentThread()) {
+            mShadeController.collapsePanel();
+        } else {
+            mContext.getMainExecutor().execute(mShadeController::collapsePanel);
+        }
+    }
+
+    /** Collapse the panel. The collapsing will be animated for the given {@code duration}. */
+    void collapsePanelWithDuration(int duration) {
+        mNotificationPanelViewController.collapseWithDuration(duration);
+    }
+
     @Override
     public void onStatePreChange(int oldState, int newState) {
         // If we're visible and switched to SHADE_LOCKED (the user dragged
@@ -4043,7 +4102,8 @@
             final Intent cameraIntent = CameraIntents.getInsecureCameraIntent(mContext);
             startActivityDismissingKeyguard(cameraIntent,
                     false /* onlyProvisioned */, true /* dismissShade */,
-                    true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0);
+                    true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
+                    null /* animationController */);
         } else {
             if (!mDeviceInteractive) {
                 // Avoid flickering of the scrim when we instant launch the camera and the bouncer
@@ -4094,7 +4154,8 @@
         if (!mStatusBarKeyguardViewManager.isShowing()) {
             startActivityDismissingKeyguard(emergencyIntent,
                     false /* onlyProvisioned */, true /* dismissShade */,
-                    true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0);
+                    true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
+                    null /* animationController */);
             return;
         }
 
@@ -4415,7 +4476,14 @@
         KeyboardShortcuts.dismiss();
     }
 
-    public void executeActionDismissingKeyguard(Runnable action, boolean afterKeyguardGone) {
+    /**
+     * Dismiss the keyguard then execute an action.
+     *
+     * @param action The action to execute after dismissing the keyguard.
+     * @param collapsePanel Whether we should collapse the panel after dismissing the keyguard.
+     */
+    private void executeActionDismissingKeyguard(Runnable action, boolean afterKeyguardGone,
+            boolean collapsePanel) {
         if (!mDeviceProvisionedController.isDeviceProvisioned()) return;
 
         dismissKeyguardThenExecute(() -> {
@@ -4431,7 +4499,8 @@
                 action.run();
             }).start();
 
-            return mShadeController.collapsePanel();
+            boolean deferred = collapsePanel ? mShadeController.collapsePanel() : false;
+            return deferred;
         }, afterKeyguardGone);
     }
 
@@ -4443,27 +4512,54 @@
     @Override
     public void startPendingIntentDismissingKeyguard(
             final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback) {
-        startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback, null /* row */);
+        startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback,
+                (ActivityLaunchAnimator.Controller) null);
+    }
+
+    @Override
+    public void startPendingIntentDismissingKeyguard(PendingIntent intent,
+            Runnable intentSentUiThreadCallback, View associatedView) {
+        ActivityLaunchAnimator.Controller animationController = null;
+        if (associatedView instanceof ExpandableNotificationRow) {
+            animationController = mNotificationAnimationProvider.getAnimatorController(
+                    ((ExpandableNotificationRow) associatedView));
+        }
+
+        startPendingIntentDismissingKeyguard(intent, intentSentUiThreadCallback,
+                animationController);
     }
 
     @Override
     public void startPendingIntentDismissingKeyguard(
             final PendingIntent intent, @Nullable final Runnable intentSentUiThreadCallback,
-            View associatedView) {
+            @Nullable ActivityLaunchAnimator.Controller animationController) {
         final boolean afterKeyguardGone = intent.isActivity()
                 && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
                 mLockscreenUserManager.getCurrentUserId());
 
+        boolean animate = animationController != null && areLaunchAnimationsEnabled();
+        boolean collapse = !animate;
         executeActionDismissingKeyguard(() -> {
             try {
-                intent.send(null, 0, null, null, null, null, getActivityOptions(
-                        mDisplayId,
-                        mActivityLaunchAnimator.getLaunchAnimation(associatedView, isOccluded())));
+                // We wrap animationCallback with a StatusBarLaunchAnimatorController so that the
+                // shade is collapsed after the animation (or when it is cancelled, aborted, etc).
+                ActivityLaunchAnimator.Controller controller =
+                        animate ? new StatusBarLaunchAnimatorController(animationController, this,
+                                intent.isActivity())
+                                : null;
+
+                mActivityLaunchAnimator.startPendingIntentWithAnimation(
+                        controller,
+                        (animationAdapter) -> intent.sendAndReturnResult(null, 0, null, null, null,
+                                null, getActivityOptions(mDisplayId, animationAdapter)));
             } catch (PendingIntent.CanceledException e) {
                 // the stack trace isn't very helpful here.
                 // Just log the exception message.
                 Log.w(TAG, "Sending intent failed: " + e);
-
+                if (!collapse) {
+                    // executeActionDismissingKeyguard did not collapse for us already.
+                    collapsePanelOnMainThread();
+                }
                 // TODO: Dismiss Keyguard.
             }
             if (intent.isActivity()) {
@@ -4472,7 +4568,7 @@
             if (intentSentUiThreadCallback != null) {
                 postOnUiThread(intentSentUiThreadCallback);
             }
-        }, afterKeyguardGone);
+        }, afterKeyguardGone, collapse);
     }
 
     private void postOnUiThread(Runnable runnable) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index ef2444e..2815ce7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -187,6 +187,7 @@
     private int mLastBiometricMode;
     private boolean mLastLockVisible;
     private boolean mLastLockOrientationIsPortrait;
+    private boolean mQsExpanded;
 
     private OnDismissAction mAfterKeyguardGoneAction;
     private Runnable mKeyguardGoneCancelAction;
@@ -1128,9 +1129,16 @@
     }
 
     /**
+     * Whether qs is currently expanded.
+     */
+    public boolean isQsExpanded() {
+        return mQsExpanded;
+    }
+    /**
      * Set whether qs is currently expanded
      */
     public void setQsExpanded(boolean expanded) {
+        mQsExpanded = expanded;
         if (mAlternateAuthInterceptor != null) {
             mAlternateAuthInterceptor.setQsExpanded(expanded);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
new file mode 100644
index 0000000..d45f64f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
@@ -0,0 +1,47 @@
+package com.android.systemui.statusbar.phone
+
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator
+
+/**
+ * A [ActivityLaunchAnimator.Controller] that takes care of collapsing the status bar at the right
+ * time.
+ */
+class StatusBarLaunchAnimatorController(
+    private val delegate: ActivityLaunchAnimator.Controller,
+    private val statusBar: StatusBar,
+    private val isLaunchForActivity: Boolean = true
+) : ActivityLaunchAnimator.Controller by delegate {
+    override fun onIntentStarted(willAnimate: Boolean) {
+        delegate.onIntentStarted(willAnimate)
+        if (!willAnimate) {
+            statusBar.collapsePanelOnMainThread()
+        }
+    }
+
+    override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
+        delegate.onLaunchAnimationStart(isExpandingFullyAbove)
+        if (!isExpandingFullyAbove) {
+            statusBar.collapsePanelWithDuration(ActivityLaunchAnimator.ANIMATION_DURATION.toInt())
+        }
+    }
+
+    override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+        delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
+        statusBar.onLaunchAnimationEnd(isExpandingFullyAbove)
+    }
+
+    override fun onLaunchAnimationCancelled() {
+        delegate.onLaunchAnimationCancelled()
+        statusBar.onLaunchAnimationCancelled()
+    }
+
+    override fun onLaunchAnimationTimedOut() {
+        delegate.onLaunchAnimationTimedOut()
+        statusBar.onLaunchAnimationTimedOut(isLaunchForActivity)
+    }
+
+    override fun onLaunchAnimationAborted() {
+        delegate.onLaunchAnimationAborted()
+        statusBar.collapsePanelOnMainThread()
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 801ac96..2e918da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -21,7 +21,6 @@
 import static com.android.systemui.statusbar.phone.StatusBar.getActivityOptions;
 
 import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
 import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -40,7 +39,6 @@
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.EventLog;
-import android.view.RemoteAnimationAdapter;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.NotificationVisibility;
@@ -52,6 +50,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.FeatureFlags;
@@ -60,11 +59,10 @@
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.RemoteInputController;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
@@ -124,6 +122,7 @@
     private final NotificationPresenter mPresenter;
     private final NotificationPanelViewController mNotificationPanel;
     private final ActivityLaunchAnimator mActivityLaunchAnimator;
+    private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
     private final OnUserInteractionCallback mOnUserInteractionCallback;
 
     private boolean mIsCollapsingToShowActivityOverLockscreen;
@@ -162,7 +161,8 @@
             StatusBar statusBar,
             NotificationPresenter presenter,
             NotificationPanelViewController panel,
-            ActivityLaunchAnimator activityLaunchAnimator) {
+            ActivityLaunchAnimator activityLaunchAnimator,
+            NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) {
         mContext = context;
         mCommandQueue = commandQueue;
         mMainThreadHandler = mainThreadHandler;
@@ -198,6 +198,7 @@
         mPresenter = presenter;
         mNotificationPanel = panel;
         mActivityLaunchAnimator = activityLaunchAnimator;
+        mNotificationAnimationProvider = notificationAnimationProvider;
 
         if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
             mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@@ -400,17 +401,15 @@
         }
 
         if (Looper.getMainLooper().isCurrentThread()) {
-            mBubblesManagerOptional.get().expandStackAndSelectBubble(entry);
+            expandBubbleStack(entry);
         } else {
-            mMainThreadHandler.post(
-                    () -> mBubblesManagerOptional.get().expandStackAndSelectBubble(entry));
+            mMainThreadHandler.post(() -> expandBubbleStack(entry));
         }
+    }
 
-        // expandStackAndSelectBubble won't affect shouldCollapse, so we can collapse directly even
-        // if we are not on the main thread.
-        if (shouldCollapse()) {
-            collapseOnMainThread();
-        }
+    private void expandBubbleStack(NotificationEntry entry) {
+        mBubblesManagerOptional.get().expandStackAndSelectBubble(entry);
+        mShadeController.collapsePanel();
     }
 
     private void startNotificationIntent(
@@ -420,32 +419,36 @@
             ExpandableNotificationRow row,
             boolean wasOccluded,
             boolean isActivityIntent) {
-        RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(row,
-                wasOccluded);
         mLogger.logStartNotificationIntent(entry.getKey(), intent);
         try {
-            if (adapter != null) {
-                ActivityTaskManager.getService()
-                        .registerRemoteAnimationForNextActivityStart(
-                                intent.getCreatorPackage(), adapter);
+            ActivityLaunchAnimator.Controller animationController = null;
+            if (!wasOccluded && mStatusBar.areLaunchAnimationsEnabled()) {
+                animationController = new StatusBarLaunchAnimatorController(
+                        mNotificationAnimationProvider.getAnimatorController(row), mStatusBar,
+                        isActivityIntent);
             }
-            long eventTime = row.getAndResetLastActionUpTime();
-            Bundle options = eventTime > 0
-                    ? getActivityOptions(
-                            mStatusBar.getDisplayId(),
-                            adapter,
-                            mKeyguardStateController.isShowing(),
-                            eventTime)
-                    : getActivityOptions(mStatusBar.getDisplayId(), adapter);
-            int launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
-                    null, null, options);
-            mMainThreadHandler.post(() -> {
-                mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent);
-                if (shouldCollapse()) {
-                    collapseOnMainThread();
-                }
-            });
-        } catch (RemoteException | PendingIntent.CanceledException e) {
+
+            mActivityLaunchAnimator.startPendingIntentWithAnimation(animationController,
+                    (adapter) -> {
+                        long eventTime = row.getAndResetLastActionUpTime();
+                        Bundle options = eventTime > 0
+                                ? getActivityOptions(
+                                mStatusBar.getDisplayId(),
+                                adapter,
+                                mKeyguardStateController.isShowing(),
+                                eventTime)
+                                : getActivityOptions(mStatusBar.getDisplayId(), adapter);
+                        return intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
+                                null, null, options);
+                    });
+
+            // Note that other cases when we should still collapse (like activity already on top) is
+            // handled by the StatusBarLaunchAnimatorController.
+            boolean shouldCollapse = animationController == null;
+            if (shouldCollapse) {
+                collapseOnMainThread();
+            }
+        } catch (PendingIntent.CanceledException e) {
             // the stack trace isn't very helpful here.
             // Just log the exception message.
             mLogger.logSendingIntentFailed(e);
@@ -458,20 +461,30 @@
             ExpandableNotificationRow row) {
         mActivityStarter.dismissKeyguardThenExecute(() -> {
             AsyncTask.execute(() -> {
-                int launchResult = TaskStackBuilder.create(mContext)
-                        .addNextIntentWithParentStack(intent)
-                        .startActivities(getActivityOptions(
-                                mStatusBar.getDisplayId(),
-                                mActivityLaunchAnimator.getLaunchAnimation(
-                                        row, mStatusBar.isOccluded())),
-                                new UserHandle(UserHandle.getUserId(appUid)));
+                ActivityLaunchAnimator.Controller animationController = null;
+                if (!mStatusBar.isOccluded() && mStatusBar.areLaunchAnimationsEnabled()) {
+                    animationController = new StatusBarLaunchAnimatorController(
+                            mNotificationAnimationProvider.getAnimatorController(row), mStatusBar,
+                            true /* isActivityIntent */);
+                }
+
+                mActivityLaunchAnimator.startIntentWithAnimation(
+                        animationController,
+                        (adapter) -> TaskStackBuilder.create(mContext)
+                                .addNextIntentWithParentStack(intent)
+                                .startActivities(getActivityOptions(
+                                        mStatusBar.getDisplayId(),
+                                        adapter),
+                                        new UserHandle(UserHandle.getUserId(appUid))));
+
+                // Note that other cases when we should still collapse (like activity already on
+                // top) is handled by the StatusBarLaunchAnimatorController.
+                boolean shouldCollapse = animationController == null;
 
                 // Putting it back on the main thread, since we're touching views
                 mMainThreadHandler.post(() -> {
-                    mActivityLaunchAnimator.setLaunchResult(launchResult,
-                            true /* isActivityIntent */);
                     removeHUN(row);
-                    if (shouldCollapse()) {
+                    if (shouldCollapse) {
                         mCommandQueue.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
                                 true /* force */);
                     }
@@ -494,11 +507,10 @@
                     tsb.addNextIntent(intent);
                 }
                 tsb.startActivities(null, UserHandle.CURRENT);
-                if (shouldCollapse()) {
-                    // Putting it back on the main thread, since we're touching views
-                    mMainThreadHandler.post(() -> mCommandQueue.animateCollapsePanels(
-                            CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
-                }
+
+                // Putting it back on the main thread, since we're touching views
+                mMainThreadHandler.post(() -> mCommandQueue.animateCollapsePanels(
+                        CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */));
             });
             return true;
         }, null, false /* afterKeyguardGone */);
@@ -576,11 +588,6 @@
         }
     }
 
-    private boolean shouldCollapse() {
-        return mStatusBarStateController.getState() != StatusBarState.SHADE
-                || !mActivityLaunchAnimator.isAnimationPending();
-    }
-
     private boolean shouldSuppressFullScreenIntent(NotificationEntry entry) {
         if (mPresenter.isDeviceInVrMode()) {
             return true;
@@ -639,6 +646,7 @@
         private NotificationPresenter mNotificationPresenter;
         private NotificationPanelViewController mNotificationPanelViewController;
         private ActivityLaunchAnimator mActivityLaunchAnimator;
+        private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
 
         @Inject
         public Builder(
@@ -714,12 +722,20 @@
             return this;
         }
 
+        /** Set the ActivityLaunchAnimator. */
         public Builder setActivityLaunchAnimator(ActivityLaunchAnimator activityLaunchAnimator) {
             mActivityLaunchAnimator = activityLaunchAnimator;
             return this;
         }
 
-        /** Set the NotificationPanelViewController */
+        /** Set the NotificationLaunchAnimatorControllerProvider. */
+        public Builder setNotificationAnimatorControllerProvider(
+                NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) {
+            mNotificationAnimationProvider = notificationAnimationProvider;
+            return this;
+        }
+
+        /** Set the NotificationPanelViewController. */
         public Builder setNotificationPanelViewController(
                 NotificationPanelViewController notificationPanelViewController) {
             mNotificationPanelViewController = notificationPanelViewController;
@@ -759,7 +775,8 @@
                     mStatusBar,
                     mNotificationPresenter,
                     mNotificationPanelViewController,
-                    mActivityLaunchAnimator);
+                    mActivityLaunchAnimator,
+                    mNotificationAnimationProvider);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 94edd1e..088f947 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -57,7 +57,6 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -117,7 +116,7 @@
 
     private final AccessibilityManager mAccessibilityManager;
     private final KeyguardManager mKeyguardManager;
-    private final ActivityLaunchAnimator mActivityLaunchAnimator;
+    private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final IStatusBarService mBarService;
     private final DynamicPrivacyController mDynamicPrivacyController;
     private boolean mReinflateNotificationsOnUserSwitched;
@@ -133,7 +132,7 @@
             NotificationStackScrollLayoutController stackScrollerController,
             DozeScrimController dozeScrimController,
             ScrimController scrimController,
-            ActivityLaunchAnimator activityLaunchAnimator,
+            NotificationShadeWindowController notificationShadeWindowController,
             DynamicPrivacyController dynamicPrivacyController,
             KeyguardStateController keyguardStateController,
             KeyguardIndicationController keyguardIndicationController,
@@ -152,7 +151,7 @@
         mShadeController = shadeController;
         mCommandQueue = commandQueue;
         mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView());
-        mActivityLaunchAnimator = activityLaunchAnimator;
+        mNotificationShadeWindowController = notificationShadeWindowController;
         mAboveShelfObserver.setListener(statusBarWindow.findViewById(
                 R.id.notification_container_parent));
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
@@ -272,8 +271,7 @@
     @Override
     public boolean isCollapsing() {
         return mNotificationPanel.isCollapsing()
-                || mActivityLaunchAnimator.isAnimationPending()
-                || mActivityLaunchAnimator.isAnimationRunning();
+                || mNotificationShadeWindowController.isLaunchingActivity();
     }
 
     private void maybeEndAmbientPulse() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index ecd9613..e0cbbf0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -18,6 +18,7 @@
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
+import com.android.systemui.biometrics.AuthRippleController;
 import com.android.systemui.statusbar.phone.LockscreenLockIconController;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
@@ -79,4 +80,10 @@
      */
     @StatusBarScope
     LockscreenLockIconController getLockscreenLockIconController();
+
+    /**
+     * Creates an AuthRippleController
+     */
+    @StatusBarScope
+    AuthRippleController getAuthRippleController();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 781abe6..0ce7538 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 
 import com.android.systemui.R;
+import com.android.systemui.biometrics.AuthRippleView;
 import com.android.systemui.statusbar.phone.LockIcon;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
@@ -44,4 +45,13 @@
             NotificationShadeWindowView notificationShadeWindowView) {
         return notificationShadeWindowView.findViewById(R.id.lock_icon);
     }
+
+    /** */
+    @Provides
+    @StatusBarComponent.StatusBarScope
+    @Nullable
+    public static AuthRippleView getAuthRippleView(
+            NotificationShadeWindowView notificationShadeWindowView) {
+        return notificationShadeWindowView.findViewById(R.id.auth_ripple);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index fc9a35d..c9011f4 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -81,7 +81,7 @@
 @SysUISingleton
 public class ThemeOverlayController extends SystemUI implements Dumpable {
     protected static final String TAG = "ThemeOverlayController";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean DEBUG = true;
 
     protected static final int NEUTRAL = 0;
     protected static final int ACCENT = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
index 603d423..46611e0 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
@@ -50,6 +50,8 @@
 
     private var desiredMeasureWidth = 0
     private var desiredMeasureHeight = 0
+    private var transitionVisibility = View.VISIBLE
+
     /**
      * The measured state of this view which is the one we will lay ourselves out with. This
      * may differ from the currentState if there is an external animation or transition running.
@@ -81,6 +83,13 @@
         }
     }
 
+    override fun setTransitionVisibility(visibility: Int) {
+        // We store the last transition visibility assigned to this view to restore it later if
+        // necessary.
+        super.setTransitionVisibility(visibility)
+        transitionVisibility = visibility
+    }
+
     override fun onFinishInflate() {
         super.onFinishInflate()
         val childCount = childCount
@@ -162,7 +171,16 @@
         updateBounds()
         translationX = currentState.translation.x
         translationY = currentState.translation.y
+
         CrossFadeHelper.fadeIn(this, currentState.alpha)
+
+        // CrossFadeHelper#fadeIn will change this view visibility, which overrides the transition
+        // visibility. We set the transition visibility again to make sure that this view plays well
+        // with GhostView, which sets the transition visibility and is used for activity launch
+        // animations.
+        if (transitionVisibility != View.VISIBLE) {
+            setTransitionVisibility(transitionVisibility)
+        }
     }
 
     private fun applyCurrentStateOnPredraw() {
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index ba063a8..c1835db 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -28,8 +28,10 @@
 
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.LifecycleActivity;
 
@@ -44,6 +46,7 @@
 
     private final QuickAccessWalletClient mQuickAccessWalletClient;
     private final KeyguardStateController mKeyguardStateController;
+    private final KeyguardDismissUtil mKeyguardDismissUtil;
     private final ActivityStarter mActivityStarter;
     private final Executor mExecutor;
     private final Handler mHandler;
@@ -54,12 +57,14 @@
     public WalletActivity(
             QuickAccessWalletClient quickAccessWalletClient,
             KeyguardStateController keyguardStateController,
+            KeyguardDismissUtil keyguardDismissUtil,
             ActivityStarter activityStarter,
             @Background Executor executor,
-            @Background Handler handler,
+            @Main Handler handler,
             UserTracker userTracker) {
         mQuickAccessWalletClient = quickAccessWalletClient;
         mKeyguardStateController = keyguardStateController;
+        mKeyguardDismissUtil = keyguardDismissUtil;
         mActivityStarter = activityStarter;
         mExecutor = executor;
         mHandler = handler;
@@ -88,20 +93,30 @@
                 mExecutor,
                 mHandler,
                 mUserTracker,
-                !mKeyguardStateController.isUnlocked());
+                mKeyguardStateController);
+        // Clicking the wallet button will open the wallet app if the device is unlocked; bring up
+        // the security bouncer otherwise.
         walletView.getWalletButton().setOnClickListener(
-                v -> mActivityStarter.startActivity(
-                        mQuickAccessWalletClient.createWalletIntent(), true));
+                v -> {
+                    if (mKeyguardStateController.isUnlocked()) {
+                        mActivityStarter.startActivity(
+                                mQuickAccessWalletClient.createWalletIntent(), true);
+                    } else {
+                        mKeyguardDismissUtil.executeWhenUnlocked(() -> false, false);
+                    }
+                });
     }
 
     @Override
     protected void onStart() {
         super.onStart();
+        mKeyguardStateController.addCallback(mWalletScreenController);
     }
 
     @Override
     protected void onResume() {
         super.onResume();
+        mWalletScreenController.queryWalletCards();
     }
 
     @Override
@@ -116,6 +131,7 @@
 
     @Override
     protected void onDestroy() {
+        mKeyguardStateController.removeCallback(mWalletScreenController);
         mWalletScreenController.onDismissed();
         super.onDestroy();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
index a93f0f0..d195062 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletScreenController.java
@@ -42,6 +42,7 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -52,7 +53,8 @@
 public class WalletScreenController implements
         WalletCardCarousel.OnSelectionListener,
         QuickAccessWalletClient.OnWalletCardsRetrievedCallback,
-        QuickAccessWalletClient.WalletServiceEventListener {
+        QuickAccessWalletClient.WalletServiceEventListener,
+        KeyguardStateController.Callback {
 
     private static final String TAG = "WalletScreenCtrl";
     private static final String PREFS_HAS_CARDS = "has_cards";
@@ -65,6 +67,7 @@
     private final ActivityStarter mActivityStarter;
     private final Executor mExecutor;
     private final Handler mHandler;
+    private final KeyguardStateController mKeyguardStateController;
     private final Runnable mSelectionRunnable = this::selectCard;
     private final SharedPreferences mPrefs;
     private final WalletView mWalletView;
@@ -72,7 +75,6 @@
 
     @VisibleForTesting String mSelectedCardId;
     @VisibleForTesting boolean mIsDismissed;
-    private boolean mIsDeviceLocked;
     private boolean mHasRegisteredListener;
 
     public WalletScreenController(
@@ -83,12 +85,13 @@
             Executor executor,
             Handler handler,
             UserTracker userTracker,
-            boolean isDeviceLocked) {
+            KeyguardStateController keyguardStateController) {
         mContext = context;
         mWalletClient = walletClient;
         mActivityStarter = activityStarter;
         mExecutor = executor;
         mHandler = handler;
+        mKeyguardStateController = keyguardStateController;
         mPrefs = userTracker.getUserContext().getSharedPreferences(TAG, Context.MODE_PRIVATE);
         mWalletView = walletView;
         mWalletView.setMinimumHeight(getExpectedMinHeight());
@@ -105,7 +108,6 @@
             // to decrease perceived latency.
             showEmptyStateView();
         }
-        mIsDeviceLocked = isDeviceLocked;
     }
 
     /**
@@ -122,11 +124,17 @@
         for (WalletCard card : walletCards) {
             data.add(new QAWalletCardViewInfo(mContext, card));
         }
+
+        // Get on main thread for UI updates.
         mHandler.post(() -> {
+            if (mIsDismissed) {
+                return;
+            }
             if (data.isEmpty()) {
                 showEmptyStateView();
             } else {
-                mWalletView.showCardCarousel(data, response.getSelectedIndex(), mIsDeviceLocked);
+                mWalletView.showCardCarousel(
+                        data, response.getSelectedIndex(), !mKeyguardStateController.isUnlocked());
             }
             // The empty state view will not be shown preemptively next time if cards were returned
             mPrefs.edit().putBoolean(PREFS_HAS_CARDS, !data.isEmpty()).apply();
@@ -140,10 +148,10 @@
      */
     @Override
     public void onWalletCardRetrievalError(@NonNull GetWalletCardsError error) {
-        if (mIsDismissed) {
-            return;
-        }
         mHandler.post(() -> {
+            if (mIsDismissed) {
+                return;
+            }
             mWalletView.showErrorMessage(error.getMessage());
         });
     }
@@ -170,6 +178,11 @@
     }
 
     @Override
+    public void onKeyguardFadingAwayChanged() {
+        queryWalletCards();
+    }
+
+    @Override
     public void onCardSelected(@NonNull WalletCardViewInfo card) {
         if (mIsDismissed) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index d2f0720..a379394 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -103,7 +103,9 @@
         CharSequence centerCardText = centerCard.getLabel();
         Drawable icon = centerCard.getIcon();
         if (icon != null) {
-            mIcon.setImageDrawable(resizeDrawable(getResources(), icon));
+            Drawable drawable = resizeDrawable(getResources(), icon);
+            drawable.setTint(mContext.getColor(R.color.GM2_blue_600));
+            mIcon.setImageDrawable(drawable);
             mIcon.setVisibility(VISIBLE);
         } else {
             mIcon.setVisibility(INVISIBLE);
@@ -126,7 +128,6 @@
         mCardCarouselContainer.setVisibility(VISIBLE);
         mErrorView.setVisibility(GONE);
         if (isDeviceLocked) {
-            // TODO(b/182964813): Add click action to prompt device unlock.
             mWalletButton.setText(R.string.wallet_button_label_device_locked);
         } else {
             mWalletButton.setText(R.string.wallet_button_label_device_unlocked);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 33b2d67..743dd46 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -157,11 +157,12 @@
     @WMSingleton
     @Provides
     static PhonePipMenuController providesPipPhoneMenuController(Context context,
-            PipMediaController pipMediaController, SystemWindows systemWindows,
+            PipBoundsState pipBoundsState, PipMediaController pipMediaController,
+            SystemWindows systemWindows,
             @ShellMainThread ShellExecutor mainExecutor,
             @ShellMainThread Handler mainHandler) {
-        return new PhonePipMenuController(context, pipMediaController, systemWindows,
-                mainExecutor, mainHandler);
+        return new PhonePipMenuController(context, pipBoundsState, pipMediaController,
+                systemWindows, mainExecutor, mainHandler);
     }
 
     @WMSingleton
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 70a7b7a..0fcd79b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -18,26 +18,34 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.ContentResolver;
+import android.content.Context;
 import android.content.res.Resources;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
 
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.keyguard.clock.ClockManager;
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.plugins.BcSmartspaceDataPlugin;
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
 import com.android.systemui.statusbar.phone.NotificationIconContainer;
@@ -50,6 +58,8 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.verification.VerificationMode;
 
+import java.util.concurrent.Executor;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
@@ -78,6 +88,12 @@
     ContentResolver mContentResolver;
     @Mock
     BroadcastDispatcher mBroadcastDispatcher;
+    @Mock
+    private PluginManager mPluginManager;
+    @Mock
+    private FeatureFlags mFeatureFlags;
+    @Mock
+    private Executor mExecutor;
 
     private KeyguardClockSwitchController mController;
 
@@ -87,6 +103,8 @@
 
         when(mView.findViewById(com.android.systemui.R.id.left_aligned_notification_icon_container))
                 .thenReturn(mNotificationIcons);
+        when(mView.getContext()).thenReturn(getContext());
+        when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true);
         when(mView.isAttachedToWindow()).thenReturn(true);
         when(mResources.getString(anyInt())).thenReturn("h:mm");
         mController = new KeyguardClockSwitchController(
@@ -98,7 +116,10 @@
                 mKeyguardSliceViewController,
                 mNotificationIconAreaController,
                 mContentResolver,
-                mBroadcastDispatcher);
+                mBroadcastDispatcher,
+                mPluginManager,
+                mFeatureFlags,
+                mExecutor);
 
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
         when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
@@ -182,6 +203,45 @@
         verify(mView).setClockPlugin(mClockPlugin, StatusBarState.SHADE);
     }
 
+    @Test
+    public void testSmartspacePluginConnectedRemovesKeyguardStatusArea() {
+        mController.init();
+
+        View statusArea = mock(View.class);
+        when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(statusArea);
+
+        View nic = mock(View.class);
+        when(mView.findViewById(R.id.left_aligned_notification_icon_container)).thenReturn(nic);
+        when(nic.getLayoutParams()).thenReturn(mock(RelativeLayout.LayoutParams.class));
+
+        BcSmartspaceDataPlugin plugin = mock(BcSmartspaceDataPlugin.class);
+        TestView view = mock(TestView.class);
+        when(plugin.getView(any())).thenReturn(view);
+
+        mController.mPluginListener.onPluginConnected(plugin, mContext);
+        verify(statusArea).setVisibility(View.GONE);
+    }
+
+    @Test
+    public void testSmartspacePluginDisconnectedShowsKeyguardStatusArea() {
+        mController.init();
+
+        View statusArea = mock(View.class);
+        when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(statusArea);
+
+        View nic = mock(View.class);
+        when(mView.findViewById(R.id.left_aligned_notification_icon_container)).thenReturn(nic);
+        when(nic.getLayoutParams()).thenReturn(mock(RelativeLayout.LayoutParams.class));
+
+        BcSmartspaceDataPlugin plugin = mock(BcSmartspaceDataPlugin.class);
+        TestView view = mock(TestView.class);
+        when(plugin.getView(any())).thenReturn(view);
+
+        mController.mPluginListener.onPluginConnected(plugin, mContext);
+        mController.mPluginListener.onPluginDisconnected(plugin);
+        verify(statusArea).setVisibility(View.VISIBLE);
+    }
+
     private void verifyAttachment(VerificationMode times) {
         verify(mClockManager, times).addOnClockChangedListener(
                 any(ClockManager.ClockChangedListener.class));
@@ -191,4 +251,12 @@
                 any(ColorExtractor.OnColorsChangedListener.class));
         verify(mView, times).updateColors(mGradientColors);
     }
+
+    private static class TestView extends View implements BcSmartspaceDataPlugin.SmartspaceView {
+        TestView(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        public void registerDataProvider(BcSmartspaceDataPlugin plugin) { }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 9659610e..d3a2d2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -303,6 +303,19 @@
     }
 
     @Test
+    public void performA11yActions_visible_notifyAccessibilityActionPerformed() {
+        final int displayId = mContext.getDisplayId();
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(2.5f, Float.NaN,
+                    Float.NaN);
+        });
+
+        mMirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null);
+
+        verify(mWindowMagnifierCallback).onAccessibilityActionPerformed(eq(displayId));
+    }
+
+    @Test
     public void onNavigationModeChanged_updateMirrorViewLayout() {
         mInstrumentation.runOnMainSync(() -> {
             mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index ad1ce76..7833114 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -118,6 +118,16 @@
     }
 
     @Test
+    public void onAccessibilityActionPerformed_enabled_notifyCallback() throws RemoteException {
+        mCommandQueue.requestWindowMagnificationConnection(true);
+        waitForIdleSync();
+
+        mWindowMagnification.onAccessibilityActionPerformed(Display.DEFAULT_DISPLAY);
+
+        verify(mConnectionCallback).onAccessibilityActionPerformed(eq(Display.DEFAULT_DISPLAY));
+    }
+
+    @Test
     public void onConfigurationChanged_updateModeSwitches() {
         final Configuration config = new Configuration();
         config.densityDpi = Configuration.DENSITY_DPI_ANY;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
index 814f073..28cc580 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
@@ -348,61 +348,95 @@
         final AccessibilityNodeInfo infos = new AccessibilityNodeInfo();
         mMenuView.onInitializeAccessibilityNodeInfo(infos);
 
-        assertThat(infos.getActionList().size()).isEqualTo(4);
+        assertThat(infos.getActionList().size()).isEqualTo(5);
     }
 
     @Test
-    public void accessibilityActionMove_moveTopLeft_success() {
+    public void accessibilityActionMove_halfOval_moveTopLeft_success() {
         final AccessibilityFloatingMenuView menuView =
                 spy(new AccessibilityFloatingMenuView(mContext));
         doReturn(mAvailableBounds).when(menuView).getAvailableBounds();
+        menuView.setShapeType(/* halfOvalShape */ 1);
 
         final boolean isActionPerformed =
                 menuView.performAccessibilityAction(R.id.action_move_top_left, null);
 
         assertThat(isActionPerformed).isTrue();
+        assertThat(menuView.mShapeType).isEqualTo(/* ovalShape */ 0);
         verify(menuView).snapToLocation(mAvailableBounds.left, mAvailableBounds.top);
     }
 
     @Test
-    public void accessibilityActionMove_moveTopRight_success() {
+    public void accessibilityActionMove_halfOval_moveTopRight_success() {
         final AccessibilityFloatingMenuView menuView =
                 spy(new AccessibilityFloatingMenuView(mContext));
         doReturn(mAvailableBounds).when(menuView).getAvailableBounds();
+        menuView.setShapeType(/* halfOvalShape */ 1);
 
         final boolean isActionPerformed =
                 menuView.performAccessibilityAction(R.id.action_move_top_right, null);
 
         assertThat(isActionPerformed).isTrue();
+        assertThat(menuView.mShapeType).isEqualTo(/* ovalShape */ 0);
         verify(menuView).snapToLocation(mAvailableBounds.right, mAvailableBounds.top);
     }
 
     @Test
-    public void accessibilityActionMove_moveBottomLeft_success() {
+    public void accessibilityActionMove_halfOval_moveBottomLeft_success() {
         final AccessibilityFloatingMenuView menuView =
                 spy(new AccessibilityFloatingMenuView(mContext));
         doReturn(mAvailableBounds).when(menuView).getAvailableBounds();
+        menuView.setShapeType(/* halfOvalShape */ 1);
 
         final boolean isActionPerformed =
                 menuView.performAccessibilityAction(R.id.action_move_bottom_left, null);
 
         assertThat(isActionPerformed).isTrue();
+        assertThat(menuView.mShapeType).isEqualTo(/* ovalShape */ 0);
         verify(menuView).snapToLocation(mAvailableBounds.left, mAvailableBounds.bottom);
     }
 
     @Test
-    public void accessibilityActionMove_moveBottomRight_success() {
+    public void accessibilityActionMove_halfOval_moveBottomRight_success() {
         final AccessibilityFloatingMenuView menuView =
                 spy(new AccessibilityFloatingMenuView(mContext));
         doReturn(mAvailableBounds).when(menuView).getAvailableBounds();
+        menuView.setShapeType(/* halfOvalShape */ 1);
 
         final boolean isActionPerformed =
                 menuView.performAccessibilityAction(R.id.action_move_bottom_right, null);
 
         assertThat(isActionPerformed).isTrue();
+        assertThat(menuView.mShapeType).isEqualTo(/* ovalShape */ 0);
         verify(menuView).snapToLocation(mAvailableBounds.right, mAvailableBounds.bottom);
     }
 
+    @Test
+    public void accessibilityActionMove_halfOval_moveOutEdgeAndShow_success() {
+        final AccessibilityFloatingMenuView menuView =
+                spy(new AccessibilityFloatingMenuView(mContext));
+        doReturn(mAvailableBounds).when(menuView).getAvailableBounds();
+        menuView.setShapeType(/* halfOvalShape */ 1);
+
+        final boolean isActionPerformed =
+                menuView.performAccessibilityAction(R.id.action_move_out_edge_and_show, null);
+
+        assertThat(isActionPerformed).isTrue();
+        assertThat(menuView.mShapeType).isEqualTo(/* ovalShape */ 0);
+    }
+
+    @Test
+    public void setupAccessibilityActions_oval_hasActionMoveToEdgeAndHide() {
+        final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext);
+        menuView.setShapeType(/* ovalShape */ 0);
+
+        final AccessibilityNodeInfo infos = new AccessibilityNodeInfo();
+        menuView.onInitializeAccessibilityNodeInfo(infos);
+
+        assertThat(infos.getActionList().stream().anyMatch(
+                action -> action.getId() == R.id.action_move_to_edge_and_hide)).isTrue();
+    }
+
     @After
     public void tearDown() {
         mInterceptMotionEvent = null;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 02ba304..d395075 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -16,14 +16,14 @@
 
 package com.android.systemui.biometrics
 
+import android.graphics.PointF
 import android.hardware.biometrics.BiometricSourceType
 import android.testing.AndroidTestingRunner
-import android.view.View
-import android.view.ViewGroup
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.NotificationShadeWindowController
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.statusbar.policy.ConfigurationController
 import org.junit.Before
@@ -32,6 +32,8 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers
 import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
@@ -41,38 +43,50 @@
 @RunWith(AndroidTestingRunner::class)
 class AuthRippleControllerTest : SysuiTestCase() {
     private lateinit var controller: AuthRippleController
+    @Mock private lateinit var rippleView: AuthRippleView
     @Mock private lateinit var commandRegistry: CommandRegistry
     @Mock private lateinit var configurationController: ConfigurationController
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
-    @Mock private lateinit var rippleView: AuthRippleView
-    @Mock private lateinit var viewHost: ViewGroup
+    @Mock private lateinit var authController: AuthController
+    @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
 
     @Before
-
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         controller = AuthRippleController(
-            commandRegistry, configurationController, context, keyguardUpdateMonitor)
-        controller.rippleView = rippleView // Replace the real ripple view with a mock instance
-        controller.setViewHost(viewHost)
+            context,
+            authController,
+            configurationController,
+            keyguardUpdateMonitor,
+            commandRegistry,
+            notificationShadeWindowController,
+            rippleView
+        )
+        controller.init()
     }
 
     @Test
-    fun testAddRippleView() {
-        val listenerCaptor = ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
-        verify(viewHost).addOnAttachStateChangeListener(listenerCaptor.capture())
+    fun testFingerprintTriggerRipple() {
+        val fpsLocation = PointF(5f, 5f)
+        `when`(authController.udfpsSensorLocation).thenReturn(fpsLocation)
+        controller.onViewAttached()
 
-        // Fake attach to window
-        listenerCaptor.value.onViewAttachedToWindow(viewHost)
-        verify(viewHost).addView(rippleView)
+        val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
+        verify(keyguardUpdateMonitor).registerCallback(captor.capture())
+
+        captor.value.onBiometricAuthenticated(
+            0 /* userId */,
+            BiometricSourceType.FINGERPRINT /* type */,
+            false /* isStrongBiometric */)
+        verify(rippleView).setSensorLocation(fpsLocation)
+        verify(rippleView).startRipple(any())
     }
 
     @Test
-    fun testTriggerRipple() {
-        // Fake attach to window
-        val listenerCaptor = ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
-        verify(viewHost).addOnAttachStateChangeListener(listenerCaptor.capture())
-        listenerCaptor.value.onViewAttachedToWindow(viewHost)
+    fun testFaceTriggerRipple() {
+        val faceLocation = PointF(5f, 5f)
+        `when`(authController.faceAuthSensorLocation).thenReturn(faceLocation)
+        controller.onViewAttached()
 
         val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
         verify(keyguardUpdateMonitor).registerCallback(captor.capture())
@@ -81,17 +95,43 @@
             0 /* userId */,
             BiometricSourceType.FACE /* type */,
             false /* isStrongBiometric */)
-        verify(rippleView, never()).startRipple()
+        verify(rippleView).setSensorLocation(faceLocation)
+        verify(rippleView).startRipple(any())
+    }
+
+    @Test
+    fun testNullFaceSensorLocationDoesNothing() {
+        `when`(authController.faceAuthSensorLocation).thenReturn(null)
+        controller.onViewAttached()
+
+        val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
+        verify(keyguardUpdateMonitor).registerCallback(captor.capture())
+
+        captor.value.onBiometricAuthenticated(
+            0 /* userId */,
+            BiometricSourceType.FACE /* type */,
+            false /* isStrongBiometric */)
+        verify(rippleView, never()).startRipple(any())
+    }
+
+    @Test
+    fun testNullFingerprintSensorLocationDoesNothing() {
+        `when`(authController.udfpsSensorLocation).thenReturn(null)
+        controller.onViewAttached()
+
+        val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
+        verify(keyguardUpdateMonitor).registerCallback(captor.capture())
 
         captor.value.onBiometricAuthenticated(
             0 /* userId */,
             BiometricSourceType.FINGERPRINT /* type */,
             false /* isStrongBiometric */)
-        verify(rippleView).startRipple()
+        verify(rippleView, never()).startRipple(any())
     }
 
     @Test
     fun testUpdateRippleColor() {
+        controller.onViewAttached()
         val captor = ArgumentCaptor
             .forClass(ConfigurationController.ConfigurationListener::class.java)
         verify(configurationController).addCallback(captor.capture())
@@ -104,10 +144,4 @@
         captor.value.onUiModeChanged()
         verify(rippleView).setColor(ArgumentMatchers.anyInt())
     }
-
-    @Test
-    fun testForwardsSensorLocation() {
-        controller.setSensorLocation(5f, 5f)
-        verify(rippleView).setSensorLocation(5f, 5f)
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 9504970..bbd3ce8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -48,6 +48,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -100,13 +101,13 @@
     @Mock
     private DumpManager mDumpManager;
     @Mock
-    private AuthRippleController mAuthRippleController;
-    @Mock
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock
     private KeyguardViewMediator mKeyguardViewMediator;
     @Mock
     private IUdfpsOverlayControllerCallback mUdfpsOverlayControllerCallback;
+    @Mock
+    private FalsingManager mFalsingManager;
 
     private FakeExecutor mFgExecutor;
 
@@ -157,9 +158,9 @@
                 mStatusBar,
                 mStatusBarKeyguardViewManager,
                 mDumpManager,
-                mAuthRippleController,
                 mKeyguardUpdateMonitor,
-                mKeyguardViewMediator);
+                mKeyguardViewMediator,
+                mFalsingManager);
         verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
         mOverlayController = mOverlayCaptor.getValue();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index 1f165bb..923cae8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -180,6 +180,17 @@
     }
 
     @Test
+    public void testIsFalseTap_EmptyRecentEvents() {
+        // Ensure we look at prior events if recent events has already been emptied.
+        when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(new ArrayList<>());
+        when(mFalsingDataProvider.getPriorMotionEvents()).thenReturn(mMotionEventList);
+
+        mBrightLineFalsingManager.isFalseTap(false, 0);
+        verify(mSingleTapClassfier).isTap(mMotionEventList);
+    }
+
+
+    @Test
     public void testIsFalseTap_RobustCheck_NoFaceAuth() {
         when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
         when(mDoubleTapClassifier.classifyGesture(anyInt(), anyDouble(), anyDouble()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 96eb4b0..e88c728 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -2,7 +2,6 @@
 
 import android.app.Notification.MediaStyle
 import android.app.PendingIntent
-import android.app.smartspace.SmartspaceTarget
 import android.graphics.Bitmap
 import android.media.MediaDescription
 import android.media.MediaMetadata
@@ -40,8 +39,6 @@
 
 private const val KEY = "KEY"
 private const val KEY_2 = "KEY_2"
-private const val KEY_MEDIA_SMARTSPACE = "MEDIA_SMARTSPACE_ID"
-private const val KEY_NONE_MEDIA_SMARTSPACE = "NONE_MEDIA_SMARTSPACE_ID"
 private const val PACKAGE_NAME = "com.android.systemui"
 private const val APP_NAME = "SystemUI"
 private const val SESSION_ARTIST = "artist"
@@ -75,8 +72,6 @@
     @Mock lateinit var listener: MediaDataManager.Listener
     @Mock lateinit var pendingIntent: PendingIntent
     @Mock lateinit var activityStarter: ActivityStarter
-    lateinit var smartspaceMediaDataProvider: SmartspaceMediaDataProvider
-    @Mock lateinit var mediaSmartspaceTarget: SmartspaceTarget
     lateinit var mediaDataManager: MediaDataManager
     lateinit var mediaNotification: StatusBarNotification
     @Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData>
@@ -85,7 +80,6 @@
     fun setup() {
         foregroundExecutor = FakeExecutor(FakeSystemClock())
         backgroundExecutor = FakeExecutor(FakeSystemClock())
-        smartspaceMediaDataProvider = SmartspaceMediaDataProvider()
         mediaDataManager = MediaDataManager(
             context = context,
             backgroundExecutor = backgroundExecutor,
@@ -100,7 +94,6 @@
             mediaDataCombineLatest = mediaDataCombineLatest,
             mediaDataFilter = mediaDataFilter,
             activityStarter = activityStarter,
-            smartspaceMediaDataProvider = smartspaceMediaDataProvider,
             useMediaResumption = true,
             useQsMediaPlayer = true
         )
@@ -124,9 +117,6 @@
         // mock, it doesn't pass those events along the chain to the external listeners. So, just
         // treat mediaSessionBasedFilter as a listener for testing.
         listener = mediaSessionBasedFilter
-
-        whenever(mediaSmartspaceTarget.smartspaceTargetId).thenReturn(KEY_MEDIA_SMARTSPACE)
-        whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA)
     }
 
     @After
@@ -330,24 +320,4 @@
         assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
         verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor))
     }
-
-    @Test
-    fun testOnSmartspaceMediaDataLoaded_hasNewMediaTarget_callsListener() {
-        smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
-        verify(listener).onSmartspaceMediaDataLoaded(
-            eq(KEY_MEDIA_SMARTSPACE), eq(mediaSmartspaceTarget))
-    }
-
-    @Test
-    fun testOnSmartspaceMediaDataLoaded_hasNoneMediaTarget_notCallsListener() {
-        smartspaceMediaDataProvider.onTargetsAvailable(listOf())
-        verify(listener, never()).onSmartspaceMediaDataLoaded(anyObject(), anyObject())
-    }
-
-    @Test
-    fun testOnSmartspaceMediaDataLoaded_hasNoneMediaTarget_callsRemoveListener() {
-        smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
-        smartspaceMediaDataProvider.onTargetsAvailable(listOf())
-        verify(listener).onSmartspaceMediaDataRemoved(KEY_MEDIA_SMARTSPACE)
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/plugins/animation/ActivityLaunchAnimatorTest.kt
new file mode 100644
index 0000000..722b0b1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/animation/ActivityLaunchAnimatorTest.kt
@@ -0,0 +1,188 @@
+package com.android.systemui.plugins.animation
+
+import android.app.ActivityManager
+import android.app.WindowConfiguration
+import android.graphics.Point
+import android.graphics.Rect
+import android.os.Looper
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.IRemoteAnimationFinishedCallback
+import android.view.RemoteAnimationAdapter
+import android.view.RemoteAnimationTarget
+import android.view.SurfaceControl
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertNotNull
+import junit.framework.Assert.assertNull
+import junit.framework.Assert.assertTrue
+import junit.framework.AssertionFailedError
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Spy
+import org.mockito.junit.MockitoJUnit
+import kotlin.concurrent.thread
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class ActivityLaunchAnimatorTest : SysuiTestCase() {
+    private val activityLaunchAnimator = ActivityLaunchAnimator()
+    private val rootView = View(mContext)
+    @Spy private val controller = TestLaunchAnimatorController(rootView)
+    @Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
+
+    @get:Rule val rule = MockitoJUnit.rule()
+
+    private fun startIntentWithAnimation(
+        controller: ActivityLaunchAnimator.Controller? = this.controller,
+        intentStarter: (RemoteAnimationAdapter?) -> Int
+    ) {
+        // We start in a new thread so that we can ensure that the callbacks are called in the main
+        // thread.
+        thread {
+            activityLaunchAnimator.startIntentWithAnimation(controller, intentStarter)
+        }.join()
+    }
+
+    @Test
+    fun animationAdapterIsNullIfControllerIsNull() {
+        var startedIntent = false
+        var animationAdapter: RemoteAnimationAdapter? = null
+
+        startIntentWithAnimation(controller = null) { adapter ->
+            startedIntent = true
+            animationAdapter = adapter
+
+            ActivityManager.START_SUCCESS
+        }
+
+        assertTrue(startedIntent)
+        assertNull(animationAdapter)
+    }
+
+    @Test
+    fun animatesIfActivityOpens() {
+        val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+        var animationAdapter: RemoteAnimationAdapter? = null
+        startIntentWithAnimation { adapter ->
+            animationAdapter = adapter
+            ActivityManager.START_SUCCESS
+        }
+
+        assertNotNull(animationAdapter)
+        waitForIdleSync()
+        verify(controller).onIntentStarted(willAnimateCaptor.capture())
+        assertTrue(willAnimateCaptor.value)
+    }
+
+    @Test
+    fun doesNotAnimateIfActivityIsAlreadyOpen() {
+        val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+        startIntentWithAnimation { ActivityManager.START_DELIVERED_TO_TOP }
+
+        waitForIdleSync()
+        verify(controller).onIntentStarted(willAnimateCaptor.capture())
+        assertFalse(willAnimateCaptor.value)
+    }
+
+    @Test
+    fun doesNotStartIfAnimationIsCancelled() {
+        val runner = ActivityLaunchAnimator.Runner(controller)
+        runner.onAnimationCancelled()
+        runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
+
+        waitForIdleSync()
+        verify(controller).onLaunchAnimationCancelled()
+        verify(controller, never()).onLaunchAnimationStart(anyBoolean())
+    }
+
+    @Test
+    fun abortsIfNoOpeningWindowIsFound() {
+        val runner = ActivityLaunchAnimator.Runner(controller)
+        runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
+
+        waitForIdleSync()
+        verify(controller).onLaunchAnimationAborted()
+        verify(controller, never()).onLaunchAnimationStart(anyBoolean())
+    }
+
+    @Test
+    fun startsAnimationIfWindowIsOpening() {
+        val runner = ActivityLaunchAnimator.Runner(controller)
+        runner.onAnimationStart(0, arrayOf(fakeWindow()), emptyArray(), emptyArray(), iCallback)
+        waitForIdleSync()
+        verify(controller).onLaunchAnimationStart(anyBoolean())
+    }
+
+    private fun fakeWindow() = RemoteAnimationTarget(
+            0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0,
+            Point(), Rect(), Rect(), WindowConfiguration(), false, SurfaceControl(), Rect(),
+            ActivityManager.RunningTaskInfo()
+    )
+}
+
+/**
+ * A simple implementation of [ActivityLaunchAnimator.Controller] which throws if it is called
+ * outside of the main thread.
+ */
+private class TestLaunchAnimatorController(
+    private val rootView: View
+) : ActivityLaunchAnimator.Controller {
+    override fun getRootView(): View = rootView
+
+    override fun createAnimatorState() = ActivityLaunchAnimator.State(
+            top = 100,
+            bottom = 200,
+            left = 300,
+            right = 400,
+            topCornerRadius = 10f,
+            bottomCornerRadius = 20f
+    )
+
+    private fun assertOnMainThread() {
+        if (Looper.myLooper() != Looper.getMainLooper()) {
+            throw AssertionFailedError("Called outside of main thread.")
+        }
+    }
+
+    override fun onIntentStarted(willAnimate: Boolean) {
+        assertOnMainThread()
+    }
+
+    override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
+        assertOnMainThread()
+    }
+
+    override fun onLaunchAnimationProgress(
+        state: ActivityLaunchAnimator.State,
+        progress: Float,
+        linearProgress: Float
+    ) {
+        assertOnMainThread()
+    }
+
+    override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+        assertOnMainThread()
+    }
+
+    override fun onLaunchAnimationCancelled() {
+        assertOnMainThread()
+    }
+
+    override fun onLaunchAnimationTimedOut() {
+        assertOnMainThread()
+    }
+
+    override fun onLaunchAnimationAborted() {
+        assertOnMainThread()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index f2f9656b..c35f8b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -34,6 +34,7 @@
 import static org.mockito.Mockito.when;
 
 import android.Manifest;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -46,6 +47,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.FeatureFlagUtils;
+import android.view.View;
 
 import androidx.test.filters.SmallTest;
 
@@ -418,7 +420,7 @@
         public void secondaryClick() {}
 
         @Override
-        public void longClick() {}
+        public void longClick(@Nullable View view) {}
 
         @Override
         public void userSwitch(int currentUser) {}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 937ab1c..0f9ca7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -189,7 +189,7 @@
 
     @Test
     public void testLongClick_Metrics() {
-        mTile.longClick();
+        mTile.longClick(null /* view */);
         verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_LONG_PRESS)));
         assertEquals(1, mUiEventLoggerFake.numLogs());
         UiEventLoggerFake.FakeUiEvent event = mUiEventLoggerFake.get(0);
@@ -201,7 +201,7 @@
     public void testLongClick_log() {
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
 
-        mTile.longClick();
+        mTile.longClick(null /* view */);
         verify(mQsLogger).logTileLongClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index f57283f..613f879 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -30,6 +30,8 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
@@ -117,10 +119,13 @@
     @Mock
     private FeatureFlags mFeatureFlags;
     @Captor
+    ArgumentCaptor<Intent> mIntentCaptor;
+    @Captor
     ArgumentCaptor<GetWalletCardsRequest> mRequestCaptor;
     @Captor
     ArgumentCaptor<QuickAccessWalletClient.OnWalletCardsRetrievedCallback> mCallbackCaptor;
 
+    private Context mSpiedContext;
     private TestableLooper mTestableLooper;
     private QuickAccessWalletTile mTile;
 
@@ -129,8 +134,10 @@
         MockitoAnnotations.initMocks(this);
 
         mTestableLooper = TestableLooper.get(this);
+        mSpiedContext = spy(mContext);
 
-        when(mHost.getContext()).thenReturn(mContext);
+        doNothing().when(mSpiedContext).startActivity(any(Intent.class));
+        when(mHost.getContext()).thenReturn(mSpiedContext);
         when(mHost.getUiEventLogger()).thenReturn(mUiEventLogger);
         when(mFeatureFlags.isQuickAccessWalletEnabled()).thenReturn(true);
         when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true);
@@ -179,16 +186,46 @@
     }
 
     @Test
-    public void testHandleClick_openGPay() {
+    public void testHandleClick_noCards_hasIntent_openWalletApp() {
         Intent intent = new Intent("WalletIntent");
         when(mQuickAccessWalletClient.createWalletIntent()).thenReturn(intent);
+        setUpWalletCard(/* hasCard= */ false);
+
         mTile.handleClick();
+        mTestableLooper.processAllMessages();
 
         verify(mActivityStarter, times(1))
                 .postStartActivityDismissingKeyguard(eq(intent), anyInt());
     }
 
     @Test
+    public void testHandleClick_noCards_noIntent_doNothing() {
+        when(mQuickAccessWalletClient.createWalletIntent()).thenReturn(null);
+        setUpWalletCard(/* hasCard= */ false);
+
+        mTile.handleClick();
+        mTestableLooper.processAllMessages();
+
+        verifyZeroInteractions(mActivityStarter);
+    }
+
+    @Test
+    public void testHandleClick_hasCards_startWalletActivity() {
+        setUpWalletCard(/* hasCard= */ true);
+
+        mTile.handleClick();
+        mTestableLooper.processAllMessages();
+
+        verify(mSpiedContext).startActivity(mIntentCaptor.capture());
+
+        Intent nextStartedIntent = mIntentCaptor.getValue();
+        String walletClassName = "com.android.systemui.wallet.ui.WalletActivity";
+
+        assertNotNull(nextStartedIntent);
+        assertThat(nextStartedIntent.getComponent().getClassName()).isEqualTo(walletClassName);
+    }
+
+    @Test
     public void testHandleUpdateState_updateLabelAndIcon() {
         QSTile.State state = new QSTile.State();
         QSTile.Icon icon = QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_wallet);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index eaef43d..35c92b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -108,6 +108,7 @@
         when(mController.isRemoteInputActive(mEntry)).thenReturn(true);
         mRemoteInputManager.onPerformRemoveNotification(mEntry, mEntry.getKey());
 
+        assertFalse(mEntry.mRemoteEditImeVisible);
         verify(mController).removeRemoteInput(mEntry, null);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index e65db5e..45a7d0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -27,7 +27,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator
+import com.android.systemui.statusbar.notification.ExpandAnimationParameters
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -174,6 +174,13 @@
     }
 
     @Test
+    fun setQsPanelExpansion_appliesBlur() {
+        notificationShadeDepthController.qsPanelExpansion = 1f
+        notificationShadeDepthController.updateBlurCallback.doFrame(0)
+        verify(blurUtils).applyBlur(any(), eq(maxBlur))
+    }
+
+    @Test
     fun updateGlobalDialogVisibility_animatesBlur() {
         notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root)
         verify(globalActionsSpring).animateTo(eq(maxBlur / 2), eq(root))
@@ -215,7 +222,7 @@
     fun updateBlurCallback_appLaunchAnimation_overridesZoom() {
         `when`(shadeSpring.radius).thenReturn(maxBlur)
         `when`(shadeAnimation.radius).thenReturn(maxBlur)
-        val animProgress = ActivityLaunchAnimator.ExpandAnimationParameters()
+        val animProgress = ExpandAnimationParameters()
         animProgress.linearProgress = 1f
         notificationShadeDepthController.notificationLaunchAnimationParams = animProgress
         notificationShadeDepthController.updateBlurCallback.doFrame(0)
@@ -264,7 +271,7 @@
 
     @Test
     fun setNotificationLaunchAnimationParams_schedulesFrame() {
-        val animProgress = ActivityLaunchAnimator.ExpandAnimationParameters()
+        val animProgress = ExpandAnimationParameters()
         animProgress.linearProgress = 0.5f
         notificationShadeDepthController.notificationLaunchAnimationParams = animProgress
         verify(choreographer).postFrameCallback(
@@ -273,7 +280,7 @@
 
     @Test
     fun setNotificationLaunchAnimationParams_whennNull_ignoresIfShadeHasNoBlur() {
-        val animProgress = ActivityLaunchAnimator.ExpandAnimationParameters()
+        val animProgress = ExpandAnimationParameters()
         animProgress.linearProgress = 0.5f
         `when`(shadeSpring.radius).thenReturn(0)
         `when`(shadeAnimation.radius).thenReturn(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
index 9ce7241..5e783a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/charging/WiredChargingRippleControllerTest.kt
@@ -16,17 +16,17 @@
 
 package com.android.systemui.statusbar.charging
 
+import android.content.Context
 import android.testing.AndroidTestingRunner
 import android.view.View
-import android.view.ViewGroupOverlay
-import android.view.ViewRootImpl
+import android.view.WindowManager
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.FeatureFlags
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.statusbar.policy.BatteryController
 import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.mockito.capture
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -34,7 +34,8 @@
 import org.mockito.ArgumentMatchers
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
-import org.mockito.Mockito.never
+import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
@@ -47,55 +48,45 @@
     @Mock private lateinit var batteryController: BatteryController
     @Mock private lateinit var featureFlags: FeatureFlags
     @Mock private lateinit var configurationController: ConfigurationController
-    @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var rippleView: ChargingRippleView
-    @Mock private lateinit var viewHost: View
-    @Mock private lateinit var viewHostRootImpl: ViewRootImpl
-    @Mock private lateinit var viewGroupOverlay: ViewGroupOverlay
+    @Mock private lateinit var windowManager: WindowManager
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        `when`(viewHost.viewRootImpl).thenReturn(viewHostRootImpl)
-        `when`(viewHostRootImpl.view).thenReturn(viewHost)
-        `when`(viewHost.overlay).thenReturn(viewGroupOverlay)
         `when`(featureFlags.isChargingRippleEnabled).thenReturn(true)
-        `when`(keyguardStateController.isShowing).thenReturn(true)
         controller = WiredChargingRippleController(
                 commandRegistry, batteryController, configurationController,
-                featureFlags, context, keyguardStateController)
+                featureFlags, context)
         controller.rippleView = rippleView // Replace the real ripple view with a mock instance
-        controller.setViewHost(viewHost)
+        context.addMockSystemService(Context.WINDOW_SERVICE, windowManager)
     }
 
     @Test
-    fun testSetRippleViewAsOverlay() {
-        val listenerCaptor = ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
-        verify(viewHost).addOnAttachStateChangeListener(listenerCaptor.capture())
-
-        // Fake attach to window
-        listenerCaptor.value.onViewAttachedToWindow(viewHost)
-        verify(viewGroupOverlay).add(rippleView)
-    }
-
-    @Test
-    fun testTriggerRipple() {
+    fun testTriggerRipple_UnlockedState() {
         val captor = ArgumentCaptor
                 .forClass(BatteryController.BatteryStateChangeCallback::class.java)
         verify(batteryController).addCallback(captor.capture())
 
-        val unusedBatteryLevel = 0
+        // Verify ripple added to window manager.
         captor.value.onBatteryLevelChanged(
-                unusedBatteryLevel,
-                false /* plugged in */,
-                false /* charging */)
-        verify(rippleView, never()).startRipple()
-
-        captor.value.onBatteryLevelChanged(
-                unusedBatteryLevel,
+                0 /* unusedBatteryLevel */,
                 false /* plugged in */,
                 true /* charging */)
-        verify(rippleView).startRipple()
+        val attachListenerCaptor =
+                ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java)
+        verify(rippleView).addOnAttachStateChangeListener(attachListenerCaptor.capture())
+        verify(windowManager).addView(eq(rippleView), any<WindowManager.LayoutParams>())
+
+        // Verify ripple started
+        val runnableCaptor =
+                ArgumentCaptor.forClass(Runnable::class.java)
+        attachListenerCaptor.value.onViewAttachedToWindow(rippleView)
+        verify(rippleView).startRipple(runnableCaptor.capture())
+
+        // Verify ripple removed
+        runnableCaptor.value.run()
+        verify(windowManager).removeView(rippleView)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
deleted file mode 100644
index 2fa6cf0..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.notification;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.RemoteAnimationAdapter;
-import android.view.View;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class ActivityLaunchAnimatorTest extends SysuiTestCase {
-
-    private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
-    private ActivityLaunchAnimator mLaunchAnimator;
-    @Mock
-    private ActivityLaunchAnimator.Callback mCallback;
-    @Mock
-    private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
-    @Mock
-    private NotificationShadeWindowView mNotificationShadeWindowView;
-    @Mock
-    private NotificationListContainer mNotificationContainer;
-    @Mock
-    private ExpandableNotificationRow mRow;
-    @Mock
-    private NotificationShadeDepthController mNotificationShadeDepthController;
-    @Mock
-    private NotificationPanelViewController mNotificationPanelViewController;
-    @Rule
-    public MockitoRule rule = MockitoJUnit.rule();
-
-    @Before
-    public void setUp() throws Exception {
-        when(mNotificationShadeWindowViewController.getView())
-                .thenReturn(mNotificationShadeWindowView);
-        when(mNotificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
-        when(mCallback.areLaunchAnimationsEnabled()).thenReturn(true);
-        mLaunchAnimator = new ActivityLaunchAnimator(
-                mNotificationShadeWindowViewController,
-                mCallback,
-                mNotificationPanelViewController,
-                mNotificationShadeDepthController,
-                mNotificationContainer,
-                mExecutor);
-    }
-
-    @Test
-    public void testReturnsNullIfNotEnabled() {
-        when(mCallback.areLaunchAnimationsEnabled()).thenReturn(false);
-        RemoteAnimationAdapter launchAnimation = mLaunchAnimator.getLaunchAnimation(mRow,
-                false /* occluded */);
-        Assert.assertTrue("The LaunchAnimator generated an animation even though animations are "
-                        + "disabled", launchAnimation == null);
-    }
-
-    @Test
-    public void testNotWorkingWhenOccluded() {
-        when(mCallback.areLaunchAnimationsEnabled()).thenReturn(false);
-        RemoteAnimationAdapter launchAnimation = mLaunchAnimator.getLaunchAnimation(mRow,
-                true /* occluded */);
-        Assert.assertTrue("The LaunchAnimator generated an animation even though we're occluded",
-                launchAnimation == null);
-    }
-
-    @Test
-    public void testTimeoutCalled() {
-        RemoteAnimationAdapter launchAnimation = mLaunchAnimator.getLaunchAnimation(mRow,
-                false /* occluded */);
-        Assert.assertTrue("No animation generated", launchAnimation != null);
-        executePostsImmediately(mNotificationShadeWindowView);
-        mLaunchAnimator.setLaunchResult(ActivityManager.START_SUCCESS,
-                true /* wasIntentActivity */);
-        verify(mCallback).onExpandAnimationTimedOut();
-    }
-
-    private void executePostsImmediately(View view) {
-        doAnswer((i) -> {
-            Runnable run = i.getArgument(0);
-            run.run();
-            return null;
-        }).when(view).post(any());
-        doAnswer((i) -> {
-            Runnable run = i.getArgument(0);
-            run.run();
-            return null;
-        }).when(view).postDelayed(any(), anyLong());
-    }
-}
-
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 6a5e6e8..04ac154 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
@@ -82,6 +82,7 @@
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
@@ -219,6 +220,8 @@
     @Mock
     private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
     @Mock
+    private NotificationShadeDepthController mNotificationShadeDepthController;
+    @Mock
     private AuthController mAuthController;
     @Mock
     private ScrimController mScrimController;
@@ -333,6 +336,7 @@
                 mScrimController,
                 mUserManager,
                 mMediaDataManager,
+                mNotificationShadeDepthController,
                 mAmbientState,
                 mFeatureFlags);
         mNotificationPanelViewController.initDependencies(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
index fcea17c..4b8eec4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java
@@ -119,7 +119,7 @@
 
     @Test
     public void testSetForcePluginOpen_beforeStatusBarInitialization() {
-        mNotificationShadeWindowController.setForcePluginOpen(true);
+        mNotificationShadeWindowController.setForcePluginOpen(true, this);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 116e1b9..123e4ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -49,7 +49,6 @@
 import com.android.systemui.DejankUtils;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dock.DockManager;
-import com.android.systemui.statusbar.BlurUtils;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -79,6 +78,7 @@
 
     private ScrimController mScrimController;
     private ScrimView mScrimBehind;
+    private ScrimView mNotificationsScrim;
     private ScrimView mScrimInFront;
     private ScrimView mScrimForBubble;
     private ScrimState mScrimState;
@@ -104,8 +104,6 @@
     @Mock
     private DockManager mDockManager;
     @Mock
-    private BlurUtils mBlurUtils;
-    @Mock
     private ConfigurationController mConfigurationController;
     @Mock
     private FeatureFlags mFeatureFlags;
@@ -163,6 +161,7 @@
         mScrimController.onPreDraw();
         // Force finish all animations.
         mLooper.processAllMessages();
+        endAnimation(mNotificationsScrim);
         endAnimation(mScrimBehind);
         endAnimation(mScrimInFront);
         endAnimation(mScrimForBubble);
@@ -189,6 +188,7 @@
         mScrimBehind = spy(new ScrimView(getContext()));
         mScrimInFront = new ScrimView(getContext());
         mScrimForBubble = new ScrimView(getContext());
+        mNotificationsScrim = new ScrimView(getContext());
         mAlwaysOnEnabled = true;
         mLooper = TestableLooper.get(this);
         DejankUtils.setImmediate(true);
@@ -201,7 +201,6 @@
 
         when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
         when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true);
-        when(mBlurUtils.supportsBlursOnWindows()).thenReturn(true);
 
         doAnswer((Answer<Void>) invocation -> {
             mScrimState = invocation.getArgument(0);
@@ -222,10 +221,11 @@
         mScrimController = new ScrimController(mLightBarController,
                 mDozeParamenters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder,
                 new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
-                mDockManager, mBlurUtils, mConfigurationController, mFeatureFlags,
+                mDockManager, mConfigurationController, mFeatureFlags,
                 new FakeExecutor(new FakeSystemClock()));
         mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
-        mScrimController.attachViews(mScrimBehind, mScrimInFront, mScrimForBubble);
+        mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront,
+                mScrimForBubble);
         mScrimController.setAnimatorListener(mAnimatorListener);
 
         mScrimController.setHasBackdrop(false);
@@ -264,7 +264,7 @@
                 TRANSPARENT /* bubble */);
 
         assertScrimTint(false /* front */,
-                false /* behind */,
+                true /* behind */,
                 false /* bubble */);
     }
 
@@ -516,7 +516,7 @@
                 TRANSPARENT /* bubble */);
 
         assertScrimTint(false /* front */,
-                false /* behind */,
+                true /* behind */,
                 false /* bubble */);
 
         // Back scrim should be visible after start dragging
@@ -586,7 +586,7 @@
     @Test
     public void qsExpansion() {
         reset(mScrimBehind);
-        mScrimController.setQsExpansion(1f);
+        mScrimController.setQsPosition(1f, 999 /* value doesn't matter */);
         finishAnimationsImmediately();
 
         assertScrimAlpha(TRANSPARENT, OPAQUE, TRANSPARENT);
@@ -636,7 +636,7 @@
 
         // Make sure at the very end of the animation, we're reset to transparent
         assertScrimTint(false /* front */,
-                false /* behind */,
+                true /* behind */,
                 false  /* bubble */);
     }
 
@@ -947,7 +947,9 @@
         if (scrim == mScrimInFront) {
             return "front";
         } else if (scrim == mScrimBehind) {
-            return "back";
+            return "behind";
+        } else if (scrim == mNotificationsScrim) {
+            return "notifications";
         } else if (scrim == mScrimForBubble) {
             return "bubble";
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 68464ce..e34bc0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -35,7 +35,6 @@
 import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.PendingIntent;
-import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -54,6 +53,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.animation.ActivityLaunchAnimator;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.FeatureFlags;
@@ -63,9 +63,9 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
@@ -136,6 +136,8 @@
     private OnUserInteractionCallback mOnUserInteractionCallback;
     @Mock
     private NotificationActivityStarter mNotificationActivityStarter;
+    @Mock
+    private ActivityLaunchAnimator mActivityLaunchAnimator;
     private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
 
     private NotificationTestHelper mNotificationTestHelper;
@@ -213,11 +215,14 @@
                         mock(MetricsLogger.class),
                         mock(StatusBarNotificationActivityStarterLogger.class),
                         mOnUserInteractionCallback)
-                .setStatusBar(mStatusBar)
-                .setNotificationPresenter(mock(NotificationPresenter.class))
-                .setNotificationPanelViewController(mock(NotificationPanelViewController.class))
-                .setActivityLaunchAnimator(mock(ActivityLaunchAnimator.class))
-                .build();
+                        .setStatusBar(mStatusBar)
+                        .setNotificationPresenter(mock(NotificationPresenter.class))
+                        .setNotificationPanelViewController(
+                                mock(NotificationPanelViewController.class))
+                        .setActivityLaunchAnimator(mActivityLaunchAnimator)
+                        .setNotificationAnimatorControllerProvider(
+                                mock(NotificationLaunchAnimatorControllerProvider.class))
+                        .build();
 
         // set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg
         doAnswer(mCallOnDismiss).when(mActivityStarter).dismissKeyguardThenExecute(
@@ -254,14 +259,7 @@
         // Then
         verify(mShadeController, atLeastOnce()).collapsePanel();
 
-        verify(mContentIntent).sendAndReturnResult(
-                any(Context.class),
-                anyInt() /* code */,
-                any() /* fillInIntent */,
-                any() /* PendingIntent.OnFinished */,
-                any() /* Handler */,
-                any() /* requiredPermission */,
-                any() /* Bundle options */);
+        verify(mActivityLaunchAnimator).startPendingIntentWithAnimation(eq(null), any());
 
         verify(mAssistManager).hideAssist();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index c0ebfad..8601de5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -47,7 +47,6 @@
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -125,7 +124,7 @@
                 mock(NotificationPanelViewController.class), mock(HeadsUpManagerPhone.class),
                 notificationShadeWindowView, stackScrollLayoutController,
                 mock(DozeScrimController.class), mock(ScrimController.class),
-                mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class),
+                mock(NotificationShadeWindowController.class), mock(DynamicPrivacyController.class),
                 mock(KeyguardStateController.class),
                 mock(KeyguardIndicationController.class), mStatusBar,
                 mock(ShadeControllerImpl.class), mCommandQueue, mInitController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 5de62b9..3d07eb1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -703,6 +703,10 @@
     // ============================= Smart Action tests ============================================
     // =============================================================================================
 
+    private View anyView() {
+        return any();
+    }
+
     @Test
     public void testTapSmartAction_waitsForKeyguard() throws InterruptedException {
         setSmartActions(TEST_ACTION_TITLES);
@@ -710,7 +714,7 @@
         mView.getChildAt(2).performClick();
 
         verify(mActivityStarter, times(1)).startPendingIntentDismissingKeyguard(any(), any(),
-                any());
+                anyView());
     }
 
     @Test
@@ -721,7 +725,8 @@
 
         mView.getChildAt(2).performClick();
 
-        verify(mActivityStarter, never()).startPendingIntentDismissingKeyguard(any(), any(), any());
+        verify(mActivityStarter, never()).startPendingIntentDismissingKeyguard(any(), any(),
+                anyView());
     }
 
     @Test
@@ -734,7 +739,7 @@
         mView.getChildAt(2).performClick();
 
         verify(mActivityStarter, times(1))
-                .startPendingIntentDismissingKeyguard(any(), any(), any());
+                .startPendingIntentDismissingKeyguard(any(), any(), anyView());
     }
 
     @Test
@@ -746,7 +751,7 @@
         mView.getChildAt(2).performClick();
 
         verify(mActivityStarter, times(1))
-                .startPendingIntentDismissingKeyguard(any(), any(), any());
+                .startPendingIntentDismissingKeyguard(any(), any(), anyView());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
index f85962b..653946e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
@@ -51,6 +51,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import com.google.common.util.concurrent.MoreExecutors;
 
@@ -85,6 +86,8 @@
     ActivityStarter mActivityStarter;
     @Mock
     UserTracker mUserTracker;
+    @Mock
+    KeyguardStateController mKeyguardStateController;
     @Captor
     ArgumentCaptor<Intent> mIntentCaptor;
     @Captor
@@ -106,6 +109,7 @@
         when(mWalletClient.getShortcutShortLabel()).thenReturn(SHORTCUT_SHORT_LABEL);
         when(mWalletClient.getServiceLabel()).thenReturn(SERVICE_LABEL);
         when(mWalletClient.createWalletIntent()).thenReturn(mWalletIntent);
+        when(mKeyguardStateController.isUnlocked()).thenReturn(true);
         mController = new WalletScreenController(
                 mContext,
                 mWalletView,
@@ -114,7 +118,7 @@
                 MoreExecutors.directExecutor(),
                 new Handler(mTestableLooper.getLooper()),
                 mUserTracker,
-                /* isDeviceLocked= */false);
+                mKeyguardStateController);
     }
 
     @Test
@@ -206,6 +210,14 @@
     }
 
     @Test
+    public void onKeyguardFadingAwayChanged_queryCards() {
+        mController.onKeyguardFadingAwayChanged();
+
+        verify(mWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
+        assertEquals(mController, mCallbackCaptor.getValue());
+    }
+
+    @Test
     public void onCardSelected() {
         mController.onCardSelected(createCardViewInfo());
 
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 8040851..f9aecd7 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -119,6 +119,11 @@
     }
 
     @Override
+    public void onAccessibilityActionPerformed(int displayId) {
+        updateMagnificationButton(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+    }
+
+    @Override
     public void onTouchInteractionStart(int displayId, int mode) {
         handleUserInteractionChanged(displayId, mode);
     }
@@ -148,8 +153,13 @@
     }
 
     private void updateMagnificationButton(int displayId, int mode) {
-        if (isActivated(displayId, mode) && mMagnificationCapabilities
-                == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) {
+        final boolean isActivated = isActivated(displayId, mode);
+        final boolean showButton;
+        synchronized (mLock) {
+            showButton = isActivated && mMagnificationCapabilities
+                    == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
+        }
+        if (showButton) {
             getWindowMagnificationMgr().showMagnificationButton(displayId, mode);
         } else {
             getWindowMagnificationMgr().removeMagnificationButton(displayId);
@@ -430,13 +440,22 @@
 
     private boolean isActivated(int displayId, int mode) {
         boolean isActivated = false;
-        if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
-                && mFullScreenMagnificationController != null) {
-            isActivated = mFullScreenMagnificationController.isMagnifying(displayId)
-                    || mFullScreenMagnificationController.isForceShowMagnifiableBounds(displayId);
-        } else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
-                && mWindowMagnificationMgr != null) {
-            isActivated = mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId);
+        if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
+            synchronized (mLock) {
+                if (mFullScreenMagnificationController == null) {
+                    return false;
+                }
+                isActivated = mFullScreenMagnificationController.isMagnifying(displayId)
+                        || mFullScreenMagnificationController.isForceShowMagnifiableBounds(
+                        displayId);
+            }
+        } else if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
+            synchronized (mLock) {
+                if (mWindowMagnificationMgr == null) {
+                    return false;
+                }
+                isActivated = mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId);
+            }
         }
         return isActivated;
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index 86f61ee..938cb73 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -95,6 +95,13 @@
         void onPerformScaleAction(int displayId, float scale);
 
         /**
+         * Called when the accessibility action is performed.
+         *
+         * @param displayId The logical display id.
+         */
+        void onAccessibilityActionPerformed(int displayId);
+
+        /**
          * Called when the state of the magnification activation is changed.
          *
          * @param displayId The logical display id.
@@ -536,9 +543,12 @@
 
         @Override
         public void onPerformScaleAction(int displayId, float scale) {
-            synchronized (mLock) {
-                mCallback.onPerformScaleAction(displayId, scale);
-            }
+            mCallback.onPerformScaleAction(displayId, scale);
+        }
+
+        @Override
+        public void onAccessibilityActionPerformed(int displayId) {
+            mCallback.onAccessibilityActionPerformed(displayId);
         }
 
         @Override
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 7fc7933..5cbcacf 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -158,7 +158,6 @@
         "android.hardware.power.stats-V1-java",
         "android.hidl.manager-V1.2-java",
         "capture_state_listener-aidl-java",
-        "dnsresolver_aidl_interface-V8-java",
         "icu4j_calendar_astronomer",
         "netd-client",
         "overlayable_policy_aidl-java",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 185cdfc..922b21a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3789,6 +3789,10 @@
     private void destroyNativeNetwork(@NonNull NetworkAgentInfo networkAgent) {
         try {
             mNetd.networkDestroy(networkAgent.network.getNetId());
+        } catch (RemoteException | ServiceSpecificException e) {
+            loge("Exception destroying network(networkDestroy): " + e);
+        }
+        try {
             mDnsResolver.destroyNetworkCache(networkAgent.network.getNetId());
         } catch (RemoteException | ServiceSpecificException e) {
             loge("Exception destroying network: " + e);
@@ -5683,7 +5687,7 @@
                     + mNetworkRequestForCallback.requestId
                     + " " + mRequests
                     + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent)
-                    + "callback flags: " + mCallbackFlags;
+                    + " callback flags: " + mCallbackFlags;
         }
     }
 
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index fa3771a..6e7771d 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
-import android.os.FileUtils;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -46,6 +45,7 @@
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.security.MessageDigest;
@@ -132,13 +132,13 @@
     private static final String FLASH_LOCK_UNLOCKED = "0";
 
     private final Context mContext;
+    private final String mDataBlockFile;
     private final boolean mIsRunningDSU;
     private final Object mLock = new Object();
     private final CountDownLatch mInitDoneSignal = new CountDownLatch(1);
 
     private int mAllowedUid = -1;
     private long mBlockDeviceSize;
-    private String mDataBlockFile;
 
     @GuardedBy("mLock")
     private boolean mIsWritable = true;
@@ -146,8 +146,12 @@
     public PersistentDataBlockService(Context context) {
         super(context);
         mContext = context;
-        mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
         mIsRunningDSU = SystemProperties.getBoolean(GSI_RUNNING_PROP, false);
+        if (mIsRunningDSU) {
+            mDataBlockFile = GSI_SANDBOX;
+        } else {
+            mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
+        }
         mBlockDeviceSize = -1; // Load lazily
     }
 
@@ -262,7 +266,11 @@
     private long getBlockDeviceSize() {
         synchronized (mLock) {
             if (mBlockDeviceSize == -1) {
-                mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
+                if (mIsRunningDSU) {
+                    mBlockDeviceSize = MAX_DATA_BLOCK_SIZE;
+                } else {
+                    mBlockDeviceSize = nativeGetBlockDeviceSize(mDataBlockFile);
+                }
             }
         }
 
@@ -292,40 +300,30 @@
         return true;
     }
 
-    private FileOutputStream getBlockOutputStream() throws IOException {
-        if (!mIsRunningDSU) {
-            return new FileOutputStream(new File(mDataBlockFile));
-        } else {
-            File sandbox = new File(GSI_SANDBOX);
-            File realpdb = new File(SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP));
-            if (!sandbox.exists()) {
-                FileUtils.copy(realpdb, sandbox);
-                mDataBlockFile = GSI_SANDBOX;
-            }
-            Slog.i(TAG, "PersistentDataBlock copy-on-write");
-            return new FileOutputStream(sandbox);
-        }
+    private FileChannel getBlockOutputChannel() throws IOException {
+        return new RandomAccessFile(mDataBlockFile, "rw").getChannel();
     }
 
     private boolean computeAndWriteDigestLocked() {
         byte[] digest = computeDigestLocked(null);
         if (digest != null) {
-            DataOutputStream outputStream;
+            FileChannel channel;
             try {
-                outputStream = new DataOutputStream(getBlockOutputStream());
+                channel = getBlockOutputChannel();
             } catch (IOException e) {
                 Slog.e(TAG, "partition not available?", e);
                 return false;
             }
 
             try {
-                outputStream.write(digest, 0, DIGEST_SIZE_BYTES);
-                outputStream.flush();
+                ByteBuffer buf = ByteBuffer.allocate(DIGEST_SIZE_BYTES);
+                buf.put(digest);
+                buf.flip();
+                channel.write(buf);
+                channel.force(true);
             } catch (IOException e) {
                 Slog.e(TAG, "failed to write block checksum", e);
                 return false;
-            } finally {
-                IoUtils.closeQuietly(outputStream);
             }
             return true;
         } else {
@@ -376,25 +374,18 @@
     }
 
     private void formatPartitionLocked(boolean setOemUnlockEnabled) {
-        DataOutputStream outputStream;
-        try {
-            outputStream = new DataOutputStream(getBlockOutputStream());
-        } catch (IOException e) {
-            Slog.e(TAG, "partition not available?", e);
-            return;
-        }
 
-        byte[] data = new byte[DIGEST_SIZE_BYTES];
         try {
-            outputStream.write(data, 0, DIGEST_SIZE_BYTES);
-            outputStream.writeInt(PARTITION_TYPE_MARKER);
-            outputStream.writeInt(0); // data size
-            outputStream.flush();
+            FileChannel channel = getBlockOutputChannel();
+            ByteBuffer buf = ByteBuffer.allocate(DIGEST_SIZE_BYTES + HEADER_SIZE);
+            buf.put(new byte[DIGEST_SIZE_BYTES]);
+            buf.putInt(PARTITION_TYPE_MARKER);
+            buf.putInt(0);
+            channel.write(buf);
+            channel.force(true);
         } catch (IOException e) {
             Slog.e(TAG, "failed to format block", e);
             return;
-        } finally {
-            IoUtils.closeQuietly(outputStream);
         }
 
         doSetOemUnlockEnabledLocked(setOemUnlockEnabled);
@@ -402,16 +393,9 @@
     }
 
     private void doSetOemUnlockEnabledLocked(boolean enabled) {
-        FileOutputStream outputStream;
-        try {
-            outputStream = getBlockOutputStream();
-        } catch (IOException e) {
-            Slog.e(TAG, "partition not available", e);
-            return;
-        }
 
         try {
-            FileChannel channel = outputStream.getChannel();
+            FileChannel channel = getBlockOutputChannel();
 
             channel.position(getBlockDeviceSize() - 1);
 
@@ -419,13 +403,12 @@
             data.put(enabled ? (byte) 1 : (byte) 0);
             data.flip();
             channel.write(data);
-            outputStream.flush();
+            channel.force(true);
         } catch (IOException e) {
             Slog.e(TAG, "unable to access persistent partition", e);
             return;
         } finally {
             SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
-            IoUtils.closeQuietly(outputStream);
         }
     }
 
@@ -479,35 +462,32 @@
                 return (int) -maxBlockSize;
             }
 
-            DataOutputStream outputStream;
+            FileChannel channel;
             try {
-                outputStream = new DataOutputStream(getBlockOutputStream());
+                channel = getBlockOutputChannel();
             } catch (IOException e) {
                 Slog.e(TAG, "partition not available?", e);
-                return -1;
+               return -1;
             }
 
-            ByteBuffer headerAndData = ByteBuffer.allocate(data.length + HEADER_SIZE);
+            ByteBuffer headerAndData = ByteBuffer.allocate(
+                                           data.length + HEADER_SIZE + DIGEST_SIZE_BYTES);
+            headerAndData.put(new byte[DIGEST_SIZE_BYTES]);
             headerAndData.putInt(PARTITION_TYPE_MARKER);
             headerAndData.putInt(data.length);
             headerAndData.put(data);
-
+            headerAndData.flip();
             synchronized (mLock) {
                 if (!mIsWritable) {
-                    IoUtils.closeQuietly(outputStream);
                     return -1;
                 }
 
                 try {
-                    byte[] checksum = new byte[DIGEST_SIZE_BYTES];
-                    outputStream.write(checksum, 0, DIGEST_SIZE_BYTES);
-                    outputStream.write(headerAndData.array());
-                    outputStream.flush();
+                    channel.write(headerAndData);
+                    channel.force(true);
                 } catch (IOException e) {
                     Slog.e(TAG, "failed writing to the persistent data block", e);
                     return -1;
-                } finally {
-                    IoUtils.closeQuietly(outputStream);
                 }
 
                 if (computeAndWriteDigestLocked()) {
@@ -567,17 +547,6 @@
         public void wipe() {
             enforceOemUnlockWritePermission();
 
-            if (mIsRunningDSU) {
-                File sandbox = new File(GSI_SANDBOX);
-                if (sandbox.exists()) {
-                    if (sandbox.delete()) {
-                        mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
-                    } else {
-                        Slog.e(TAG, "Failed to wipe sandbox persistent data block");
-                    }
-                }
-                return;
-            }
             synchronized (mLock) {
                 int ret = nativeWipe(mDataBlockFile);
 
@@ -735,28 +704,18 @@
         }
 
         private void writeDataBuffer(long offset, ByteBuffer dataBuffer) {
-            FileOutputStream outputStream;
-            try {
-                outputStream = getBlockOutputStream();
-            } catch (IOException e) {
-                Slog.e(TAG, "partition not available", e);
-                return;
-            }
             synchronized (mLock) {
                 if (!mIsWritable) {
-                    IoUtils.closeQuietly(outputStream);
                     return;
                 }
                 try {
-                    FileChannel channel = outputStream.getChannel();
+                    FileChannel channel = getBlockOutputChannel();
                     channel.position(offset);
                     channel.write(dataBuffer);
-                    outputStream.flush();
+                    channel.force(true);
                 } catch (IOException e) {
                     Slog.e(TAG, "unable to access persistent partition", e);
                     return;
-                } finally {
-                    IoUtils.closeQuietly(outputStream);
                 }
 
                 computeAndWriteDigestLocked();
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index b485fe8..611fe7a 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -29,6 +29,8 @@
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -155,7 +157,6 @@
         int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
         int phoneId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
-        int targetSdk;
 
         boolean matchTelephonyCallbackEvent(int event) {
             return (callback != null) && (this.eventList.contains(event));
@@ -228,6 +229,54 @@
                     TelecomManager.ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION, packageName,
                     userHandle));
         }
+
+        /**
+         * To check the SDK version for
+         * {@link android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener} should add
+         * {@link android.Manifest.permission#READ_PHONE_STATE} since Android 12.
+         * @noinspection ConstantConditions
+         */
+        public boolean isActiveDataSubIdReadPhoneStateEnforcedInPlatformCompat(String packageName,
+                UserHandle userHandle) {
+            return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
+                    REQUIRE_READ_PHONE_STATE_PERMISSION_FOR_ACTIVE_DATA_SUB_ID, packageName,
+                    userHandle));
+        }
+
+        /**
+         * To check the SDK version for
+         * {@link android.telephony.TelephonyCallback.CellInfoListener} should add
+         * {@link android.Manifest.permission#READ_PHONE_STATE} since Android 12.
+         * @noinspection ConstantConditions
+         */
+        public boolean isCellInfoReadPhoneStateEnforcedInPlatformCompat(String packageName,
+                UserHandle userHandle) {
+            return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
+                    REQUIRE_READ_PHONE_STATE_PERMISSION_FOR_CELL_INFO, packageName, userHandle));
+        }
+
+        /**
+         * To check the SDK version for
+         * {@link android.telephony.TelephonyCallback.DisplayInfoListener} should remove
+         * {@link android.Manifest.permission#READ_PHONE_STATE} since Android 12.
+         * @noinspection ConstantConditions
+         */
+        public boolean isDisplayInfoReadPhoneStateEnforcedInPlatformCompat(String packageName,
+                UserHandle userHandle) {
+            return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
+                    REQUIRE_READ_PHONE_STATE_PERMISSION_FOR_DISPLAY_INFO, packageName, userHandle));
+        }
+
+        /**
+         * Support backward compatibility for {@link android.telephony.TelephonyDisplayInfo}.
+         *
+         * @noinspection ConstantConditions
+         */
+        public boolean isDisplayInfoNrAdvancedSupported(String packageName,
+                UserHandle userHandle) {
+            return Binder.withCleanCallingIdentity(() -> CompatChanges.isChangeEnabled(
+                    DISPLAY_INFO_NR_ADVANCED_SUPPORTED, packageName, userHandle));
+        }
     }
 
     private final Context mContext;
@@ -346,6 +395,39 @@
      */
     private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>>
             mPreciseDataConnectionStates;
+    /**
+     * Support backward compatibility for {@link android.telephony.TelephonyDisplayInfo}.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+    private static final long DISPLAY_INFO_NR_ADVANCED_SUPPORTED = 181658987L;
+
+    /**
+     * To check the SDK version for
+     * {@link android.telephony.TelephonyCallback.DisplayInfoListener} should remove
+     * {@link android.Manifest.permission#READ_PHONE_STATE} since Android 12.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+    private static final long REQUIRE_READ_PHONE_STATE_PERMISSION_FOR_DISPLAY_INFO = 183164979L;
+
+    /**
+     * To check the SDK version for
+     * {@link android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener} should add
+     * {@link android.Manifest.permission#READ_PHONE_STATE} since Android 12.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+    private static final long REQUIRE_READ_PHONE_STATE_PERMISSION_FOR_ACTIVE_DATA_SUB_ID
+            = 182478738L;
+
+    /**
+     * To check the SDK version for {@link android.telephony.TelephonyCallback.CellInfoListener}
+     * should add {@link android.Manifest.permission#READ_PHONE_STATE} since Android 12.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+    private static final long REQUIRE_READ_PHONE_STATE_PERMISSION_FOR_CELL_INFO = 184323934L;
 
     private static final Set<Integer> REQUIRE_PRECISE_PHONE_STATE_PERMISSION;
     static {
@@ -379,13 +461,46 @@
                 || events.contains(TelephonyCallback.EVENT_BARRING_INFO_CHANGED);
     }
 
-    private boolean isPhoneStatePermissionRequired(Set<Integer> events, int targetSdk) {
-        return events.contains(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
+    private boolean isPhoneStatePermissionRequired(Set<Integer> events, String callingPackage,
+            UserHandle userHandle) {
+        if (events.contains(TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
                 || events.contains(TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
-                || events.contains(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
-                || events.contains(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)
-                || (targetSdk <= android.os.Build.VERSION_CODES.R ? events.contains(
-                TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED) : false);
+                || events.contains(TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)) {
+            return true;
+        }
+
+        // Only check READ_PHONE_STATE for CALL_STATE_CHANGED for Android 12 or above.
+        if ((events.contains(TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED)
+                || events.contains(TelephonyCallback.EVENT_CALL_STATE_CHANGED))
+                && mConfigurationProvider.isCallStateReadPhoneStateEnforcedInPlatformCompat(
+                        callingPackage, userHandle)) {
+            return true;
+        }
+
+        // Only check READ_PHONE_STATE for ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED for Android 12
+        // or above.
+        if (events.contains(TelephonyCallback.EVENT_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGED)
+                && mConfigurationProvider.isActiveDataSubIdReadPhoneStateEnforcedInPlatformCompat(
+                        callingPackage, userHandle)) {
+            return true;
+        }
+
+        // Only check READ_PHONE_STATE for CELL_INFO_CHANGED for Android 12 or above.
+        if (events.contains(TelephonyCallback.EVENT_CELL_INFO_CHANGED)
+                && mConfigurationProvider.isCellInfoReadPhoneStateEnforcedInPlatformCompat(
+                        callingPackage, userHandle)) {
+            return true;
+        }
+
+        // Only check READ_PHONE_STATE for DISPLAY_INFO_CHANGED for Android 11 or older.
+        // READ_PHONE_STATE is not required anymore after Android 12.
+        if (events.contains(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)
+                && !mConfigurationProvider.isDisplayInfoReadPhoneStateEnforcedInPlatformCompat(
+                        callingPackage, userHandle)) {
+            return true;
+        }
+
+        return false;
     }
 
     private boolean isPrecisePhoneStatePermissionRequired(Set<Integer> events) {
@@ -902,12 +1017,11 @@
             remove(callback.asBinder());
             return;
         }
-        int callerTargetSdk = TelephonyPermissions.getTargetSdk(mContext, callingPackage);
+
         // Checks permission and throws SecurityException for disallowed operations. For pre-M
         // apps whose runtime permission has been revoked, we return immediately to skip sending
         // events to the app without crashing it.
-        if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId,
-                "listen", callerTargetSdk)) {
+        if (!checkListenerPermission(events, subId, callingPackage, callingFeatureId, "listen")) {
             return;
         }
 
@@ -940,7 +1054,6 @@
             }
             r.phoneId = phoneId;
             r.eventList = events;
-            r.targetSdk = callerTargetSdk;
 
             if (DBG) {
                 log("listen:  Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
@@ -1773,7 +1886,8 @@
                             TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)
                             && idMatchWithoutDefaultPhoneCheck(r.subId, subId)) {
                         try {
-                            if (r.targetSdk <= android.os.Build.VERSION_CODES.R) {
+                            if (!mConfigurationProvider.isDisplayInfoNrAdvancedSupported(
+                                    r.callingPackage, Binder.getCallingUserHandle())) {
                                 telephonyDisplayInfo =
                                         getBackwardCompatibleTelephonyDisplayInfo(
                                                 telephonyDisplayInfo);
@@ -2922,7 +3036,7 @@
     }
 
     private boolean checkListenerPermission(Set<Integer> events, int subId, String callingPackage,
-            @Nullable String callingFeatureId, String message, int targetSdk) {
+            @Nullable String callingFeatureId, String message) {
         LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder =
                 new LocationAccessPolicy.LocationPermissionQuery.Builder()
                         .setCallingPackage(callingPackage)
@@ -2958,26 +3072,13 @@
             }
         }
 
-        if (isPhoneStatePermissionRequired(events, targetSdk)) {
+        if (isPhoneStatePermissionRequired(events, callingPackage, Binder.getCallingUserHandle())) {
             if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
                     mContext, subId, callingPackage, callingFeatureId, message)) {
                 isPermissionCheckSuccessful = false;
             }
         }
 
-        // Only check READ_PHONE_STATE for CALL_STATE_CHANGED for API 31+.
-        if (mConfigurationProvider.isCallStateReadPhoneStateEnforcedInPlatformCompat(callingPackage,
-                Binder.getCallingUserHandle())) {
-            if (events.contains(TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED)
-                    || events.contains(TelephonyCallback.EVENT_CALL_STATE_CHANGED)) {
-                if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                        mContext, subId, callingPackage, callingFeatureId, message)) {
-                    throw new SecurityException("CALL_STATE_CHANGED event requires "
-                            + "READ_PHONE_STATE");
-                }
-            }
-        }
-
         if (isPrecisePhoneStatePermissionRequired(events)) {
             // check if calling app has either permission READ_PRECISE_PHONE_STATE
             // or with carrier privileges
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index c1ab6cc..c7f2f43 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -78,6 +78,7 @@
 import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.ForegroundServiceDidNotStartInTimeException;
 import android.app.ForegroundServiceStartNotAllowedException;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
@@ -4988,9 +4989,10 @@
     }
 
     void serviceForegroundCrash(ProcessRecord app, CharSequence serviceRecord) {
-        mAm.crashApplication(app.uid, app.getPid(), app.info.packageName, app.userId,
+        mAm.crashApplicationWithType(app.uid, app.getPid(), app.info.packageName, app.userId,
                 "Context.startForegroundService() did not then call Service.startForeground(): "
-                    + serviceRecord, false /*force*/);
+                    + serviceRecord, false /*force*/,
+                ForegroundServiceDidNotStartInTimeException.TYPE_ID);
     }
 
     void scheduleServiceTimeoutLocked(ProcessRecord proc) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7dc39b3..ac042a7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -179,6 +179,7 @@
 import android.app.ProcessMemoryState;
 import android.app.ProfilerInfo;
 import android.app.PropertyInvalidatedCache;
+import android.app.RemoteServiceException;
 import android.app.SyncNotedAppOp;
 import android.app.WaitResult;
 import android.app.backup.BackupManager.OperationType;
@@ -2935,6 +2936,13 @@
     @Override
     public void crashApplication(int uid, int initialPid, String packageName, int userId,
             String message, boolean force) {
+        crashApplicationWithType(uid, initialPid, packageName, userId, message, force,
+                RemoteServiceException.TYPE_ID);
+    }
+
+    @Override
+    public void crashApplicationWithType(int uid, int initialPid, String packageName, int userId,
+            String message, boolean force, int exceptionTypeId) {
         if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
                 != PackageManager.PERMISSION_GRANTED) {
             String msg = "Permission Denial: crashApplication() from pid="
@@ -2947,7 +2955,7 @@
 
         synchronized(this) {
             mAppErrors.scheduleAppCrashLocked(uid, initialPid, packageName, userId,
-                    message, force);
+                    message, force, exceptionTypeId);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 3602f44..5a59eabd 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -486,7 +486,7 @@
      * @param message
      */
     void scheduleAppCrashLocked(int uid, int initialPid, String packageName, int userId,
-            String message, boolean force) {
+            String message, boolean force, int exceptionTypeId) {
         ProcessRecord proc = null;
 
         // Figure out which process to kill.  We don't trust that initialPid
@@ -518,7 +518,7 @@
             return;
         }
 
-        proc.scheduleCrashLocked(message);
+        proc.scheduleCrashLocked(message, exceptionTypeId);
         if (force) {
             // If the app is responsive, the scheduled crash will happen as expected
             // and then the delayed summary kill will be a no-op.
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index c8721cc..960cc42 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1328,6 +1328,9 @@
                         || uidRec.getSetProcState() == PROCESS_STATE_NONEXISTENT) {
                     uidChange |= isCached ? UidRecord.CHANGE_CACHED : UidRecord.CHANGE_UNCACHED;
                 }
+                if (uidRec.getSetCapability() != uidRec.getCurCapability()) {
+                    uidChange |= UidRecord.CHANGE_CAPABILITY;
+                }
                 uidRec.setSetProcState(uidRec.getCurProcState());
                 uidRec.setSetCapability(uidRec.getCurCapability());
                 uidRec.setSetAllowListed(uidRec.isCurAllowListed());
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index ed136af..9e94d4a 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -26,6 +26,7 @@
 import android.app.ApplicationExitInfo.Reason;
 import android.app.ApplicationExitInfo.SubReason;
 import android.app.IApplicationThread;
+import android.app.RemoteServiceException;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ProcessInfo;
 import android.content.pm.VersionedPackage;
@@ -949,6 +950,21 @@
 
     @GuardedBy("mService")
     void scheduleCrashLocked(String message) {
+        scheduleCrashLocked(message, RemoteServiceException.TYPE_ID);
+    }
+
+    /**
+     * Let an app process throw an exception on a binder thread, which typically crashes the
+     * process, unless it has an unhandled exception handler.
+     *
+     * See {@link ActivityThread#throwRemoteServiceException}.
+     *
+     * @param message exception message
+     * @param exceptionTypeId ID defined in {@link android.app.RemoteServiceException} or one
+     *                        of its subclasses.
+     */
+    @GuardedBy("mService")
+    void scheduleCrashLocked(String message, int exceptionTypeId) {
         // Checking killedbyAm should keep it from showing the crash dialog if the process
         // was already dead for a good / normal reason.
         if (!mKilledByAm) {
@@ -959,7 +975,7 @@
                 }
                 final long ident = Binder.clearCallingIdentity();
                 try {
-                    mThread.scheduleCrash(message);
+                    mThread.scheduleCrash(message, exceptionTypeId);
                 } catch (RemoteException e) {
                     // If it's already dead our work is done. If it's wedged just kill it.
                     // We won't get the crash dialog or the error reporting.
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index df65ee6..c1bfe25 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -147,6 +147,9 @@
         if ((currentChange & UidRecord.CHANGE_GONE) != 0) {
             currentChange &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED);
         }
+        if ((pendingChange & UidRecord.CHANGE_CAPABILITY) != 0) {
+            currentChange |= UidRecord.CHANGE_CAPABILITY;
+        }
         return currentChange;
     }
 
@@ -285,12 +288,9 @@
                         reg.mLastProcStates.delete(item.uid);
                     }
                 } else {
+                    boolean doReport = false;
                     if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
-                        if (DEBUG_UID_OBSERVERS) {
-                            Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid
-                                    + ": " + item.procState + ": " + item.capability);
-                        }
-                        boolean doReport = true;
+                        doReport = true;
                         if (reg.mCutpoint >= ActivityManager.MIN_PROCESS_STATE) {
                             final int lastState = reg.mLastProcStates.get(item.uid,
                                     ActivityManager.PROCESS_STATE_UNKNOWN);
@@ -302,13 +302,20 @@
                                 doReport = item.procState != PROCESS_STATE_NONEXISTENT;
                             }
                         }
-                        if (doReport) {
-                            if (reg.mLastProcStates != null) {
-                                reg.mLastProcStates.put(item.uid, item.procState);
-                            }
-                            observer.onUidStateChanged(item.uid, item.procState,
-                                    item.procStateSeq, item.capability);
+                    }
+                    if ((reg.mWhich & ActivityManager.UID_OBSERVER_CAPABILITY) != 0) {
+                        doReport |= (change & UidRecord.CHANGE_CAPABILITY) != 0;
+                    }
+                    if (doReport) {
+                        if (DEBUG_UID_OBSERVERS) {
+                            Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid
+                                    + ": " + item.procState + ": " + item.capability);
                         }
+                        if (reg.mLastProcStates != null) {
+                            reg.mLastProcStates.put(item.uid, item.procState);
+                        }
+                        observer.onUidStateChanged(item.uid, item.procState,
+                                item.procStateSeq, item.capability);
                     }
                 }
                 final int duration = (int) (SystemClock.uptimeMillis() - start);
@@ -428,12 +435,14 @@
                 ActivityManager.UID_OBSERVER_ACTIVE,
                 ActivityManager.UID_OBSERVER_GONE,
                 ActivityManager.UID_OBSERVER_PROCSTATE,
+                ActivityManager.UID_OBSERVER_CAPABILITY,
         };
         private static final int[] PROTO_ENUMS = new int[]{
                 ActivityManagerProto.UID_OBSERVER_FLAG_IDLE,
                 ActivityManagerProto.UID_OBSERVER_FLAG_ACTIVE,
                 ActivityManagerProto.UID_OBSERVER_FLAG_GONE,
                 ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE,
+                ActivityManagerProto.UID_OBSERVER_FLAG_CAPABILITY,
         };
 
         UidObserverRegistration(int uid, @NonNull String pkg, int which, int cutpoint) {
@@ -462,6 +471,9 @@
             if ((mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) {
                 pw.print(" GONE");
             }
+            if ((mWhich & ActivityManager.UID_OBSERVER_CAPABILITY) != 0) {
+                pw.print(" CAP");
+            }
             if ((mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
                 pw.print(" STATE");
                 pw.print(" (cut=");
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 2fb662f..4ba59fa 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -122,6 +122,7 @@
     static final int CHANGE_ACTIVE = 1<<2;
     static final int CHANGE_CACHED = 1<<3;
     static final int CHANGE_UNCACHED = 1<<4;
+    static final int CHANGE_CAPABILITY = 1<<5;
 
     // Keep the enum lists in sync
     private static int[] ORIG_ENUMS = new int[] {
@@ -130,6 +131,7 @@
             CHANGE_ACTIVE,
             CHANGE_CACHED,
             CHANGE_UNCACHED,
+            CHANGE_CAPABILITY,
     };
     private static int[] PROTO_ENUMS = new int[] {
             UidRecordProto.CHANGE_GONE,
@@ -137,6 +139,7 @@
             UidRecordProto.CHANGE_ACTIVE,
             UidRecordProto.CHANGE_CACHED,
             UidRecordProto.CHANGE_UNCACHED,
+            UidRecordProto.CHANGE_CAPABILITY,
     };
 
     // UidObserverController is the only thing that should modify this.
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 143a1cf..a5d0e72 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -93,7 +93,6 @@
 import android.util.EventLog;
 import android.util.IntArray;
 import android.util.Pair;
-import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
@@ -110,6 +109,7 @@
 import com.android.server.am.UserState.KeyEvictedCallback;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.UserManagerService;
+import com.android.server.utils.Slogf;
 import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerService;
@@ -510,7 +510,7 @@
                         elapsedTimeMs);
                 final long maxElapsedTimeMs = 120_000;
                 if (elapsedTimeMs > maxElapsedTimeMs) {
-                    Slog.wtf("SystemServerTiming",
+                    Slogf.wtf("SystemServerTiming",
                             "finishUserBoot took too long. elapsedTimeMs=" + elapsedTimeMs);
                 }
             }
@@ -533,12 +533,12 @@
             final UserInfo parent = mInjector.getUserManager().getProfileParent(userId);
             if (parent != null
                     && isUserRunning(parent.id, ActivityManager.FLAG_AND_UNLOCKED)) {
-                Slog.d(TAG, "User " + userId + " (parent " + parent.id
+                Slogf.d(TAG, "User " + userId + " (parent " + parent.id
                         + "): attempting unlock because parent is unlocked");
                 maybeUnlockUser(userId);
             } else {
                 String parentId = (parent == null) ? "<null>" : String.valueOf(parent.id);
-                Slog.d(TAG, "User " + userId + " (parent " + parentId
+                Slogf.d(TAG, "User " + userId + " (parent " + parentId
                         + "): delaying unlock because parent is locked");
             }
         } else {
@@ -587,7 +587,7 @@
         // Call onBeforeUnlockUser on a worker thread that allows disk I/O
         FgThread.getHandler().post(() -> {
             if (!StorageManager.isUserKeyUnlocked(userId)) {
-                Slog.w(TAG, "User key got locked unexpectedly, leaving user locked.");
+                Slogf.w(TAG, "User key got locked unexpectedly, leaving user locked.");
                 return;
             }
             mInjector.getUserManager().onBeforeUnlockUser(userId);
@@ -716,7 +716,7 @@
 
         Runnable initializeUser = () -> mInjector.getUserManager().makeInitialized(userInfo.id);
         if (!userInfo.isInitialized()) {
-            Slog.d(TAG, "Initializing user #" + userId);
+            Slogf.d(TAG, "Initializing user #" + userId);
             if (userInfo.preCreated) {
                 initializeUser.run();
             } else if (userId != UserHandle.USER_SYSTEM) {
@@ -739,7 +739,7 @@
         }
 
         if (userInfo.preCreated) {
-            Slog.i(TAG, "Stopping pre-created user " + userInfo.toFullString());
+            Slogf.i(TAG, "Stopping pre-created user " + userInfo.toFullString());
             // Pre-created user was started right after creation so services could properly
             // intialize it; it should be stopped right away as it's not really a "real" user.
             stopUser(userInfo.id, /* force= */ true, /* allowDelayedLocking= */ false,
@@ -752,7 +752,7 @@
 
         mHandler.obtainMessage(USER_UNLOCKED_MSG, userId, 0).sendToTarget();
 
-        Slog.i(TAG, "Posting BOOT_COMPLETED user #" + userId);
+        Slogf.i(TAG, "Posting BOOT_COMPLETED user #" + userId);
         // Do not report secondary users, runtime restarts or first boot/upgrade
         if (userId == UserHandle.USER_SYSTEM
                 && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
@@ -777,7 +777,7 @@
                         public void performReceive(Intent intent, int resultCode, String data,
                                 Bundle extras, boolean ordered, boolean sticky, int sendingUser)
                                         throws RemoteException {
-                            Slog.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u"
+                            Slogf.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u"
                                     + userId);
                             mBootCompleted = true;
                         }
@@ -863,11 +863,12 @@
         for (int i = 0; i < usersToStop.length; i++) {
             int relatedUserId = usersToStop[i];
             if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLU(relatedUserId)) {
-                if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked cannot stop related user "
-                        + relatedUserId);
+                if (DEBUG_MU) {
+                    Slogf.i(TAG, "stopUsersLocked cannot stop related user " + relatedUserId);
+                }
                 // We still need to stop the requested user if it's a force stop.
                 if (force) {
-                    Slog.i(TAG,
+                    Slogf.i(TAG,
                             "Force stop user " + userId + ". Related users will not be stopped");
                     stopSingleUserLU(userId, allowDelayedLocking, stopUserCallback,
                             keyEvictedCallback);
@@ -876,7 +877,7 @@
                 return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
             }
         }
-        if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop));
+        if (DEBUG_MU) Slogf.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop));
         for (int userIdToStop : usersToStop) {
             stopSingleUserLU(userIdToStop, allowDelayedLocking,
                     userIdToStop == userId ? stopUserCallback : null,
@@ -906,7 +907,7 @@
     private void stopSingleUserLU(final int userId, boolean allowDelayedLocking,
             final IStopUserCallback stopUserCallback,
             KeyEvictedCallback keyEvictedCallback) {
-        if (DEBUG_MU) Slog.i(TAG, "stopSingleUserLocked userId=" + userId);
+        if (DEBUG_MU) Slogf.i(TAG, "stopSingleUserLocked userId=" + userId);
         final UserState uss = mStartedUsers.get(userId);
         if (uss == null) {  // User is not started
             // If mDelayUserDataLocking is set and allowDelayedLocking is not set, we need to lock
@@ -917,7 +918,7 @@
             // and no further action is necessary.
             if (mDelayUserDataLocking) {
                 if (allowDelayedLocking && keyEvictedCallback != null) {
-                    Slog.wtf(TAG, "allowDelayedLocking set with KeyEvictedCallback, ignore it"
+                    Slogf.wtf(TAG, "allowDelayedLocking set with KeyEvictedCallback, ignore it"
                             + " and lock user:" + userId, new RuntimeException());
                     allowDelayedLocking = false;
                 }
@@ -1043,7 +1044,7 @@
     void finishUserStopped(UserState uss, boolean allowDelayedLocking) {
         final int userId = uss.mHandle.getIdentifier();
         if (DEBUG_MU) {
-            Slog.i(TAG, "finishUserStopped(%d): allowDelayedLocking=%b", userId,
+            Slogf.i(TAG, "finishUserStopped(%d): allowDelayedLocking=%b", userId,
                     allowDelayedLocking);
         }
 
@@ -1067,7 +1068,7 @@
                 mUserLru.remove(Integer.valueOf(userId));
                 updateStartedUserArrayLU();
                 if (allowDelayedLocking && !keyEvictedCallbacks.isEmpty()) {
-                    Slog.wtf(TAG,
+                    Slogf.wtf(TAG,
                             "Delayed locking enabled while KeyEvictedCallbacks not empty, userId:"
                                     + userId + " callbacks:" + keyEvictedCallbacks);
                     allowDelayedLocking = false;
@@ -1118,7 +1119,7 @@
         FgThread.getHandler().post(() -> {
             synchronized (mLock) {
                 if (mStartedUsers.get(userId) != null) {
-                    Slog.w(TAG, "User was restarted, skipping key eviction");
+                    Slogf.w(TAG, "User was restarted, skipping key eviction");
                     return;
                 }
             }
@@ -1156,10 +1157,10 @@
             if (totalUnlockedUsers > mMaxRunningUsers) { // should lock a user
                 userIdToLock = mLastActiveUsers.get(mLastActiveUsers.size() - 1);
                 mLastActiveUsers.remove(mLastActiveUsers.size() - 1);
-                Slog.i(TAG, "finishUserStopped, stopping user:" + userId
+                Slogf.i(TAG, "finishUserStopped, stopping user:" + userId
                         + " lock user:" + userIdToLock);
             } else {
-                Slog.i(TAG, "finishUserStopped, user:" + userId + ", skip locking");
+                Slogf.i(TAG, "finishUserStopped, user:" + userId + ", skip locking");
                 // do not lock
                 userIdToLock = UserHandle.USER_NULL;
             }
@@ -1196,7 +1197,7 @@
     }
 
     private void forceStopUser(@UserIdInt int userId, String reason) {
-        if (DEBUG_MU) Slog.i(TAG, "forceStopUser(%d): %s", userId, reason);
+        if (DEBUG_MU) Slogf.i(TAG, "forceStopUser(%d): %s", userId, reason);
         mInjector.activityManagerForceStopPackage(userId, reason);
         if (mInjector.getUserManager().isPreCreated(userId)) {
             // Don't fire intent for precreated.
@@ -1227,7 +1228,7 @@
      * Stops the guest or ephemeral user if it has gone to the background.
      */
     private void stopGuestOrEphemeralUserIfBackground(int oldUserId) {
-        if (DEBUG_MU) Slog.i(TAG, "Stop guest or ephemeral user if background: " + oldUserId);
+        if (DEBUG_MU) Slogf.i(TAG, "Stop guest or ephemeral user if background: " + oldUserId);
         synchronized(mLock) {
             UserState oldUss = mStartedUsers.get(oldUserId);
             if (oldUserId == UserHandle.USER_SYSTEM || oldUserId == mCurrentUserId || oldUss == null
@@ -1265,7 +1266,7 @@
 
     void startProfiles() {
         int currentUserId = getCurrentUserId();
-        if (DEBUG_MU) Slog.i(TAG, "startProfilesLocked");
+        if (DEBUG_MU) Slogf.i(TAG, "startProfilesLocked");
         List<UserInfo> profiles = mInjector.getUserManager().getProfiles(
                 currentUserId, false /* enabledOnly */);
         List<UserInfo> profilesToStart = new ArrayList<>(profiles.size());
@@ -1281,7 +1282,7 @@
             startUser(profilesToStart.get(i).id, /* foreground= */ false);
         }
         if (i < profilesToStartSize) {
-            Slog.w(TAG, "More profiles than MAX_RUNNING_USERS");
+            Slogf.w(TAG, "More profiles than MAX_RUNNING_USERS");
         }
     }
 
@@ -1309,7 +1310,7 @@
         }
 
         if (!userInfo.isEnabled()) {
-            Slog.w(TAG, "Cannot start disabled profile #" + userId);
+            Slogf.w(TAG, "Cannot start disabled profile #" + userId);
             return false;
         }
 
@@ -1375,7 +1376,9 @@
 
     private boolean startUserInternal(@UserIdInt int userId, boolean foreground,
             @Nullable IProgressListener unlockListener, @NonNull TimingsTraceAndSlog t) {
-        if (DEBUG_MU) Slog.i(TAG, "Starting user %d%s", userId, foreground ? " in foreground" : "");
+        if (DEBUG_MU) {
+            Slogf.i(TAG, "Starting user %d%s", userId, foreground ? " in foreground" : "");
+        }
         EventLog.writeEvent(EventLogTags.UC_START_USER_INTERNAL, userId);
 
         final int callingUid = Binder.getCallingUid();
@@ -1387,7 +1390,7 @@
             if (oldUserId == userId) {
                 final UserState state = getStartedUserState(userId);
                 if (state == null) {
-                    Slog.wtf(TAG, "Current user has no UserState");
+                    Slogf.wtf(TAG, "Current user has no UserState");
                     // continue starting.
                 } else {
                     if (userId == UserHandle.USER_SYSTEM && state.state == STATE_BOOTING) {
@@ -1417,16 +1420,16 @@
             t.traceEnd();
 
             if (userInfo == null) {
-                Slog.w(TAG, "No user info for user #" + userId);
+                Slogf.w(TAG, "No user info for user #" + userId);
                 return false;
             }
             if (foreground && userInfo.isProfile()) {
-                Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
+                Slogf.w(TAG, "Cannot switch to User #" + userId + ": not a full user");
                 return false;
             }
 
             if (foreground && userInfo.preCreated) {
-                Slog.w(TAG, "Cannot start pre-created user #" + userId + " as foreground");
+                Slogf.w(TAG, "Cannot start pre-created user #" + userId + " as foreground");
                 return false;
             }
 
@@ -1454,7 +1457,7 @@
                     needStart = true;
                     updateUmState = true;
                 } else if (uss.state == UserState.STATE_SHUTDOWN && !isCallingOnHandlerThread()) {
-                    Slog.i(TAG, "User #" + userId
+                    Slogf.i(TAG, "User #" + userId
                             + " is shutting down - will start after full stop");
                     mHandler.post(() -> startUser(userId, foreground, unlockListener));
                     t.traceEnd(); // updateStartedUserArrayStarting
@@ -1663,7 +1666,7 @@
                 // We always want to unlock user storage, even user is not started yet
                 storageManager.unlockUserKey(userId, userInfo.serialNumber, token, secret);
             } catch (RemoteException | RuntimeException e) {
-                Slog.w(TAG, "Failed to unlock: " + e.getMessage());
+                Slogf.w(TAG, "Failed to unlock: " + e.getMessage());
             }
         }
         synchronized (mLock) {
@@ -1699,7 +1702,7 @@
         for (int testUserId : userIds) {
             final UserInfo parent = mInjector.getUserManager().getProfileParent(testUserId);
             if (parent != null && parent.id == userId && testUserId != userId) {
-                Slog.d(TAG, "User " + testUserId + " (parent " + parent.id
+                Slogf.d(TAG, "User " + testUserId + " (parent " + parent.id
                         + "): attempting unlock because parent was just unlocked");
                 maybeUnlockUser(testUserId);
             }
@@ -1714,25 +1717,25 @@
         int currentUserId = getCurrentUserId();
         UserInfo targetUserInfo = getUserInfo(targetUserId);
         if (targetUserId == currentUserId) {
-            Slog.i(TAG, "user #" + targetUserId + " is already the current user");
+            Slogf.i(TAG, "user #" + targetUserId + " is already the current user");
             return true;
         }
         if (targetUserInfo == null) {
-            Slog.w(TAG, "No user info for user #" + targetUserId);
+            Slogf.w(TAG, "No user info for user #" + targetUserId);
             return false;
         }
         if (!targetUserInfo.supportsSwitchTo()) {
-            Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
+            Slogf.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
             return false;
         }
         if (targetUserInfo.isProfile()) {
-            Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user");
+            Slogf.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user");
             return false;
         }
         boolean userSwitchUiEnabled;
         synchronized (mLock) {
             if (!mInitialized) {
-                Slog.e(TAG, "Cannot switch to User #" + targetUserId
+                Slogf.e(TAG, "Cannot switch to User #" + targetUserId
                         + ": UserController not ready yet");
                 return false;
             }
@@ -1809,13 +1812,13 @@
             boolean disallowRunInBg = hasRestriction || shouldStopBackgroundUsersOnSwitch();
             if (!disallowRunInBg) {
                 if (DEBUG_MU) {
-                    Slog.i(TAG, "stopBackgroundUsersIfEnforced() NOT stopping %d and related users",
-                            oldUserId);
+                    Slogf.i(TAG, "stopBackgroundUsersIfEnforced() NOT stopping %d and related "
+                            + "users", oldUserId);
                 }
                 return;
             }
             if (DEBUG_MU) {
-                Slog.i(TAG, "stopBackgroundUsersIfEnforced() stopping %d and related users",
+                Slogf.i(TAG, "stopBackgroundUsersIfEnforced() stopping %d and related users",
                         oldUserId);
             }
             stopUsersLU(oldUserId, /* force= */ false, /* allowDelayedLocking= */ true,
@@ -1825,7 +1828,7 @@
 
     private void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) {
         synchronized (mLock) {
-            Slog.e(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
+            Slogf.e(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId);
             mTimeoutUserSwitchCallbacks = mCurWaitingUserSwitchCallbacks;
             mHandler.removeMessages(USER_SWITCH_CALLBACKS_TIMEOUT_MSG);
             sendContinueUserSwitchLU(uss, oldUserId, newUserId);
@@ -1838,7 +1841,7 @@
     private void timeoutUserSwitchCallbacks(int oldUserId, int newUserId) {
         synchronized (mLock) {
             if (mTimeoutUserSwitchCallbacks != null && !mTimeoutUserSwitchCallbacks.isEmpty()) {
-                Slog.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId
+                Slogf.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId
                         + ". Observers that didn't respond: " + mTimeoutUserSwitchCallbacks);
                 mTimeoutUserSwitchCallbacks = null;
             }
@@ -1870,10 +1873,10 @@
                             synchronized (mLock) {
                                 long delay = SystemClock.elapsedRealtime() - dispatchStartedTime;
                                 if (delay > USER_SWITCH_TIMEOUT_MS) {
-                                    Slog.e(TAG, "User switch timeout: observer " + name
+                                    Slogf.e(TAG, "User switch timeout: observer " + name
                                             + " sent result after " + delay + " ms");
                                 } else if (delay > USER_SWITCH_WARNING_TIMEOUT_MS) {
-                                    Slog.w(TAG, "User switch slowed down by observer " + name
+                                    Slogf.w(TAG, "User switch slowed down by observer " + name
                                             + ": result sent after " + delay + " ms");
                                 }
 
@@ -2091,7 +2094,7 @@
                         }
                     }
                     String msg = builder.toString();
-                    Slog.w(TAG, msg);
+                    Slogf.w(TAG, msg);
                     throw new SecurityException(msg);
                 }
             }
@@ -2318,7 +2321,7 @@
                     + Binder.getCallingPid()
                     + ", uid=" + Binder.getCallingUid()
                     + " requires " + INTERACT_ACROSS_USERS;
-            Slog.w(TAG, msg);
+            Slogf.w(TAG, msg);
             throw new SecurityException(msg);
         }
     }
@@ -2421,7 +2424,7 @@
                     + "() from pid=" + Binder.getCallingPid()
                     + ", uid=" + Binder.getCallingUid()
                     + " requires " + permission;
-            Slog.w(TAG, msg);
+            Slogf.w(TAG, msg);
             throw new SecurityException(msg);
         }
     }
@@ -2732,7 +2735,7 @@
                      * journey so no need to create a new journey for user start.
                      */
                     if (DEBUG_MU) {
-                        Slog.d(TAG, journey + " not logged as it is expected to be part of "
+                        Slogf.d(TAG, journey + " not logged as it is expected to be part of "
                                 + userJourneySession.mJourney);
                     }
                     return;
@@ -2755,7 +2758,7 @@
             }
 
             if (DEBUG_MU) {
-                Slog.d(TAG,
+                Slogf.d(TAG,
                         "Starting a new journey: " + journey + " with session id: " + newSessionId);
             }
 
@@ -2787,7 +2790,7 @@
         synchronized (mUserIdToUserJourneyMap) {
             final UserJourneySession userJourneySession = mUserIdToUserJourneyMap.get(userId);
             if (userJourneySession == null || userJourneySession.mSessionId == INVALID_SESSION_ID) {
-                Slog.w(TAG, "UserLifecycleEvent " + event
+                Slogf.w(TAG, "UserLifecycleEvent " + event
                         + " received without an active userJourneySession.");
                 return;
             }
@@ -2870,13 +2873,13 @@
         private volatile long mUnlockStarted;
         @Override
         public void onStarted(int id, Bundle extras) throws RemoteException {
-            Slog.d(TAG, "Started unlocking user " + id);
+            Slogf.d(TAG, "Started unlocking user " + id);
             mUnlockStarted = SystemClock.uptimeMillis();
         }
 
         @Override
         public void onProgress(int id, int progress, Bundle extras) throws RemoteException {
-            Slog.d(TAG, "Unlocking user " + id + " progress " + progress);
+            Slogf.d(TAG, "Unlocking user " + id + " progress " + progress);
         }
 
         @Override
@@ -3061,7 +3064,7 @@
                 // config_customUserSwitchUi is set to true on Automotive as CarSystemUI is
                 // responsible to show the UI; OEMs should not change that, but if they do, we
                 // should at least warn the user...
-                Slog.w(TAG, "Showing user switch dialog on UserController, it could cause a race "
+                Slogf.w(TAG, "Showing user switch dialog on UserController, it could cause a race "
                         + "condition if it's shown by CarSystemUI as well");
             }
             final Dialog d = new UserSwitchingDialog(mService, mService.mContext, fromUser,
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index ef43bc5..262cb36 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -259,6 +259,21 @@
         this.confirmationRequested = confirmationRequested;
     }
 
+    private Pair<BiometricSensor, Integer> calculateErrorByPriority() {
+        // If the caller requested STRONG, and the device contains both STRONG and non-STRONG
+        // sensors, prioritize BIOMETRIC_NOT_ENROLLED over the weak sensor's
+        // BIOMETRIC_INSUFFICIENT_STRENGTH error. Pretty sure we can always prioritize
+        // BIOMETRIC_NOT_ENROLLED over any other error (unless of course its calculation is
+        // wrong, in which case we should fix that instead).
+        for (Pair<BiometricSensor, Integer> pair : ineligibleSensors) {
+            if (pair.second == BIOMETRIC_NOT_ENROLLED) {
+                return pair;
+            }
+        }
+
+        return ineligibleSensors.get(0);
+    }
+
     /**
      * With {@link PreAuthInfo} generated with the requested authenticators from the public API
      * surface, combined with the actual sensor/credential and user/system settings, calculate the
@@ -281,8 +296,9 @@
             } else {
                 // Pick the first sensor error if it exists
                 if (!ineligibleSensors.isEmpty()) {
-                    modality |= ineligibleSensors.get(0).first.modality;
-                    status = ineligibleSensors.get(0).second;
+                    final Pair<BiometricSensor, Integer> pair = calculateErrorByPriority();
+                    modality |= pair.first.modality;
+                    status = pair.second;
                 } else {
                     modality |= TYPE_CREDENTIAL;
                     status = CREDENTIAL_NOT_ENROLLED;
@@ -297,8 +313,9 @@
             } else {
                 // Pick the first sensor error if it exists
                 if (!ineligibleSensors.isEmpty()) {
-                    modality |= ineligibleSensors.get(0).first.modality;
-                    status = ineligibleSensors.get(0).second;
+                    final Pair<BiometricSensor, Integer> pair = calculateErrorByPriority();
+                    modality |= pair.first.modality;
+                    status = pair.second;
                 } else {
                     modality |= TYPE_NONE;
                     status = BIOMETRIC_NO_HARDWARE;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 10f6948f..919d25c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -258,18 +258,6 @@
         return super.handleUserControlPressed(message);
     }
 
-    @Override
-    protected void wakeUpIfActiveSource() {
-        if (!isActiveSource()) {
-            return;
-        }
-        // Wake up the device if the power is in standby mode, or its screen is off -
-        // which can happen if the device is holding a partial lock.
-        if (mService.isPowerStandbyOrTransient() || !mService.getPowerManager().isScreenOn()) {
-            mService.wakeUp();
-        }
-    }
-
     @ServiceThreadOnly
     @Constants.HandleMessageResult
     protected int handleSetMenuLanguage(HdmiCecMessage message) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
index 979a1d4..c001c40 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -292,7 +292,6 @@
 
     // Only source devices that react to routing control messages should implement
     // this method (e.g. a TV with built in switch).
-    // TODO(): decide which type will handle the routing when multi device type is supported
     protected void handleRoutingChangeAndInformation(int physicalAddress, HdmiCecMessage message) {
         // do nothing
     }
@@ -372,7 +371,7 @@
         if (!isActiveSource()) {
             return;
         }
-        // Wake up the device
+        // Wake up the device. This will also exit dream mode.
         mService.wakeUp();
         return;
     }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 352eac3..05922b3 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -926,10 +926,11 @@
 
             mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
             try {
-                // TODO: There shouldn't be a need to receive callback for all changes.
-                mActivityManager.registerUidObserver(mUidObserver,
-                        ActivityManager.UID_OBSERVER_PROCSTATE|ActivityManager.UID_OBSERVER_GONE,
-                        ActivityManager.PROCESS_STATE_UNKNOWN, "android");
+                final int changes = ActivityManager.UID_OBSERVER_PROCSTATE
+                        | ActivityManager.UID_OBSERVER_GONE
+                        | ActivityManager.UID_OBSERVER_CAPABILITY;
+                mActivityManager.registerUidObserver(mUidObserver, changes,
+                        NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE, "android");
                 mNetworkManager.registerObserver(mAlertObserver);
             } catch (RemoteException e) {
                 // ignored; both services live in system_server
@@ -5889,7 +5890,8 @@
         return (bundle != null) ? bundle.getBoolean(key, defaultValue) : defaultValue;
     }
 
-    private class UidBlockedState {
+    @VisibleForTesting
+    static final class UidBlockedState {
         public int blockedReasons;
         public int allowedReasons;
         public int effectiveBlockedReasons;
@@ -5901,19 +5903,29 @@
         }
 
         void updateEffectiveBlockedReasons() {
-            effectiveBlockedReasons = blockedReasons;
+            if (LOGV && blockedReasons == BLOCKED_REASON_NONE) {
+                Log.v(TAG, "updateEffectiveBlockedReasons(): no blocked reasons");
+            }
+            effectiveBlockedReasons = getEffectiveBlockedReasons(blockedReasons, allowedReasons);
+            if (LOGV) {
+                Log.v(TAG, "updateEffectiveBlockedReasons()"
+                        + ": blockedReasons=" + Integer.toBinaryString(blockedReasons)
+                        + ", effectiveReasons=" + Integer.toBinaryString(effectiveBlockedReasons));
+            }
+        }
+
+        @VisibleForTesting
+        static int getEffectiveBlockedReasons(int blockedReasons, int allowedReasons) {
+            int effectiveBlockedReasons = blockedReasons;
             // If the uid is not subject to any blocked reasons, then return early
             if (blockedReasons == BLOCKED_REASON_NONE) {
-                if (LOGV) {
-                    Log.v(TAG, "updateEffectiveBlockedReasons(): no blocked reasons");
-                }
-                return;
+                return effectiveBlockedReasons;
             }
             if ((allowedReasons & ALLOWED_REASON_SYSTEM) != 0) {
-                effectiveBlockedReasons = (blockedReasons & ALLOWED_METERED_REASON_MASK);
+                effectiveBlockedReasons &= ALLOWED_METERED_REASON_MASK;
             }
             if ((allowedReasons & ALLOWED_METERED_REASON_SYSTEM) != 0) {
-                effectiveBlockedReasons = (blockedReasons & ~ALLOWED_METERED_REASON_MASK);
+                effectiveBlockedReasons &= ~ALLOWED_METERED_REASON_MASK;
             }
             if ((allowedReasons & ALLOWED_REASON_FOREGROUND) != 0) {
                 effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER;
@@ -5939,11 +5951,7 @@
             if ((allowedReasons & ALLOWED_METERED_REASON_USER_EXEMPTED) != 0) {
                 effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_DATA_SAVER;
             }
-            if (LOGV) {
-                Log.v(TAG, "updateEffectiveBlockedReasons()"
-                        + ": blockedReasons=" + Integer.toBinaryString(blockedReasons)
-                        + ", effectiveReasons=" + Integer.toBinaryString(effectiveBlockedReasons));
-            }
+            return effectiveBlockedReasons;
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6dcf39b..2f4cbd5 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -685,6 +685,21 @@
                 }
             }
         }
+
+        // Remove notifications with the specified user & channel ID.
+        public void removeChannelNotifications(String pkg, @UserIdInt int userId,
+                String channelId) {
+            for (int i = 0; i < mBuffer.size(); i++) {
+                final Pair<StatusBarNotification, Integer> pair = mBuffer.get(i);
+                if (pair.first != null
+                        && userId == pair.first.getNormalizedUserId()
+                        && pkg != null && pkg.equals(pair.first.getPackageName())
+                        && pair.first.getNotification() != null
+                        && Objects.equals(channelId, pair.first.getNotification().getChannelId())) {
+                    mBuffer.remove(i);
+                }
+            }
+        }
     }
 
     void loadDefaultApprovedServices(int userId) {
@@ -3623,6 +3638,8 @@
             cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
                     callingUser, REASON_CHANNEL_REMOVED, null);
             mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, channelId);
+            // Remove from both recent notification archive and notification history
+            mArchive.removeChannelNotifications(pkg, callingUser, channelId);
             mHistoryManager.deleteNotificationChannel(pkg, callingUid, channelId);
             mListeners.notifyNotificationChannelChanged(pkg,
                     UserHandle.getUserHandleForUid(callingUid),
@@ -8179,8 +8196,10 @@
             summaries.remove(r.getSbn().getPackageName());
         }
 
-        // Save it for users of getHistoricalNotifications()
-        mArchive.record(r.getSbn(), reason);
+        // Save it for users of getHistoricalNotifications(), unless the whole channel was deleted
+        if (reason != REASON_CHANNEL_REMOVED) {
+            mArchive.record(r.getSbn(), reason);
+        }
 
         final long now = System.currentTimeMillis();
         final LogMaker logMaker = r.getItemLogMaker()
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 18c689f..fcee63c 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -586,19 +586,6 @@
     }
 
     private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) {
-        if (isNew) {
-            rule.id = ZenModeConfig.newRuleId();
-            rule.creationTime = System.currentTimeMillis();
-            rule.component = automaticZenRule.getOwner();
-            rule.configurationActivity = automaticZenRule.getConfigurationActivity();
-            rule.pkg = (rule.component != null)
-                    ? rule.component.getPackageName()
-                    : rule.configurationActivity.getPackageName();
-        }
-
-        if (rule.enabled != automaticZenRule.isEnabled()) {
-            rule.snoozing = false;
-        }
         rule.name = automaticZenRule.getName();
         rule.condition = null;
         rule.conditionId = automaticZenRule.getConditionId();
@@ -607,6 +594,20 @@
         rule.zenPolicy = automaticZenRule.getZenPolicy();
         rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
                 automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
+        rule.configurationActivity = automaticZenRule.getConfigurationActivity();
+
+        if (isNew) {
+            rule.id = ZenModeConfig.newRuleId();
+            rule.creationTime = System.currentTimeMillis();
+            rule.component = automaticZenRule.getOwner();
+            rule.pkg = (rule.component != null)
+                    ? rule.component.getPackageName()
+                    : rule.configurationActivity.getPackageName();
+        }
+
+        if (rule.enabled != automaticZenRule.isEnabled()) {
+            rule.snoozing = false;
+        }
     }
 
     protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4eafe51..a6d4ed9 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -948,14 +948,14 @@
                 == PackageManager.PERMISSION_GRANTED);
         final int targetPackageUid = mPm.getPackageUid(packageName, 0, userId);
         final boolean isUpdate = targetPackageUid != -1;
-        final InstallSourceInfo installSourceInfo = isUpdate
+        final InstallSourceInfo existingInstallSourceInfo = isUpdate
                 ? mPm.getInstallSourceInfo(packageName)
                 : null;
-        final String installerPackageName = installSourceInfo != null
-                ? installSourceInfo.getInstallingPackageName()
+        final String existingInstallerPackageName = existingInstallSourceInfo != null
+                ? existingInstallSourceInfo.getInstallingPackageName()
                 : null;
         final boolean isInstallerOfRecord = isUpdate
-                && Objects.equals(installerPackageName, getInstallerPackageName());
+                && Objects.equals(existingInstallerPackageName, getInstallerPackageName());
         final boolean isSelfUpdate = targetPackageUid == mInstallerUid;
         final boolean isPermissionGranted = isInstallPermissionGranted
                 || (isUpdatePermissionGranted && isUpdate)
@@ -972,7 +972,7 @@
             return USER_ACTION_NOT_NEEDED;
         }
 
-        if (mPm.isInstallDisabledForPackage(installerPackageName, mInstallerUid, userId)) {
+        if (mPm.isInstallDisabledForPackage(getInstallerPackageName(), mInstallerUid, userId)) {
             // show the installer to account for device poslicy or unknown sources use cases
             return USER_ACTION_REQUIRED;
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2dcc8d9..30ce57f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -12271,19 +12271,18 @@
 
     public ArraySet<String> getOptimizablePackages() {
         ArraySet<String> pkgs = new ArraySet<>();
-        final boolean hibernationEnabled = AppHibernationService.isAppHibernationEnabled();
-        AppHibernationManagerInternal appHibernationManager =
-                mInjector.getLocalService(AppHibernationManagerInternal.class);
         synchronized (mLock) {
             for (AndroidPackage p : mPackages.values()) {
-                // Checking hibernation state is an inexpensive call.
-                boolean isHibernating = hibernationEnabled
-                        && appHibernationManager.isHibernatingGlobally(p.getPackageName());
-                if (PackageDexOptimizer.canOptimizePackage(p) && !isHibernating) {
+                if (PackageDexOptimizer.canOptimizePackage(p)) {
                     pkgs.add(p.getPackageName());
                 }
             }
         }
+        if (AppHibernationService.isAppHibernationEnabled()) {
+            AppHibernationManagerInternal appHibernationManager =
+                    mInjector.getLocalService(AppHibernationManagerInternal.class);
+            pkgs.removeIf(pkgName -> appHibernationManager.isHibernatingGlobally(pkgName));
+        }
         return pkgs;
     }
 
@@ -21456,6 +21455,8 @@
         synchronized (mLock) {
             if (outInfo != null) {
                 outInfo.uid = ps.appId;
+                outInfo.broadcastAllowList = mAppsFilter.getVisibilityAllowList(ps,
+                        allUserHandles, mSettings.getPackagesLocked());
             }
         }
 
@@ -23464,10 +23465,12 @@
             }
         }
         if (shouldUnhibernate) {
-            AppHibernationManagerInternal ah =
-                    mInjector.getLocalService(AppHibernationManagerInternal.class);
-            ah.setHibernatingForUser(packageName, userId, false);
-            ah.setHibernatingGlobally(packageName, false);
+            mHandler.post(() -> {
+                AppHibernationManagerInternal ah =
+                        mInjector.getLocalService(AppHibernationManagerInternal.class);
+                ah.setHibernatingForUser(packageName, userId, false);
+                ah.setHibernatingGlobally(packageName, false);
+            });
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d13babc..01a46b3 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1949,7 +1949,7 @@
     boolean addStartingWindow(String pkg, int resolvedTheme, CompatibilityInfo compatInfo,
             CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
             IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
-            boolean allowTaskSnapshot, boolean activityCreated) {
+            boolean allowTaskSnapshot, boolean activityCreated, boolean samePackage) {
         // If the display is frozen, we won't do anything until the actual window is
         // displayed so there is no reason to put in the starting window.
         if (!okToDisplay()) {
@@ -1971,7 +1971,7 @@
                         false /* restoreFromDisk */, false /* isLowResolution */);
         final int typeParameter = mWmService.mStartingSurfaceController
                 .makeStartingWindowTypeParameter(newTask, taskSwitch, processRunning,
-                        allowTaskSnapshot, activityCreated);
+                        allowTaskSnapshot, activityCreated, samePackage);
         final int type = getStartingWindowType(newTask, taskSwitch, processRunning,
                 allowTaskSnapshot, activityCreated, snapshot);
 
@@ -6141,11 +6141,11 @@
 
     void showStartingWindow(boolean taskSwitch) {
         showStartingWindow(null /* prev */, false /* newTask */, taskSwitch,
-                0 /* splashScreenTheme */);
+                0 /* splashScreenTheme */, true /* samePackage */);
     }
 
     void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
-            int splashScreenTheme) {
+            int splashScreenTheme, boolean samePackage) {
         if (mTaskOverlay) {
             // We don't show starting window for overlay activities.
             return;
@@ -6165,7 +6165,8 @@
                 compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                 prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
                 allowTaskSnapshot(),
-                mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal());
+                mState.ordinal() >= STARTED.ordinal() && mState.ordinal() <= STOPPED.ordinal(),
+                samePackage);
         if (shown) {
             mStartingWindowState = STARTING_WINDOW_SHOWN;
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 0efadcf..aa1f42e 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1769,9 +1769,17 @@
         mRootWindowContainer.startPowerModeLaunchIfNeeded(
                 false /* forceSend */, mStartActivity);
 
+        final boolean startFromSamePackage;
+        if (sourceRecord != null && sourceRecord.mActivityComponent != null) {
+            startFromSamePackage = mStartActivity.mActivityComponent
+                    .getPackageName().equals(sourceRecord.mActivityComponent.getPackageName());
+        } else {
+            startFromSamePackage = false;
+        }
+
         mTargetRootTask.startActivityLocked(mStartActivity,
                 topRootTask != null ? topRootTask.getTopNonFinishingActivity() : null, newTask,
-                mKeepCurTransition, mOptions);
+                mKeepCurTransition, mOptions, startFromSamePackage);
         if (mDoResume) {
             final ActivityRecord topTaskActivity =
                     mStartActivity.getTask().topRunningActivityLocked();
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index 0708569..140ae3e 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -20,6 +20,7 @@
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_SAME_PACKAGE;
 import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
 
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -66,7 +67,8 @@
     }
 
     int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch,
-            boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated) {
+            boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated,
+            boolean samePackage) {
         int parameter = 0;
         if (newTask) {
             parameter |= TYPE_PARAMETER_NEW_TASK;
@@ -83,6 +85,9 @@
         if (activityCreated) {
             parameter |= TYPE_PARAMETER_ACTIVITY_CREATED;
         }
+        if (samePackage) {
+            parameter |= TYPE_PARAMETER_SAME_PACKAGE;
+        }
         return parameter;
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b36c25c..c293a15 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -6525,7 +6525,8 @@
     }
 
     void startActivityLocked(ActivityRecord r, @Nullable ActivityRecord focusedTopActivity,
-            boolean newTask, boolean keepCurTransition, ActivityOptions options) {
+            boolean newTask, boolean keepCurTransition, ActivityOptions options,
+            boolean samePackage) {
         Task rTask = r.getTask();
         final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
         final boolean isOrhasTask = rTask == this || hasChild(rTask);
@@ -6647,7 +6648,7 @@
                 final int splashScreenThemeResId = options != null
                         ? options.getSplashScreenThemeResId() : 0;
                 r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity),
-                        splashScreenThemeResId);
+                        splashScreenThemeResId, samePackage);
             }
         } else {
             // If this is the first activity, don't do any fancy animations,
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 9f25daf..15f5765 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -95,8 +95,8 @@
         "libaudioclient",
         "libbase",
         "libappfuse",
-        "libbinder",
         "libbinder_ndk",
+        "libbinder",
         "libcutils",
         "libcrypto",
         "liblog",
@@ -105,6 +105,7 @@
         "libhardware_legacy",
         "libhidlbase",
         "libmeminfo",
+        "libmemtrackproxy",
         "libmtp",
         "libnativehelper",
         "libnativewindow",
@@ -154,6 +155,7 @@
         "android.hardware.input.classifier@1.0",
         "android.hardware.ir@1.0",
         "android.hardware.light@2.0",
+        "android.hardware.memtrack-V1-ndk_platform",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
         "android.hardware.power-V1-cpp",
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 6cb4a63..fe728ab 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -24,11 +24,13 @@
 #include <nativehelper/JNIHelp.h>
 
 #include <android/binder_manager.h>
+#include <android/binder_stability.h>
 #include <android/hidl/manager/1.2/IServiceManager.h>
 #include <binder/IServiceManager.h>
 #include <hidl/HidlTransportSupport.h>
 #include <incremental_service.h>
 
+#include <memtrackproxy/MemtrackProxy.h>
 #include <schedulerservice/SchedulingPolicyService.h>
 #include <sensorservice/SensorService.h>
 #include <sensorservicehidl/SensorManager.h>
@@ -83,6 +85,21 @@
     }
 }
 
+static void android_server_SystemServer_startMemtrackProxyService(JNIEnv* env,
+                                                                  jobject /* clazz */) {
+    using aidl::android::hardware::memtrack::MemtrackProxy;
+
+    const char* memtrackProxyService = "memtrack.proxy";
+
+    std::shared_ptr<MemtrackProxy> memtrack_proxy = ndk::SharedRefBase::make<MemtrackProxy>();
+    auto binder = memtrack_proxy->asBinder();
+
+    AIBinder_forceDowngradeToLocalStability(binder.get());
+
+    const binder_exception_t err = AServiceManager_addService(binder.get(), memtrackProxyService);
+    LOG_ALWAYS_FATAL_IF(err != EX_NONE, "Cannot register %s: %d", memtrackProxyService, err);
+}
+
 static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject /* clazz */) {
     using ::android::frameworks::schedulerservice::V1_0::ISchedulingPolicyService;
     using ::android::frameworks::schedulerservice::V1_0::implementation::SchedulingPolicyService;
@@ -144,6 +161,8 @@
 static const JNINativeMethod gMethods[] = {
         /* name, signature, funcPtr */
         {"startSensorService", "()V", (void*)android_server_SystemServer_startSensorService},
+        {"startMemtrackProxyService", "()V",
+         (void*)android_server_SystemServer_startMemtrackProxyService},
         {"startHidlServices", "()V", (void*)android_server_SystemServer_startHidlServices},
         {"initZygoteChildHeapProfiling", "()V",
          (void*)android_server_SystemServer_initZygoteChildHeapProfiling},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index a419bf8..fb0265e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -41,13 +41,13 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
-import android.util.Slog;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.pm.UserRestrictionsUtils;
+import com.android.server.utils.Slogf;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -469,7 +469,7 @@
                     try {
                         trustAgentInfo.options.saveToXml(out);
                     } catch (XmlPullParserException e) {
-                        Slog.e(LOG_TAG, e, "Failed to save TrustAgent options");
+                        Slogf.e(LOG_TAG, e, "Failed to save TrustAgent options");
                     }
                     out.endTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS);
                 }
@@ -651,7 +651,7 @@
             String tag = parser.getName();
             if (TAG_POLICIES.equals(tag)) {
                 if (shouldOverridePolicies) {
-                    Slog.d(LOG_TAG, "Overriding device admin policies from XML.");
+                    Slogf.d(LOG_TAG, "Overriding device admin policies from XML.");
                     info.readPoliciesFromXml(parser);
                 }
             } else if (TAG_PASSWORD_QUALITY.equals(tag)) {
@@ -747,14 +747,14 @@
                 if (type == TypedXmlPullParser.TEXT) {
                     shortSupportMessage = parser.getText();
                 } else {
-                    Slog.w(LOG_TAG, "Missing text when loading short support message");
+                    Slogf.w(LOG_TAG, "Missing text when loading short support message");
                 }
             } else if (TAG_LONG_SUPPORT_MESSAGE.equals(tag)) {
                 type = parser.next();
                 if (type == TypedXmlPullParser.TEXT) {
                     longSupportMessage = parser.getText();
                 } else {
-                    Slog.w(LOG_TAG, "Missing text when loading long support message");
+                    Slogf.w(LOG_TAG, "Missing text when loading long support message");
                 }
             } else if (TAG_PARENT_ADMIN.equals(tag)) {
                 Preconditions.checkState(!isParent);
@@ -767,7 +767,7 @@
                 if (type == TypedXmlPullParser.TEXT) {
                     organizationName = parser.getText();
                 } else {
-                    Slog.w(LOG_TAG, "Missing text when loading organization name");
+                    Slogf.w(LOG_TAG, "Missing text when loading organization name");
                 }
             } else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) {
                 isLogoutEnabled = parser.getAttributeBoolean(null, ATTR_VALUE, false);
@@ -776,14 +776,14 @@
                 if (type == TypedXmlPullParser.TEXT) {
                     startUserSessionMessage = parser.getText();
                 } else {
-                    Slog.w(LOG_TAG, "Missing text when loading start session message");
+                    Slogf.w(LOG_TAG, "Missing text when loading start session message");
                 }
             } else if (TAG_END_USER_SESSION_MESSAGE.equals(tag)) {
                 type = parser.next();
                 if (type == TypedXmlPullParser.TEXT) {
                     endUserSessionMessage = parser.getText();
                 } else {
-                    Slog.w(LOG_TAG, "Missing text when loading end session message");
+                    Slogf.w(LOG_TAG, "Missing text when loading end session message");
                 }
             } else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES.equals(tag)) {
                 mCrossProfileCalendarPackages = readPackageList(parser, tag);
@@ -822,14 +822,14 @@
                 if (type == TypedXmlPullParser.TEXT) {
                     mOrganizationId = parser.getText();
                 } else {
-                    Slog.w(LOG_TAG, "Missing Organization ID.");
+                    Slogf.w(LOG_TAG, "Missing Organization ID.");
                 }
             } else if (TAG_ENROLLMENT_SPECIFIC_ID.equals(tag)) {
                 type = parser.next();
                 if (type == TypedXmlPullParser.TEXT) {
                     mEnrollmentSpecificId = parser.getText();
                 } else {
-                    Slog.w(LOG_TAG, "Missing Enrollment-specific ID.");
+                    Slogf.w(LOG_TAG, "Missing Enrollment-specific ID.");
                 }
             } else if (TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS.equals(tag)) {
                 mAdminCanGrantSensorsPermissions = parser.getAttributeBoolean(null, ATTR_VALUE,
@@ -838,7 +838,7 @@
                 mUsbDataSignalingEnabled = parser.getAttributeBoolean(null, ATTR_VALUE,
                         USB_DATA_SIGNALING_ENABLED_DEFAULT);
             } else {
-                Slog.w(LOG_TAG, "Unknown admin tag: %s", tag);
+                Slogf.w(LOG_TAG, "Unknown admin tag: %s", tag);
                 XmlUtils.skipCurrentTag(parser);
             }
         }
@@ -860,10 +860,10 @@
                 if (packageName != null) {
                     result.add(packageName);
                 } else {
-                    Slog.w(LOG_TAG, "Package name missing under %s", outerTag);
+                    Slogf.w(LOG_TAG, "Package name missing under %s", outerTag);
                 }
             } else {
-                Slog.w(LOG_TAG, "Unknown tag under %s: ", tag, outerTag);
+                Slogf.w(LOG_TAG, "Unknown tag under %s: ", tag, outerTag);
             }
         }
         return result;
@@ -884,7 +884,7 @@
             if (tag.equals(tagDAM)) {
                 result.add(parser.getAttributeValue(null, ATTR_VALUE));
             } else {
-                Slog.e(LOG_TAG, "Expected tag %s but found %s", tag, tagDAM);
+                Slogf.e(LOG_TAG, "Expected tag %s but found %s", tag, tagDAM);
             }
         }
     }
@@ -906,7 +906,7 @@
                 final TrustAgentInfo trustAgentInfo = getTrustAgentInfo(parser, tag);
                 result.put(component, trustAgentInfo);
             } else {
-                Slog.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM);
+                Slogf.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM);
             }
         }
         return result;
@@ -926,7 +926,7 @@
             if (TAG_TRUST_AGENT_COMPONENT_OPTIONS.equals(tagDAM)) {
                 result.options = PersistableBundle.restoreFromXml(parser);
             } else {
-                Slog.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM);
+                Slogf.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM);
             }
         }
         return result;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
index 8027e5b..cc385c7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
@@ -35,11 +35,11 @@
 import android.security.Credentials;
 import android.security.KeyChain;
 import android.security.KeyChain.KeyChainConnection;
-import android.util.Slog;
 
 import com.android.internal.R;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
+import com.android.server.utils.Slogf;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -79,16 +79,16 @@
             X509Certificate cert = parseCert(certBuffer);
             pemCert = Credentials.convertToPem(cert);
         } catch (CertificateException | IOException ce) {
-            Slog.e(LOG_TAG, "Problem converting cert", ce);
+            Slogf.e(LOG_TAG, "Problem converting cert", ce);
             return null;
         }
 
         try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) {
             return keyChainConnection.getService().installCaCertificate(pemCert);
         } catch (RemoteException e) {
-            Slog.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
+            Slogf.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
         } catch (InterruptedException e1) {
-            Slog.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
+            Slogf.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
             Thread.currentThread().interrupt();
         }
         return null;
@@ -100,9 +100,9 @@
                 keyChainConnection.getService().deleteCaCertificate(aliases[i]);
             }
         } catch (RemoteException e) {
-            Slog.e(LOG_TAG, "from CaCertUninstaller: ", e);
+            Slogf.e(LOG_TAG, "from CaCertUninstaller: ", e);
         } catch (InterruptedException ie) {
-            Slog.w(LOG_TAG, "CaCertUninstaller: ", ie);
+            Slogf.w(LOG_TAG, "CaCertUninstaller: ", ie);
             Thread.currentThread().interrupt();
         }
     }
@@ -147,7 +147,7 @@
         try {
             installedCerts = getInstalledCaCertificates(userHandle);
         } catch (RemoteException | RuntimeException e) {
-            Slog.e(LOG_TAG, e, "Could not retrieve certificates from KeyChain service for user %d",
+            Slogf.e(LOG_TAG, e, "Could not retrieve certificates from KeyChain service for user %d",
                     userId);
             return;
         }
@@ -170,7 +170,7 @@
         try {
             userContext = mInjector.createContextAsUser(userHandle);
         } catch (PackageManager.NameNotFoundException e) {
-            Slog.e(LOG_TAG, e, "Create context as %s failed", userHandle);
+            Slogf.e(LOG_TAG, e, "Create context as %s failed", userHandle);
             return null;
         }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
index 00e0292..8f0af91 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
@@ -27,13 +27,13 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.util.IndentingPrintWriter;
-import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
 import com.android.server.am.PersistentConnection;
 import com.android.server.appbinding.AppBindingUtils;
+import com.android.server.utils.Slogf;
 
 /**
  * Manages connections to persistent services in owner packages.
@@ -114,7 +114,8 @@
                 final ServiceInfo service = findService(packageName, userId);
                 if (service == null) {
                     if (DEBUG) {
-                        Slog.d(TAG, "Owner package %s on u%d has no service.", packageName, userId);
+                        Slogf.d(TAG, "Owner package %s on u%d has no service.", packageName,
+                                userId);
                     }
                     disconnectServiceOnUserLocked(userId, actionForLog);
                     return;
@@ -127,14 +128,14 @@
                     // would have died at this point due to a package update.  So we disconnect
                     // anyway and re-connect.
                     if (DEBUG) {
-                        Slog.d("Disconnecting from existing service connection.", packageName,
+                        Slogf.d("Disconnecting from existing service connection.", packageName,
                                 userId);
                     }
                     disconnectServiceOnUserLocked(userId, actionForLog);
                 }
 
                 if (DEBUG) {
-                    Slog.d("Owner package %s on u%d has service %s for %s", packageName, userId,
+                    Slogf.d("Owner package %s on u%d has service %s for %s", packageName, userId,
                         service.getComponentName().flattenToShortString(), actionForLog);
                 }
 
@@ -168,7 +169,7 @@
         final DevicePolicyServiceConnection conn = mConnections.get(userId);
         if (conn != null) {
             if (DEBUG) {
-                Slog.d(TAG, "Stopping service for u%d if already running for %s.", userId,
+                Slogf.d(TAG, "Stopping service for u%d if already running for %s.", userId,
                         actionForLog);
             }
             conn.unbind();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
index 2825eea..6de341d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
@@ -17,7 +17,8 @@
 
 import android.util.IndentingPrintWriter;
 import android.util.KeyValueListParser;
-import android.util.Slog;
+
+import com.android.server.utils.Slogf;
 
 import java.util.concurrent.TimeUnit;
 
@@ -99,7 +100,7 @@
         } catch (IllegalArgumentException e) {
             // Failed to parse the settings string, log this and move on
             // with defaults.
-            Slog.e(TAG, "Bad device policy settings: %s", settings);
+            Slogf.e(TAG, "Bad device policy settings: %s", settings);
         }
 
         long dasDiedServiceReconnectBackoffSec = parser.getLong(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index 52cdce6..26c442d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -27,13 +27,13 @@
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.IndentingPrintWriter;
-import android.util.Slog;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.XmlUtils;
+import com.android.server.utils.Slogf;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -183,7 +183,7 @@
         try {
             chooseForWrite = file.chooseForWrite();
             if (VERBOSE_LOG) {
-                Slog.v(TAG, "Storing data for user %d on %s ", policyData.mUserId, chooseForWrite);
+                Slogf.v(TAG, "Storing data for user %d on %s ", policyData.mUserId, chooseForWrite);
             }
             stream = new FileOutputStream(chooseForWrite, false);
             TypedXmlSerializer out = Xml.resolveSerializer(stream);
@@ -195,7 +195,7 @@
                         policyData.mRestrictionsProvider.flattenToString());
             }
             if (policyData.mUserSetupComplete) {
-                if (VERBOSE_LOG) Slog.v(TAG, "setting %s to true", ATTR_SETUP_COMPLETE);
+                if (VERBOSE_LOG) Slogf.v(TAG, "setting %s to true", ATTR_SETUP_COMPLETE);
                 out.attributeBoolean(null, ATTR_SETUP_COMPLETE, true);
             }
             if (policyData.mPaired) {
@@ -216,7 +216,7 @@
 
             if (policyData.mFactoryResetFlags != 0) {
                 if (VERBOSE_LOG) {
-                    Slog.v(TAG, "Storing factory reset flags for user %d: %s", policyData.mUserId,
+                    Slogf.v(TAG, "Storing factory reset flags for user %d: %s", policyData.mUserId,
                             factoryResetFlagsToString(policyData.mFactoryResetFlags));
                 }
                 out.attributeInt(null, ATTR_FACTORY_RESET_FLAGS, policyData.mFactoryResetFlags);
@@ -382,7 +382,7 @@
             file.commit();
             return true;
         } catch (XmlPullParserException | IOException e) {
-            Slog.w(TAG, e, "failed writing file %s", chooseForWrite);
+            Slogf.w(TAG, e, "failed writing file %s", chooseForWrite);
             try {
                 if (stream != null) {
                     stream.close();
@@ -404,7 +404,7 @@
             ComponentName ownerComponent) {
         FileInputStream stream = null;
         File file = journaledFile.chooseForRead();
-        if (VERBOSE_LOG) Slog.v(TAG, "Loading data for user %d from %s", policy.mUserId, file);
+        if (VERBOSE_LOG) Slogf.v(TAG, "Loading data for user %d from %s", policy.mUserId, file);
         boolean needsRewrite = false;
         try {
             stream = new FileInputStream(file);
@@ -428,7 +428,7 @@
             }
             String userSetupComplete = parser.getAttributeValue(null, ATTR_SETUP_COMPLETE);
             if (Boolean.toString(true).equals(userSetupComplete)) {
-                if (VERBOSE_LOG) Slog.v(TAG, "setting mUserSetupComplete to true");
+                if (VERBOSE_LOG) Slogf.v(TAG, "setting mUserSetupComplete to true");
                 policy.mUserSetupComplete = true;
             }
             String paired = parser.getAttributeValue(null, ATTR_DEVICE_PAIRED);
@@ -452,7 +452,7 @@
 
             policy.mFactoryResetFlags = parser.getAttributeInt(null, ATTR_FACTORY_RESET_FLAGS, 0);
             if (VERBOSE_LOG) {
-                Slog.v(TAG, "Restored factory reset flags for user %d: %s", policy.mUserId,
+                Slogf.v(TAG, "Restored factory reset flags for user %d: %s", policy.mUserId,
                         factoryResetFlagsToString(policy.mFactoryResetFlags));
             }
             policy.mFactoryResetReason = parser.getAttributeValue(null, ATTR_FACTORY_RESET_REASON);
@@ -486,7 +486,7 @@
                             policy.mAdminMap.put(ap.info.getComponent(), ap);
                         }
                     } catch (RuntimeException e) {
-                        Slog.w(TAG, e, "Failed loading admin %s", name);
+                        Slogf.w(TAG, e, "Failed loading admin %s", name);
                     }
                 } else if ("delegation".equals(tag)) {
                     // Parse delegation info.
@@ -558,7 +558,7 @@
                     policy.mAppsSuspended =
                             parser.getAttributeBoolean(null, ATTR_VALUE, false);
                 } else {
-                    Slog.w(TAG, "Unknown tag: %s", tag);
+                    Slogf.w(TAG, "Unknown tag: %s", tag);
                     XmlUtils.skipCurrentTag(parser);
                 }
             }
@@ -566,7 +566,7 @@
             // Don't be noisy, this is normal if we haven't defined any policies.
         } catch (NullPointerException | NumberFormatException | XmlPullParserException | IOException
                 | IndexOutOfBoundsException e) {
-            Slog.w(TAG, e, "failed parsing %s", file);
+            Slogf.w(TAG, e, "failed parsing %s", file);
         }
         try {
             if (stream != null) {
@@ -590,7 +590,7 @@
                 }
             }
             if (!haveOwner) {
-                Slog.w(TAG, "Previous password owner %s no longer active; disabling",
+                Slogf.w(TAG, "Previous password owner %s no longer active; disabling",
                         mPasswordOwner);
                 mPasswordOwner = -1;
             }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 00a1786d..8e361eb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -283,7 +283,6 @@
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.Pair;
-import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
@@ -333,6 +332,7 @@
 import com.android.server.storage.DeviceStorageMonitorInternal;
 import com.android.server.uri.NeededUriGrants;
 import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.utils.Slogf;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import com.google.android.collect.Sets;
@@ -734,7 +734,7 @@
         if (Thread.holdsLock(mLockDoNoUseDirectly)) {
             return;
         }
-        Slog.wtfStack(LOG_TAG, "Not holding DPMS lock.");
+        Slogf.wtfStack(LOG_TAG, "Not holding DPMS lock.");
     }
 
     @VisibleForTesting
@@ -843,7 +843,7 @@
             if (Intent.ACTION_BOOT_COMPLETED.equals(action)
                     || ACTION_EXPIRED_PASSWORD_NOTIFICATION.equals(action)) {
                 if (VERBOSE_LOG) {
-                    Slog.v(LOG_TAG, "Sending password expiration notifications for action "
+                    Slogf.v(LOG_TAG, "Sending password expiration notifications for action "
                             + action + " for user " + userHandle);
                 }
                 mHandler.post(new Runnable() {
@@ -887,7 +887,7 @@
             } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
                 sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STOPPED, userHandle);
                 if (isManagedProfile(userHandle)) {
-                    Slog.d(LOG_TAG, "Managed profile was stopped");
+                    Slogf.d(LOG_TAG, "Managed profile was stopped");
                     updatePersonalAppsSuspension(userHandle, false /* unlocked */);
                 }
             } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
@@ -897,7 +897,7 @@
                     maybeSendAdminEnabledBroadcastLocked(userHandle);
                 }
                 if (isManagedProfile(userHandle)) {
-                    Slog.d(LOG_TAG, "Managed profile became unlocked");
+                    Slogf.d(LOG_TAG, "Managed profile became unlocked");
                     final boolean suspended =
                             updatePersonalAppsSuspension(userHandle, true /* unlocked */);
                     triggerPolicyComplianceCheckIfNeeded(userHandle, suspended);
@@ -929,15 +929,15 @@
                     updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked(userId));
                 }
             } else if (ACTION_PROFILE_OFF_DEADLINE.equals(action)) {
-                Slog.i(LOG_TAG, "Profile off deadline alarm was triggered");
+                Slogf.i(LOG_TAG, "Profile off deadline alarm was triggered");
                 final int userId = getManagedUserId(UserHandle.USER_SYSTEM);
                 if (userId >= 0) {
                     updatePersonalAppsSuspension(userId, mUserManager.isUserUnlocked(userId));
                 } else {
-                    Slog.wtf(LOG_TAG, "Got deadline alarm for nonexistent profile");
+                    Slogf.wtf(LOG_TAG, "Got deadline alarm for nonexistent profile");
                 }
             } else if (ACTION_TURN_PROFILE_ON_NOTIFICATION.equals(action)) {
-                Slog.i(LOG_TAG, "requesting to turn on the profile: " + userHandle);
+                Slogf.i(LOG_TAG, "requesting to turn on the profile: " + userHandle);
                 mUserManager.requestQuietModeEnabled(false, UserHandle.of(userHandle));
             }
         }
@@ -986,7 +986,7 @@
 
                 // 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 "
+                Slogf.i(LOG_TAG, "Resetting cross-profile intent filters on restriction "
                         + "change");
                 mDpms.resetDefaultCrossProfileIntentFilters(parentId);
                 mContext.sendBroadcastAsUser(new Intent(
@@ -1007,7 +1007,7 @@
     private void handlePackagesChanged(@Nullable String packageName, int userHandle) {
         boolean removedAdmin = false;
         if (VERBOSE_LOG) {
-            Slog.d(LOG_TAG, "Handling package changes package " + packageName
+            Slogf.d(LOG_TAG, "Handling package changes package " + packageName
                     + " for user " + userHandle);
         }
         DevicePolicyData policy = getUserData(userHandle);
@@ -1077,7 +1077,7 @@
                     service.removeCredentialManagementApp();
                 }
             } catch (RemoteException | InterruptedException | IllegalStateException e) {
-                Slog.e(LOG_TAG, "Unable to remove the credential management app");
+                Slogf.e(LOG_TAG, "Unable to remove the credential management app");
             }
         });
     }
@@ -1103,7 +1103,7 @@
         // Check if package is considered not suspendable?
         if (mInjector.getPackageManager(userHandle)
                 .getUnsuspendablePackages(packagesToSuspend).length != 0) {
-            Slog.i(LOG_TAG, "Newly installed package is unsuspendable: " + packageName);
+            Slogf.i(LOG_TAG, "Newly installed package is unsuspendable: " + packageName);
             return;
         }
         try {
@@ -1127,7 +1127,7 @@
      * Used by {@code setDevicePolicySafetyChecker()} above and {@link OneTimeSafetyChecker}.
      */
     void setDevicePolicySafetyCheckerUnchecked(DevicePolicySafetyChecker safetyChecker) {
-        Slog.i(LOG_TAG, "Setting DevicePolicySafetyChecker as %s", safetyChecker);
+        Slogf.i(LOG_TAG, "Setting DevicePolicySafetyChecker as %s", safetyChecker);
         mSafetyChecker = safetyChecker;
         mInjector.setDevicePolicySafetyChecker(safetyChecker);
     }
@@ -1170,7 +1170,7 @@
             @OperationSafetyReason int reason) {
         Preconditions.checkCallAuthorization(
                 hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
-        Slog.i(LOG_TAG, "setNextOperationSafety(%s, %s)",
+        Slogf.i(LOG_TAG, "setNextOperationSafety(%s, %s)",
                 DevicePolicyManager.operationToString(operation),
                 DevicePolicyManager.operationSafetyReasonToString(reason));
         mSafetyChecker = new OneTimeSafetyChecker(this, operation, reason);
@@ -1179,7 +1179,7 @@
     @Override
     public boolean isSafeOperation(@OperationSafetyReason int reason) {
         if (VERBOSE_LOG) {
-            Slog.v(LOG_TAG, "checking isSafeOperation(%s) using mSafetyChecker %s",
+            Slogf.v(LOG_TAG, "checking isSafeOperation(%s) using mSafetyChecker %s",
                     DevicePolicyManager.operationSafetyReasonToString(reason), mSafetyChecker);
         }
         return mSafetyChecker == null ? true : mSafetyChecker.isSafeOperation(reason);
@@ -1758,7 +1758,7 @@
     void removeUserData(int userHandle) {
         synchronized (getLockObject()) {
             if (userHandle == UserHandle.USER_SYSTEM) {
-                Slog.w(LOG_TAG, "Tried to remove device policy file for user 0! Ignoring.");
+                Slogf.w(LOG_TAG, "Tried to remove device policy file for user 0! Ignoring.");
                 return;
             }
             updatePasswordQualityCacheForUserGroup(userHandle);
@@ -1774,7 +1774,7 @@
             File policyFile = new File(mInjector.environmentGetUserSystemDirectory(userHandle),
                     DEVICE_POLICIES_XML);
             policyFile.delete();
-            Slog.i(LOG_TAG, "Removed device policy file " + policyFile.getAbsolutePath());
+            Slogf.i(LOG_TAG, "Removed device policy file " + policyFile.getAbsolutePath());
         }
     }
 
@@ -1872,64 +1872,64 @@
      */
     @GuardedBy("getLockObject()")
     private void migrateToProfileOnOrganizationOwnedDeviceIfCompLocked() {
-        if (VERBOSE_LOG) Slog.d(LOG_TAG, "Checking whether we need to migrate COMP ");
+        if (VERBOSE_LOG) Slogf.d(LOG_TAG, "Checking whether we need to migrate COMP ");
         final int doUserId = mOwners.getDeviceOwnerUserId();
         if (doUserId == UserHandle.USER_NULL) {
-            if (VERBOSE_LOG) Slog.d(LOG_TAG, "No DO found, skipping migration.");
+            if (VERBOSE_LOG) Slogf.d(LOG_TAG, "No DO found, skipping migration.");
             return;
         }
 
         final List<UserInfo> profiles = mUserManager.getProfiles(doUserId);
         if (profiles.size() != 2) {
             if (profiles.size() == 1) {
-                if (VERBOSE_LOG) Slog.d(LOG_TAG, "Profile not found, skipping migration.");
+                if (VERBOSE_LOG) Slogf.d(LOG_TAG, "Profile not found, skipping migration.");
             } else {
-                Slog.wtf(LOG_TAG, "Found " + profiles.size() + " profiles, skipping migration");
+                Slogf.wtf(LOG_TAG, "Found " + profiles.size() + " profiles, skipping migration");
             }
             return;
         }
 
         final int poUserId = getManagedUserId(doUserId);
         if (poUserId < 0) {
-            Slog.wtf(LOG_TAG, "Found DO and a profile, but it is not managed, skipping migration");
+            Slogf.wtf(LOG_TAG, "Found DO and a profile, but it is not managed, skipping migration");
             return;
         }
 
         final ActiveAdmin doAdmin = getDeviceOwnerAdminLocked();
         final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(poUserId);
         if (doAdmin == null || poAdmin == null) {
-            Slog.wtf(LOG_TAG, "Failed to get either PO or DO admin, aborting migration.");
+            Slogf.wtf(LOG_TAG, "Failed to get either PO or DO admin, aborting migration.");
             return;
         }
 
         final ComponentName doAdminComponent = mOwners.getDeviceOwnerComponent();
         final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(poUserId);
         if (doAdminComponent == null || poAdminComponent == null) {
-            Slog.wtf(LOG_TAG, "Cannot find PO or DO component name, aborting migration.");
+            Slogf.wtf(LOG_TAG, "Cannot find PO or DO component name, aborting migration.");
             return;
         }
         if (!doAdminComponent.getPackageName().equals(poAdminComponent.getPackageName())) {
-            Slog.e(LOG_TAG, "DO and PO are different packages, aborting migration.");
+            Slogf.e(LOG_TAG, "DO and PO are different packages, aborting migration.");
             return;
         }
 
-        Slog.i(LOG_TAG, "Migrating COMP to PO on a corp owned device; primary user: %d; "
+        Slogf.i(LOG_TAG, "Migrating COMP to PO on a corp owned device; primary user: %d; "
                 + "profile: %d", doUserId, poUserId);
 
-        Slog.i(LOG_TAG, "Giving the PO additional power...");
+        Slogf.i(LOG_TAG, "Giving the PO additional power...");
         markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(poAdminComponent, poUserId);
-        Slog.i(LOG_TAG, "Migrating DO policies to PO...");
+        Slogf.i(LOG_TAG, "Migrating DO policies to PO...");
         moveDoPoliciesToProfileParentAdminLocked(doAdmin, poAdmin.getParentActiveAdmin());
         migratePersonalAppSuspensionLocked(doUserId, poUserId, poAdmin);
         saveSettingsLocked(poUserId);
-        Slog.i(LOG_TAG, "Clearing the DO...");
+        Slogf.i(LOG_TAG, "Clearing the DO...");
         final ComponentName doAdminReceiver = doAdmin.info.getComponent();
         clearDeviceOwnerLocked(doAdmin, doUserId);
-        Slog.i(LOG_TAG, "Removing admin artifacts...");
+        Slogf.i(LOG_TAG, "Removing admin artifacts...");
         removeAdminArtifacts(doAdminReceiver, doUserId);
-        Slog.i(LOG_TAG, "Uninstalling the DO...");
+        Slogf.i(LOG_TAG, "Uninstalling the DO...");
         uninstallOrDisablePackage(doAdminComponent.getPackageName(), doUserId);
-        Slog.i(LOG_TAG, "Migration complete.");
+        Slogf.i(LOG_TAG, "Migration complete.");
 
         // Note: KeyChain keys are not removed and will remain accessible for the apps that have
         // been given grants to use them.
@@ -1945,16 +1945,16 @@
             int doUserId, int poUserId, ActiveAdmin poAdmin) {
         final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
         if (!pmi.isSuspendingAnyPackages(PLATFORM_PACKAGE_NAME, doUserId)) {
-            Slog.i(LOG_TAG, "DO is not suspending any apps.");
+            Slogf.i(LOG_TAG, "DO is not suspending any apps.");
             return;
         }
 
         if (getTargetSdk(poAdmin.info.getPackageName(), poUserId) >= Build.VERSION_CODES.R) {
-            Slog.i(LOG_TAG, "PO is targeting R+, keeping personal apps suspended.");
+            Slogf.i(LOG_TAG, "PO is targeting R+, keeping personal apps suspended.");
             getUserData(doUserId).mAppsSuspended = true;
             poAdmin.mSuspendPersonalApps = true;
         } else {
-            Slog.i(LOG_TAG, "PO isn't targeting R+, unsuspending personal apps.");
+            Slogf.i(LOG_TAG, "PO isn't targeting R+, unsuspending personal apps.");
             pmi.unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, doUserId);
         }
     }
@@ -1969,11 +1969,11 @@
             return;
         }
         if (appInfo == null) {
-            Slog.wtf(LOG_TAG, "Failed to get package info for " + packageName);
+            Slogf.wtf(LOG_TAG, "Failed to get package info for " + packageName);
             return;
         }
         if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-            Slog.i(LOG_TAG, "Package %s is pre-installed, marking disabled until used",
+            Slogf.i(LOG_TAG, "Package %s is pre-installed, marking disabled until used",
                     packageName);
             mContext.getPackageManager().setApplicationEnabledSetting(packageName,
                     PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, /* flags= */ 0);
@@ -1987,9 +1987,9 @@
                 final int status = intent.getIntExtra(
                         PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
                 if (status == PackageInstaller.STATUS_SUCCESS) {
-                    Slog.i(LOG_TAG, "Package %s uninstalled for user %d", packageName, userId);
+                    Slogf.i(LOG_TAG, "Package %s uninstalled for user %d", packageName, userId);
                 } else {
-                    Slog.e(LOG_TAG, "Failed to uninstall %s; status: %d", packageName, status);
+                    Slogf.e(LOG_TAG, "Failed to uninstall %s; status: %d", packageName, status);
                 }
             }
         };
@@ -2057,7 +2057,7 @@
     private void applyManagedProfileRestrictionIfDeviceOwnerLocked() {
         final int doUserId = mOwners.getDeviceOwnerUserId();
         if (doUserId == UserHandle.USER_NULL) {
-            if (VERBOSE_LOG) Slog.d(LOG_TAG, "No DO found, skipping application of restriction.");
+            if (VERBOSE_LOG) Slogf.d(LOG_TAG, "No DO found, skipping application of restriction.");
             return;
         }
 
@@ -2114,10 +2114,10 @@
         if (defaultRestrictions.equals(admin.defaultEnabledRestrictionsAlreadySet)) {
             return; // The same set of default restrictions has been already applied.
         }
-        Slog.i(LOG_TAG, "New user restrictions need to be set by default for user " + userId);
+        Slogf.i(LOG_TAG, "New user restrictions need to be set by default for user " + userId);
 
         if (VERBOSE_LOG) {
-            Slog.d(LOG_TAG,"Default enabled restrictions: "
+            Slogf.d(LOG_TAG, "Default enabled restrictions: "
                     + defaultRestrictions
                     + ". Restrictions already enabled: "
                     + admin.defaultEnabledRestrictionsAlreadySet);
@@ -2130,7 +2130,7 @@
                 admin.ensureUserRestrictions().putBoolean(restriction, true);
             }
             admin.defaultEnabledRestrictionsAlreadySet.addAll(restrictionsToSet);
-            Slog.i(LOG_TAG, "Enabled the following restrictions by default: " + restrictionsToSet);
+            Slogf.i(LOG_TAG, "Enabled the following restrictions by default: " + restrictionsToSet);
             saveUserRestrictionsLocked(userId);
         }
     }
@@ -2154,10 +2154,10 @@
         final String value = Boolean.toString(hasDeviceOwner || hasOrgOwnedProfile);
         final String currentVal = mInjector.systemPropertiesGet(PROPERTY_ORGANIZATION_OWNED, null);
         if (TextUtils.isEmpty(currentVal)) {
-            Slog.i(LOG_TAG, "Set ro.organization_owned property to " + value);
+            Slogf.i(LOG_TAG, "Set ro.organization_owned property to " + value);
             mInjector.systemPropertiesSet(PROPERTY_ORGANIZATION_OWNED, value);
         } else if (!value.equals(currentVal)) {
-            Slog.w(LOG_TAG, "Cannot change existing ro.organization_owned to " + value);
+            Slogf.w(LOG_TAG, "Cannot change existing ro.organization_owned to " + value);
         }
     }
 
@@ -2185,7 +2185,7 @@
                 doComponentName.getPackageName(),
                 mOwners.getDeviceOwnerUserId());
         if (doComponent == null) {
-            Slog.e(LOG_TAG, "Device-owner isn't registered as device-admin");
+            Slogf.e(LOG_TAG, "Device-owner isn't registered as device-admin");
         } else {
             mOwners.setDeviceOwnerWithRestrictionsMigrated(
                     doComponent,
@@ -2194,7 +2194,7 @@
                     !mOwners.getDeviceOwnerUserRestrictionsNeedsMigration());
             mOwners.writeDeviceOwner();
             if (VERBOSE_LOG) {
-                Slog.v(LOG_TAG, "Device owner component filled in");
+                Slogf.v(LOG_TAG, "Device owner component filled in");
             }
         }
     }
@@ -2209,7 +2209,7 @@
         // except for the "system controlled" ones.
         if (mOwners.getDeviceOwnerUserRestrictionsNeedsMigration()) {
             if (VERBOSE_LOG) {
-                Slog.v(LOG_TAG, "Migrating DO user restrictions");
+                Slogf.v(LOG_TAG, "Migrating DO user restrictions");
             }
             migrated = true;
 
@@ -2237,7 +2237,7 @@
             final int userId = ui.id;
             if (mOwners.getProfileOwnerUserRestrictionsNeedsMigration(userId)) {
                 if (VERBOSE_LOG) {
-                    Slog.v(LOG_TAG, "Migrating PO user restrictions for user %d", userId);
+                    Slogf.v(LOG_TAG, "Migrating PO user restrictions for user %d", userId);
                 }
                 migrated = true;
 
@@ -2260,7 +2260,7 @@
             }
         }
         if (VERBOSE_LOG && migrated) {
-            Slog.v(LOG_TAG, "User restrictions migrated.");
+            Slogf.v(LOG_TAG, "User restrictions migrated.");
         }
     }
 
@@ -2288,9 +2288,9 @@
         }
 
         if (VERBOSE_LOG) {
-            Slog.v(LOG_TAG, "origRestrictions=%s", origRestrictions);
-            Slog.v(LOG_TAG, "newBaseRestrictions=%s", newBaseRestrictions);
-            Slog.v(LOG_TAG, "newOwnerRestrictions=%s", newOwnerRestrictions);
+            Slogf.v(LOG_TAG, "origRestrictions=%s", origRestrictions);
+            Slogf.v(LOG_TAG, "newBaseRestrictions=%s", newBaseRestrictions);
+            Slogf.v(LOG_TAG, "newOwnerRestrictions=%s", newOwnerRestrictions);
         }
         mUserManagerInternal.setBaseUserRestrictionsByDpmsForMigration(user.getIdentifier(),
                 newBaseRestrictions);
@@ -2299,7 +2299,7 @@
             admin.ensureUserRestrictions().clear();
             admin.ensureUserRestrictions().putAll(newOwnerRestrictions);
         } else {
-            Slog.w(LOG_TAG, "ActiveAdmin for DO/PO not found. user=" + user.getIdentifier());
+            Slogf.w(LOG_TAG, "ActiveAdmin for DO/PO not found. user=" + user.getIdentifier());
         }
         saveSettingsLocked(user.getIdentifier());
     }
@@ -2320,7 +2320,7 @@
             }
         }
         if (nFound > 1) {
-            Slog.w(LOG_TAG, "Multiple DA found; assume the first one is DO.");
+            Slogf.w(LOG_TAG, "Multiple DA found; assume the first one is DO.");
         }
         return found;
     }
@@ -2760,7 +2760,7 @@
         if (!permission.BIND_DEVICE_ADMIN.equals(ai.permission)) {
             final String message = "DeviceAdminReceiver " + adminName + " must be protected with "
                     + permission.BIND_DEVICE_ADMIN;
-            Slog.w(LOG_TAG, message);
+            Slogf.w(LOG_TAG, message);
             if (throwForMissingPermission &&
                     ai.applicationInfo.targetSdkVersion > Build.VERSION_CODES.M) {
                 throw new IllegalArgumentException(message);
@@ -2770,7 +2770,7 @@
         try {
             return new DeviceAdminInfo(mContext, ai);
         } catch (XmlPullParserException | IOException e) {
-            Slog.w(LOG_TAG, "Bad device admin requested for user=" + userHandle + ": " + adminName,
+            Slogf.w(LOG_TAG, "Bad device admin requested for user=" + userHandle + ": " + adminName,
                     e);
             return null;
         }
@@ -2785,7 +2785,7 @@
     private JournaledFile makeJournaledFile(@UserIdInt int userId, String fileName) {
         final String base = new File(getPolicyFileDirectory(userId), fileName)
                 .getAbsolutePath();
-        if (VERBOSE_LOG) Slog.v(LOG_TAG, "Opening %s", base);
+        if (VERBOSE_LOG) Slogf.v(LOG_TAG, "Opening %s", base);
         return new JournaledFile(new File(base), new File(base + ".tmp"));
     }
 
@@ -3033,7 +3033,7 @@
         if (!mTransferOwnershipMetadataManager.metadataFileExists()) {
             return;
         }
-        Slog.e(LOG_TAG, "Owner transfer metadata file exists! Reverting transfer.");
+        Slogf.e(LOG_TAG, "Owner transfer metadata file exists! Reverting transfer.");
         final TransferOwnershipMetadataManager.Metadata metadata =
                 mTransferOwnershipMetadataManager.loadMetadataFile();
         // Revert transfer
@@ -3079,7 +3079,7 @@
                 // STOPSHIP Prevent the DO user from being killed.
 
             } catch (RemoteException e) {
-                Slog.w(LOG_TAG, "Exception starting user", e);
+                Slogf.w(LOG_TAG, "Exception starting user", e);
             }
         }
     }
@@ -3512,7 +3512,7 @@
             }
             // Remove the admin skipping sending the broadcast.
             removeAdminArtifacts(adminReceiver, userHandle);
-            Slog.i(LOG_TAG, "Admin " + adminReceiver + " removed from user " + userHandle);
+            Slogf.i(LOG_TAG, "Admin " + adminReceiver + " removed from user " + userHandle);
         });
     }
 
@@ -3584,8 +3584,8 @@
             // Active device/profile owners must remain active admins.
             if (isDeviceOwner(adminReceiver, userHandle)
                     || isProfileOwner(adminReceiver, userHandle)) {
-                Slog.e(LOG_TAG, "Device/profile owner cannot be removed: component=" +
-                        adminReceiver);
+                Slogf.e(LOG_TAG, "Device/profile owner cannot be removed: component="
+                        + adminReceiver);
                 return;
             }
             if (admin.getUid() != mInjector.binderGetCallingUid()) {
@@ -3850,7 +3850,7 @@
                         }
                     }
                 } else {
-                    Slog.w(LOG_TAG, "Unknown user type: " + userInfo);
+                    Slogf.w(LOG_TAG, "Unknown user type: " + userInfo);
                 }
             }
         });
@@ -3959,7 +3959,7 @@
             ap.passwordExpirationDate = expiration;
             ap.passwordExpirationTimeout = timeout;
             if (timeout > 0L) {
-                Slog.w(LOG_TAG, "setPasswordExpiration(): password will expire on "
+                Slogf.w(LOG_TAG, "setPasswordExpiration(): password will expire on "
                         + DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT)
                         .format(new Date(expiration)));
             }
@@ -4883,7 +4883,7 @@
     @Override
     public boolean resetPassword(@Nullable String password, int flags) throws RemoteException {
         if (!mLockPatternUtils.hasSecureLockScreen()) {
-            Slog.w(LOG_TAG, "Cannot reset password when the device has no lock screen");
+            Slogf.w(LOG_TAG, "Cannot reset password when the device has no lock screen");
             return false;
         }
         if (password == null) password = "";
@@ -4901,7 +4901,7 @@
             synchronized (getLockObject()) {
                 ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
                 if (getTargetSdk(admin.info.getPackageName(), userHandle) < Build.VERSION_CODES.O) {
-                    Slog.e(LOG_TAG, "DPC can no longer call resetPassword()");
+                    Slogf.e(LOG_TAG, "DPC can no longer call resetPassword()");
                     return false;
                 }
                 throw new SecurityException("Device admin can no longer call resetPassword()");
@@ -4917,7 +4917,7 @@
                     "Unauthorized caller cannot call resetPassword.");
             if (getTargetSdk(admin.info.getPackageName(),
                     userHandle) <= android.os.Build.VERSION_CODES.M) {
-                Slog.e(LOG_TAG, "Device admin can no longer call resetPassword()");
+                Slogf.e(LOG_TAG, "Device admin can no longer call resetPassword()");
                 return false;
             }
             throw new SecurityException("Device admin can no longer call resetPassword()");
@@ -4944,7 +4944,7 @@
             }
 
             if (!validationErrors.isEmpty()) {
-                Slog.w(LOG_TAG, "Failed to reset password due to constraint violation: %s",
+                Slogf.w(LOG_TAG, "Failed to reset password due to constraint violation: %s",
                         validationErrors.get(0));
                 return false;
             }
@@ -4952,7 +4952,7 @@
 
         DevicePolicyData policy = getUserData(userHandle);
         if (policy.mPasswordOwner >= 0 && policy.mPasswordOwner != callingUid) {
-            Slog.w(LOG_TAG, "resetPassword: already set by another uid and not entered by user");
+            Slogf.w(LOG_TAG, "resetPassword: already set by another uid and not entered by user");
             return false;
         }
 
@@ -5266,7 +5266,7 @@
                 if (userToLock == UserHandle.USER_ALL) {
                     if (mIsAutomotive) {
                         if (VERBOSE_LOG) {
-                            Slog.v(LOG_TAG, "lockNow(): not powering off display on automotive"
+                            Slogf.v(LOG_TAG, "lockNow(): not powering off display on automotive"
                                     + " build");
                         }
                     } else {
@@ -5373,7 +5373,7 @@
         });
 
         if (alias == null) {
-            Slog.w(LOG_TAG, "Problem installing cert");
+            Slogf.w(LOG_TAG, "Problem installing cert");
             return false;
         }
 
@@ -5446,12 +5446,12 @@
                         .write();
                 return true;
             } catch (RemoteException e) {
-                Slog.e(LOG_TAG, "Installing certificate", e);
+                Slogf.e(LOG_TAG, "Installing certificate", e);
             } finally {
                 keyChainConnection.close();
             }
         } catch (InterruptedException e) {
-            Slog.w(LOG_TAG, "Interrupted while installing certificate", e);
+            Slogf.w(LOG_TAG, "Interrupted while installing certificate", e);
             Thread.currentThread().interrupt();
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
@@ -5496,12 +5496,12 @@
                         .write();
                 return keyChain.removeKeyPair(alias);
             } catch (RemoteException e) {
-                Slog.e(LOG_TAG, "Removing keypair", e);
+                Slogf.e(LOG_TAG, "Removing keypair", e);
             } finally {
                 keyChainConnection.close();
             }
         } catch (InterruptedException e) {
-            Slog.w(LOG_TAG, "Interrupted while removing keypair", e);
+            Slogf.w(LOG_TAG, "Interrupted while removing keypair", e);
             Thread.currentThread().interrupt();
         } finally {
             Binder.restoreCallingIdentity(id);
@@ -5520,9 +5520,9 @@
                          KeyChain.bindAsUser(mContext, caller.getUserHandle())) {
                 return keyChainConnection.getService().containsKeyPair(alias);
             } catch (RemoteException e) {
-                Slog.e(LOG_TAG, "Querying keypair", e);
+                Slogf.e(LOG_TAG, "Querying keypair", e);
             } catch (InterruptedException e) {
-                Slog.w(LOG_TAG, "Interrupted while querying keypair", e);
+                Slogf.w(LOG_TAG, "Interrupted while querying keypair", e);
                 Thread.currentThread().interrupt();
             }
             return false;
@@ -5564,7 +5564,7 @@
                 }
                 return false;
             } catch (RemoteException e) {
-                Slog.e(LOG_TAG, "Querying grant to wifi auth.", e);
+                Slogf.e(LOG_TAG, "Querying grant to wifi auth.", e);
                 return false;
             }
         });
@@ -5605,11 +5605,11 @@
                 keyChain.setGrant(granteeUid, alias, hasGrant);
                 return true;
             } catch (RemoteException e) {
-                Slog.e(LOG_TAG, "Setting grant for package.", e);
+                Slogf.e(LOG_TAG, "Setting grant for package.", e);
                 return false;
             }
         } catch (InterruptedException e) {
-            Slog.w(LOG_TAG, "Interrupted while setting key grant", e);
+            Slogf.w(LOG_TAG, "Interrupted while setting key grant", e);
             Thread.currentThread().interrupt();
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
@@ -5636,7 +5636,7 @@
                 for (final int uid : granteeUids) {
                     final String[] packages = pm.getPackagesForUid(uid);
                     if (packages == null) {
-                        Slog.wtf(LOG_TAG, "No packages found for uid " + uid);
+                        Slogf.wtf(LOG_TAG, "No packages found for uid " + uid);
                         continue;
                     }
                     if (!result.isEmpty()) {
@@ -5646,9 +5646,9 @@
                 }
                 return result;
             } catch (RemoteException e) {
-                Slog.e(LOG_TAG, "Querying keypair grants", e);
+                Slogf.e(LOG_TAG, "Querying keypair grants", e);
             } catch (InterruptedException e) {
-                Slog.w(LOG_TAG, "Interrupted while querying keypair grants", e);
+                Slogf.w(LOG_TAG, "Interrupted while querying keypair grants", e);
                 Thread.currentThread().interrupt();
             }
             return Collections.emptyList();
@@ -5773,7 +5773,7 @@
         // As the caller will be granted access to the key, ensure no UID was specified, as
         // it will not have the desired effect.
         if (keySpec.getUid() != KeyStore.UID_SELF) {
-            Slog.e(LOG_TAG, "Only the caller can be granted access to the generated keypair.");
+            Slogf.e(LOG_TAG, "Only the caller can be granted access to the generated keypair.");
             logGenerateKeyPairFailure(caller, isCredentialManagementApp);
             return false;
         }
@@ -5799,7 +5799,7 @@
                 final int generationResult = keyChain.generateKeyPair(algorithm,
                         new ParcelableKeyGenParameterSpec(keySpec));
                 if (generationResult != KeyChain.KEY_GEN_SUCCESS) {
-                    Slog.e(LOG_TAG, "KeyChain failed to generate a keypair, error %d.",
+                    Slogf.e(LOG_TAG, "KeyChain failed to generate a keypair, error %d.",
                             generationResult);
                     logGenerateKeyPairFailure(caller, isCredentialManagementApp);
                     switch (generationResult) {
@@ -5839,7 +5839,7 @@
                     attestationChain.shallowCopyFrom(new KeymasterCertificateChain(encodedCerts));
                 } catch (CertificateException e) {
                     logGenerateKeyPairFailure(caller, isCredentialManagementApp);
-                    Slog.e(LOG_TAG, "While retrieving certificate chain.", e);
+                    Slogf.e(LOG_TAG, "While retrieving certificate chain.", e);
                     return false;
                 }
 
@@ -5854,9 +5854,9 @@
                 return true;
             }
         } catch (RemoteException e) {
-            Slog.e(LOG_TAG, "KeyChain error while generating a keypair", e);
+            Slogf.e(LOG_TAG, "KeyChain error while generating a keypair", e);
         } catch (InterruptedException e) {
-            Slog.w(LOG_TAG, "Interrupted while generating keypair", e);
+            Slogf.w(LOG_TAG, "Interrupted while generating keypair", e);
             Thread.currentThread().interrupt();
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
@@ -5914,10 +5914,10 @@
                     .write();
             return true;
         } catch (InterruptedException e) {
-            Slog.w(LOG_TAG, "Interrupted while setting keypair certificate", e);
+            Slogf.w(LOG_TAG, "Interrupted while setting keypair certificate", e);
             Thread.currentThread().interrupt();
         } catch (RemoteException e) {
-            Slog.e(LOG_TAG, "Failed setting keypair certificate", e);
+            Slogf.e(LOG_TAG, "Failed setting keypair certificate", e);
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
@@ -5991,7 +5991,7 @@
         } catch (Exception e) {
             // Caller could throw RuntimeException or RemoteException back across processes. Catch
             // everything just to be sure.
-            Slog.e(LOG_TAG, "error while responding to callback", e);
+            Slogf.e(LOG_TAG, "error while responding to callback", e);
         }
     }
 
@@ -6208,7 +6208,7 @@
         if (delegates.size() == 0) {
             return null;
         } else if (delegates.size() > 1) {
-            Slog.wtf(LOG_TAG, "More than one delegate holds " + scope);
+            Slogf.wtf(LOG_TAG, "More than one delegate holds " + scope);
             return null;
         }
         final String pkg = delegates.get(0);
@@ -6224,7 +6224,7 @@
         final int count = receivers.size();
         if (count >= 1) {
             if (count > 1) {
-                Slog.w(LOG_TAG, pkg + " defines more than one delegate receiver for " + action);
+                Slogf.w(LOG_TAG, pkg + " defines more than one delegate receiver for " + action);
             }
             return receivers.get(0).activityInfo.getComponentName();
         } else {
@@ -6352,7 +6352,7 @@
             boolean isUserSelectable) {
         // Should not be user selectable
         if (isUserSelectable) {
-            Slog.e(LOG_TAG, "The credential management app is not allowed to install a "
+            Slogf.e(LOG_TAG, "The credential management app is not allowed to install a "
                     + "user selectable key pair");
             return false;
         }
@@ -6442,7 +6442,7 @@
         final int userId = caller.getUserId();
         mInjector.binderWithCleanCallingIdentity(() -> {
             if (vpnPackage != null && !isPackageInstalledForUser(vpnPackage, userId)) {
-                Slog.w(LOG_TAG, "Non-existent VPN package specified: " + vpnPackage);
+                Slogf.w(LOG_TAG, "Non-existent VPN package specified: " + vpnPackage);
                 throw new ServiceSpecificException(
                         DevicePolicyManager.ERROR_VPN_PACKAGE_NOT_FOUND, vpnPackage);
             }
@@ -6450,7 +6450,7 @@
             if (vpnPackage != null && lockdown && lockdownAllowlist != null) {
                 for (String packageName : lockdownAllowlist) {
                     if (!isPackageInstalledForUser(packageName, userId)) {
-                        Slog.w(LOG_TAG, "Non-existent package in VPN allowlist: " + packageName);
+                        Slogf.w(LOG_TAG, "Non-existent package in VPN allowlist: " + packageName);
                         throw new ServiceSpecificException(
                                 DevicePolicyManager.ERROR_VPN_PACKAGE_NOT_FOUND, packageName);
                     }
@@ -6552,7 +6552,7 @@
                 // Persist the request so the device is automatically factory-reset on next start if
                 // the system crashes or reboots before the {@code DevicePolicySafetyChecker} calls
                 // its callback.
-                Slog.i(LOG_TAG, "Persisting factory reset request as it could be delayed by %s",
+                Slogf.i(LOG_TAG, "Persisting factory reset request as it could be delayed by %s",
                         mSafetyChecker);
                 synchronized (getLockObject()) {
                     DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
@@ -6563,7 +6563,7 @@
             }
             success = true;
         } catch (IOException | SecurityException e) {
-            Slog.w(LOG_TAG, "Failed requesting data wipe", e);
+            Slogf.w(LOG_TAG, "Failed requesting data wipe", e);
         } finally {
             if (!success) SecurityLog.writeEvent(SecurityLog.TAG_WIPE_FAILURE);
         }
@@ -6577,7 +6577,7 @@
 
             if (policy.mFactoryResetReason == null) {
                 // Shouldn't happen.
-                Slog.e(LOG_TAG, "no persisted reason for factory resetting");
+                Slogf.e(LOG_TAG, "no persisted reason for factory resetting");
                 policy.mFactoryResetReason = "requested before boot";
             }
             FactoryResetter factoryResetter = FactoryResetter.newBuilder(mContext)
@@ -6589,16 +6589,16 @@
                     .setWipeFactoryResetProtection((policy.mFactoryResetFlags & DevicePolicyData
                             .FACTORY_RESET_FLAG_WIPE_FACTORY_RESET_PROTECTION) != 0)
                     .build();
-            Slog.i(LOG_TAG, "Factory resetting on boot using " + factoryResetter);
+            Slogf.i(LOG_TAG, "Factory resetting on boot using " + factoryResetter);
             try {
                 if (!factoryResetter.factoryReset()) {
                     // Shouldn't happen because FactoryResetter was created without a
                     // DevicePolicySafetyChecker.
-                    Slog.wtf(LOG_TAG, "Factory reset using " + factoryResetter + " failed.");
+                    Slogf.wtf(LOG_TAG, "Factory reset using " + factoryResetter + " failed.");
                 }
             } catch (IOException e) {
                 // Shouldn't happen.
-                Slog.wtf(LOG_TAG, "Could not factory reset using " + factoryResetter, e);
+                Slogf.wtf(LOG_TAG, "Could not factory reset using " + factoryResetter, e);
             }
         }
     }
@@ -6612,7 +6612,7 @@
 
             success = mUserManagerInternal.removeUserEvenWhenDisallowed(userId);
             if (!success) {
-                Slog.w(LOG_TAG, "Couldn't remove user " + userId);
+                Slogf.w(LOG_TAG, "Couldn't remove user " + userId);
             } else if (isManagedProfile(userId) && !wipeSilently) {
                 sendWipeProfileNotification(wipeReasonForUser);
             }
@@ -6667,7 +6667,7 @@
 
         int userId = admin != null ? admin.getUserHandle().getIdentifier()
                 : caller.getUserId();
-        Slog.i(LOG_TAG, "wipeDataWithReason(%s): admin=%s, user=%d", wipeReasonForUser, admin,
+        Slogf.i(LOG_TAG, "wipeDataWithReason(%s): admin=%s, user=%d", wipeReasonForUser, admin,
                 userId);
         if (calledByProfileOwnerOnOrgOwnedDevice) {
             // When wipeData is called on the parent instance, it implies wiping the entire device.
@@ -6705,7 +6705,7 @@
         } else {
             adminComp = null;
             adminName = mInjector.getPackageManager().getPackagesForUid(caller.getUid())[0];
-            Slog.i(LOG_TAG, "Logging wipeData() event admin as " + adminName);
+            Slogf.i(LOG_TAG, "Logging wipeData() event admin as " + adminName);
             event.setAdmin(adminName);
             if (mInjector.userManagerIsHeadlessSystemUserMode()) {
                 // On headless system user mode, the call is meant to factory reset the whole
@@ -6980,7 +6980,7 @@
 
         if (wipeData && strictestAdmin != null) {
             final int userId = getUserIdToWipeForFailedPasswords(strictestAdmin);
-            Slog.i(LOG_TAG, "Max failed password attempts policy reached for admin: "
+            Slogf.i(LOG_TAG, "Max failed password attempts policy reached for admin: "
                     + strictestAdmin.info.getComponent().flattenToShortString()
                     + ". Calling wipeData for user " + userId);
 
@@ -6999,7 +6999,7 @@
                         wipeReasonForUser,
                         userId);
             } catch (SecurityException e) {
-                Slog.w(LOG_TAG, "Failed to wipe user " + userId
+                Slogf.w(LOG_TAG, "Failed to wipe user " + userId
                         + " after max failed password attempts reached.", e);
             }
         }
@@ -7133,7 +7133,7 @@
 
             // If the user is not system, don't set the global proxy. Fail silently.
             if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
-                Slog.w(LOG_TAG, "Only the owner is allowed to set the global proxy. User "
+                Slogf.w(LOG_TAG, "Only the owner is allowed to set the global proxy. User "
                         + UserHandle.getCallingUserId() + " is not permitted.");
                 return null;
             }
@@ -7226,7 +7226,7 @@
         ProxyInfo proxyProperties = ProxyInfo.buildDirectProxy(data[0], proxyPort,
                 ProxyUtils.exclusionStringAsList(exclusionList));
         if (!proxyProperties.isValid()) {
-            Slog.e(LOG_TAG, "Invalid proxy properties, ignoring: " + proxyProperties.toString());
+            Slogf.e(LOG_TAG, "Invalid proxy properties, ignoring: " + proxyProperties.toString());
             return;
         }
         mInjector.settingsGlobalPutString(Settings.Global.GLOBAL_HTTP_PROXY_HOST, data[0]);
@@ -7253,8 +7253,8 @@
             // Check for permissions
             // Only system user can set storage encryption
             if (userHandle != UserHandle.USER_SYSTEM) {
-                Slog.w(LOG_TAG, "Only owner/system user is allowed to set storage encryption. User "
-                        + UserHandle.getCallingUserId() + " is not permitted.");
+                Slogf.w(LOG_TAG, "Only owner/system user is allowed to set storage encryption. "
+                        + "User " + UserHandle.getCallingUserId() + " is not permitted.");
                 return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
             }
 
@@ -7468,7 +7468,7 @@
             try {
                 mInjector.getIWindowManager().refreshScreenCaptureDisabled(userHandle);
             } catch (RemoteException e) {
-                Slog.w(LOG_TAG, "Unable to notify WindowManager.", e);
+                Slogf.w(LOG_TAG, "Unable to notify WindowManager.", e);
             }
         });
     }
@@ -7796,7 +7796,7 @@
             intent.putExtras(extras);
         }
         if (VERBOSE_LOG) {
-            Slog.v(LOG_TAG, "sendActiveAdminCommand(): broadcasting " + action + " to "
+            Slogf.v(LOG_TAG, "sendActiveAdminCommand(): broadcasting " + action + " to "
                     + receiverComponent.flattenToShortString() + " on user " + userId);
         }
         mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
@@ -8083,7 +8083,7 @@
      * feature will be appended to it.
      */
     private void logMissingFeatureAction(String message) {
-        Slog.w(LOG_TAG, message + " because device does not have the "
+        Slogf.w(LOG_TAG, message + " because device does not have the "
                 + PackageManager.FEATURE_DEVICE_ADMIN + " feature.");
     }
 
@@ -8150,11 +8150,11 @@
             mDeviceAdminServiceController.startServiceForOwner(
                     admin.getPackageName(), userId, "set-device-owner");
 
-            Slog.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId);
+            Slogf.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId);
 
             if (mInjector.userManagerIsHeadlessSystemUserMode()) {
                 int currentForegroundUser = getCurrentForegroundUserId();
-                Slog.i(LOG_TAG, "setDeviceOwner(): setting " + admin
+                Slogf.i(LOG_TAG, "setDeviceOwner(): setting " + admin
                         + " as profile owner on user " + currentForegroundUser);
                 // Sets profile owner on current foreground user since
                 // the human user will complete the DO setup workflow from there.
@@ -8383,7 +8383,7 @@
                 return admin;
             }
         }
-        Slog.wtf(LOG_TAG, "Active admin for device owner not found. component=" + component);
+        Slogf.wtf(LOG_TAG, "Active admin for device owner not found. component=" + component);
         return null;
     }
 
@@ -8424,7 +8424,7 @@
                 sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED,
                         deviceOwnerUserId);
             });
-            Slog.i(LOG_TAG, "Device owner removed: " + deviceOwnerComponent);
+            Slogf.i(LOG_TAG, "Device owner removed: " + deviceOwnerComponent);
         }
     }
 
@@ -8518,7 +8518,7 @@
             if (parentUserId != userHandle && mUserManager.hasUserRestriction(
                     UserManager.DISALLOW_ADD_MANAGED_PROFILE,
                     UserHandle.of(parentUserId))) {
-                Slog.i(LOG_TAG, "Cannot set profile owner because of restriction.");
+                Slogf.i(LOG_TAG, "Cannot set profile owner because of restriction.");
                 return false;
             }
 
@@ -8537,7 +8537,7 @@
 
             mOwners.setProfileOwner(who, ownerName, userHandle);
             mOwners.writeProfileOwner(userHandle);
-            Slog.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle);
+            Slogf.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle);
 
             mInjector.binderWithCleanCallingIdentity(() -> {
                 if (mUserManager.isManagedProfile(userHandle)) {
@@ -8593,7 +8593,7 @@
                 sendOwnerChangedBroadcast(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED,
                         userId);
             });
-            Slog.i(LOG_TAG, "Profile owner " + who + " removed from user " + userId);
+            Slogf.i(LOG_TAG, "Profile owner " + who + " removed from user " + userId);
         }
     }
 
@@ -8806,7 +8806,7 @@
             // Check if the profile is already enabled.
             UserInfo managedProfile = getUserInfo(userId);
             if (managedProfile.isEnabled()) {
-                Slog.e(LOG_TAG,
+                Slogf.e(LOG_TAG,
                         "setProfileEnabled is called when the profile is already enabled");
                 return;
             }
@@ -9019,18 +9019,18 @@
             // thrown but null data can be returned; if the appInfo for the specified package cannot
             // be found then return false to prevent crashing the app.
             if (appInfo == null) {
-                Slog.w(LOG_TAG, "appInfo could not be found for package %s", packageName);
+                Slogf.w(LOG_TAG, "appInfo could not be found for package %s", packageName);
                 return false;
             } else if (uid != appInfo.uid) {
                 String message = String.format("Package %s (uid=%d) does not match provided uid %d",
                         packageName, appInfo.uid, uid);
-                Slog.w(LOG_TAG, message);
+                Slogf.w(LOG_TAG, message);
                 throw new SecurityException(message);
             }
         } catch (RemoteException e) {
             // If an exception is caught obtaining the appInfo just return false to prevent crashing
             // apps due to an internal error.
-            Slog.e(LOG_TAG, e, "Exception caught obtaining appInfo for package %s", packageName);
+            Slogf.e(LOG_TAG, e, "Exception caught obtaining appInfo for package %s", packageName);
             return false;
         }
         return true;
@@ -9046,7 +9046,7 @@
             String message = String.format(
                     "Calling uid %d, pid %d cannot check device identifier access for package %s "
                             + "(uid=%d, pid=%d)", callingUid, callingPid, packageName, uid, pid);
-            Slog.w(LOG_TAG, message);
+            Slogf.w(LOG_TAG, message);
             throw new SecurityException(message);
         }
     }
@@ -9062,7 +9062,7 @@
                 userContext = mContext.createPackageContextAsUser(packageName, /* flags= */ 0,
                         userHandle);
             } catch (PackageManager.NameNotFoundException nnfe) {
-                Slog.w(LOG_TAG, nnfe, "%s is not installed for user %d", packageName, userId);
+                Slogf.w(LOG_TAG, nnfe, "%s is not installed for user %d", packageName, userId);
                 return null;
             }
             ApplicationInfo appInfo = userContext.getApplicationInfo();
@@ -9079,7 +9079,7 @@
      */
     private void wtfIfInLock() {
         if (Thread.holdsLock(this)) {
-            Slog.wtfStack(LOG_TAG, "Shouldn't be called with DPMS lock held");
+            Slogf.wtfStack(LOG_TAG, "Shouldn't be called with DPMS lock held");
         }
     }
 
@@ -9296,7 +9296,7 @@
         try {
             return mInjector.getIActivityManager().getCurrentUser().id;
         } catch (RemoteException e) {
-            Slog.wtf(LOG_TAG, "cannot get current user");
+            Slogf.wtf(LOG_TAG, "cannot get current user");
         }
         return UserHandle.USER_NULL;
     }
@@ -9384,7 +9384,7 @@
                 // pm.getUnsuspendablePackages() will fail if it's called for a different user;
                 // as this dump is mostly useful for system user anyways, we can just ignore the
                 // others (rather than changing the permission check in the PM method)
-                Slog.d(LOG_TAG, "skipping PersonalAppsSuspensionHelper.dump() for user " + userId);
+                Slogf.d(LOG_TAG, "skipping PersonalAppsSuspensionHelper.dump() for user " + userId);
             }
         }
     }
@@ -9681,8 +9681,8 @@
                         }
                         result.add(info.options);
                     } else {
-                        Slog.w(LOG_TAG, "Ignoring admin %s because it has trust options but doesn't"
-                                + " declare KEYGUARD_DISABLE_TRUST_AGENTS", active.info);
+                        Slogf.w(LOG_TAG, "Ignoring admin %s because it has trust options but "
+                                + "doesn't declare KEYGUARD_DISABLE_TRUST_AGENTS", active.info);
                     }
                 } else if (disablesTrust) {
                     allAdminsHaveOptions = false;
@@ -9728,7 +9728,7 @@
             try {
                 UserInfo parent = mUserManager.getProfileParent(callingUserId);
                 if (parent == null) {
-                    Slog.e(LOG_TAG, "Cannot call addCrossProfileIntentFilter if there is no "
+                    Slogf.e(LOG_TAG, "Cannot call addCrossProfileIntentFilter if there is no "
                             + "parent");
                     return;
                 }
@@ -9778,7 +9778,7 @@
             try {
                 UserInfo parent = mUserManager.getProfileParent(callingUserId);
                 if (parent == null) {
-                    Slog.e(LOG_TAG, "Cannot call clearCrossProfileIntentFilter if there is no "
+                    Slogf.e(LOG_TAG, "Cannot call clearCrossProfileIntentFilter if there is no "
                             + "parent");
                     return;
                 }
@@ -9820,7 +9820,7 @@
                             userIdToCheck);
                     systemService = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
                 } catch (RemoteException e) {
-                    Slog.i(LOG_TAG, "Can't talk to package managed", e);
+                    Slogf.i(LOG_TAG, "Can't talk to package managed", e);
                 }
                 if (!systemService && !permittedList.contains(enabledPackage)) {
                     return false;
@@ -9873,7 +9873,7 @@
                 }
                 if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList,
                         userId)) {
-                    Slog.e(LOG_TAG, "Cannot set permitted accessibility services, "
+                    Slogf.e(LOG_TAG, "Cannot set permitted accessibility services, "
                             + "because it contains already enabled accesibility services.");
                     return false;
                 }
@@ -10026,7 +10026,7 @@
                 }
                 if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList,
                         userId)) {
-                    Slog.e(LOG_TAG, "Cannot set permitted input methods, because the list of "
+                    Slogf.e(LOG_TAG, "Cannot set permitted input methods, because the list of "
                             + "permitted input methods excludes an already-enabled input method.");
                     return false;
                 }
@@ -10292,7 +10292,7 @@
                 }
 
                 Object token = new Object();
-                Slog.d(LOG_TAG, "Adding new pending token: " + token);
+                Slogf.d(LOG_TAG, "Adding new pending token: " + token);
                 mPendingUserCreatedCallbackTokens.add(token);
                 try {
                     UserInfo userInfo = mUserManagerInternal.createUserEvenWhenDisallowed(name,
@@ -10301,7 +10301,7 @@
                         user = userInfo.getUserHandle();
                     }
                 } catch (UserManager.CheckedUserOperationException e) {
-                    Slog.e(LOG_TAG, "Couldn't createUserEvenWhenDisallowed", e);
+                    Slogf.e(LOG_TAG, "Couldn't createUserEvenWhenDisallowed", e);
                 }
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
@@ -10356,7 +10356,7 @@
             boolean showDisclaimer) {
         synchronized (getLockObject()) {
             if (VERBOSE_LOG) {
-                Slog.v(LOG_TAG, "manageUserUnchecked(): admin=" + admin + ", po=" + profileOwner
+                Slogf.v(LOG_TAG, "manageUserUnchecked(): admin=" + admin + ", po=" + profileOwner
                         + ", userId=" + userId + ", hasAdminExtras=" + (adminExtras != null)
                         + ", showDisclaimer=" + showDisclaimer);
             }
@@ -10372,7 +10372,7 @@
             }
         } catch (RemoteException e) {
             // Does not happen, same process
-            Slog.wtf(LOG_TAG, e, "Failed to install admin package %s for user %d",
+            Slogf.wtf(LOG_TAG, e, "Failed to install admin package %s for user %d",
                     adminPkg, userId);
         }
 
@@ -10396,7 +10396,7 @@
 
     private void handleNewUserCreated(UserInfo user, @Nullable Object token) {
         if (VERBOSE_LOG) {
-            Slog.v(LOG_TAG, "handleNewUserCreated(): user=" + user.toFullString()
+            Slogf.v(LOG_TAG, "handleNewUserCreated(): user=" + user.toFullString()
                     + ", token=" + token);
         }
 
@@ -10405,7 +10405,7 @@
             synchronized (getLockObject()) {
                 if (mPendingUserCreatedCallbackTokens.contains(token)) {
                     // Ignore because it was triggered by createAndManageUser()
-                    Slog.d(LOG_TAG, "handleNewUserCreated(): ignoring for user " + userId
+                    Slogf.d(LOG_TAG, "handleNewUserCreated(): ignoring for user " + userId
                             + " due to token" + token);
                     mPendingUserCreatedCallbackTokens.remove(token);
                     return;
@@ -10417,12 +10417,12 @@
 
         if (mInjector.userManagerIsHeadlessSystemUserMode()) {
             ComponentName admin = mOwners.getDeviceOwnerComponent();
-            Slog.i(LOG_TAG, "Automatically setting profile owner (" + admin + ") on new user "
+            Slogf.i(LOG_TAG, "Automatically setting profile owner (" + admin + ") on new user "
                     + userId);
             manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin,
                     /* managedUser= */ userId, /* adminExtras= */ null, /* showDisclaimer= */ true);
         } else {
-            Slog.i(LOG_TAG, "User %d added on DO mode; setting ShowNewUserDisclaimer", userId);
+            Slogf.i(LOG_TAG, "User %d added on DO mode; setting ShowNewUserDisclaimer", userId);
             setShowNewUserDisclaimer(userId, DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED);
         }
     }
@@ -10437,7 +10437,7 @@
     }
 
     private void setShowNewUserDisclaimer(@UserIdInt int userId, String value) {
-        Slog.i(LOG_TAG, "Setting new user disclaimer for user " + userId + " as " + value);
+        Slogf.i(LOG_TAG, "Setting new user disclaimer for user " + userId + " as " + value);
         synchronized (getLockObject()) {
             DevicePolicyData policyData = getUserData(userId);
             policyData.mNewUserDisclaimer = value;
@@ -10450,7 +10450,7 @@
         synchronized (getLockObject()) {
             DevicePolicyData policyData = getUserData(userId);
             if (VERBOSE_LOG) {
-                Slog.v(LOG_TAG, "showNewUserDisclaimerIfNecessary(" + userId + "): "
+                Slogf.v(LOG_TAG, "showNewUserDisclaimerIfNecessary(" + userId + "): "
                         + policyData.mNewUserDisclaimer + ")");
             }
             mustShow = DevicePolicyData.NEW_USER_DISCLAIMER_NEEDED
@@ -10461,7 +10461,7 @@
         Intent intent = new Intent(DevicePolicyManager.ACTION_SHOW_NEW_USER_DISCLAIMER);
 
         // TODO(b/172691310): add CTS tests to make sure disclaimer is shown
-        Slog.i(LOG_TAG, "Dispatching ACTION_SHOW_NEW_USER_DISCLAIMER intent");
+        Slogf.i(LOG_TAG, "Dispatching ACTION_SHOW_NEW_USER_DISCLAIMER intent");
         mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
     }
 
@@ -10478,7 +10478,7 @@
                     ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE
                     : UserManager.DISALLOW_REMOVE_USER;
             if (isAdminAffectedByRestriction(who, restriction, caller.getUserId())) {
-                Slog.w(LOG_TAG, "The device owner cannot remove a user because %s is enabled, and "
+                Slogf.w(LOG_TAG, "The device owner cannot remove a user because %s is enabled, and "
                         + "was not set by the device owner", restriction);
                 return false;
             }
@@ -10516,7 +10516,7 @@
                 }
                 return mInjector.getIActivityManager().switchUser(userId);
             } catch (RemoteException e) {
-                Slog.e(LOG_TAG, "Couldn't switch user", e);
+                Slogf.e(LOG_TAG, "Couldn't switch user", e);
                 return false;
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
@@ -10534,19 +10534,19 @@
 
         final int userId = userHandle.getIdentifier();
         if (isManagedProfile(userId)) {
-            Slog.w(LOG_TAG, "Managed profile cannot be started in background");
+            Slogf.w(LOG_TAG, "Managed profile cannot be started in background");
             return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
         }
 
         final long id = mInjector.binderClearCallingIdentity();
         try {
             if (!mInjector.getActivityManagerInternal().canStartMoreUsers()) {
-                Slog.w(LOG_TAG, "Cannot start user %d, too many users in background", userId);
+                Slogf.w(LOG_TAG, "Cannot start user %d, too many users in background", userId);
                 return UserManager.USER_OPERATION_ERROR_MAX_RUNNING_USERS;
             }
 
             if (mInjector.getIActivityManager().startUserInBackground(userId)) {
-                Slog.i(LOG_TAG, "Started used %d in background", userId);
+                Slogf.i(LOG_TAG, "Started used %d in background", userId);
                 return UserManager.USER_OPERATION_SUCCESS;
             } else {
                 return UserManager.USER_OPERATION_ERROR_UNKNOWN;
@@ -10569,7 +10569,7 @@
 
         final int userId = userHandle.getIdentifier();
         if (isManagedProfile(userId)) {
-            Slog.w(LOG_TAG, "Managed profile cannot be stopped");
+            Slogf.w(LOG_TAG, "Managed profile cannot be stopped");
             return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
         }
 
@@ -10592,14 +10592,14 @@
         }
 
         if (isManagedProfile(callingUserId)) {
-            Slog.w(LOG_TAG, "Managed profile cannot be logout");
+            Slogf.w(LOG_TAG, "Managed profile cannot be logout");
             return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
         }
 
         final long id = mInjector.binderClearCallingIdentity();
         try {
             if (!mInjector.getIActivityManager().switchUser(UserHandle.USER_SYSTEM)) {
-                Slog.w(LOG_TAG, "Failed to switch to primary user");
+                Slogf.w(LOG_TAG, "Failed to switch to primary user");
                 // This should never happen as target user is UserHandle.USER_SYSTEM
                 return UserManager.USER_OPERATION_ERROR_UNKNOWN;
             }
@@ -10735,7 +10735,7 @@
                         suspended, null, null, null, PLATFORM_PACKAGE_NAME, caller.getUserId());
             } catch (RemoteException re) {
                 // Shouldn't happen.
-                Slog.e(LOG_TAG, "Failed talking to the package manager", re);
+                Slogf.e(LOG_TAG, "Failed talking to the package manager", re);
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
             }
@@ -10748,7 +10748,7 @@
                 .write();
 
         if (nonSuspendedPackages == null) {
-            Slog.w(LOG_TAG, "PM failed to suspend packages (%s)", Arrays.toString(packageNames));
+            Slogf.w(LOG_TAG, "PM failed to suspend packages (%s)", Arrays.toString(packageNames));
             return packageNames;
         }
         if (exemptApps.isEmpty()) {
@@ -10756,7 +10756,7 @@
         }
 
         String[] result = buildNonSuspendedPackagesUnionArray(nonSuspendedPackages, exemptApps);
-        if (VERBOSE_LOG) Slog.v(LOG_TAG, "Returning %s", Arrays.toString(result));
+        if (VERBOSE_LOG) Slogf.v(LOG_TAG, "Returning %s", Arrays.toString(result));
         return result;
     }
 
@@ -10790,7 +10790,7 @@
                 return mIPackageManager.isPackageSuspendedForUser(packageName, caller.getUserId());
             } catch (RemoteException re) {
                 // Shouldn't happen.
-                Slog.e(LOG_TAG, "Failed talking to the package manager", re);
+                Slogf.e(LOG_TAG, "Failed talking to the package manager", re);
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
             }
@@ -10978,7 +10978,7 @@
 
         List<String> exemptApps = listPolicyExemptAppsUnchecked();
         if (exemptApps.contains(packageName)) {
-            Slog.d(LOG_TAG, "setApplicationHidden(): ignoring %s as it's on policy-exempt list",
+            Slogf.d(LOG_TAG, "setApplicationHidden(): ignoring %s as it's on policy-exempt list",
                     packageName);
             return false;
         }
@@ -10998,7 +10998,7 @@
             checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_APPLICATION_HIDDEN);
 
             if (VERBOSE_LOG) {
-                Slog.v(LOG_TAG, "calling pm.setApplicationHiddenSettingAsUser(%s, %b, %d)",
+                Slogf.v(LOG_TAG, "calling pm.setApplicationHiddenSettingAsUser(%s, %b, %d)",
                         packageName, hidden, userId);
             }
             result = mInjector.binderWithCleanCallingIdentity(() -> mIPackageManager
@@ -11064,7 +11064,7 @@
             long id = mInjector.binderClearCallingIdentity();
             try {
                 if (VERBOSE_LOG) {
-                    Slog.v(LOG_TAG, "installing " + packageName + " for " + userId);
+                    Slogf.v(LOG_TAG, "installing " + packageName + " for " + userId);
                 }
 
                 Preconditions.checkArgument(isDemo || isSystemApp(mIPackageManager, packageName,
@@ -11082,7 +11082,7 @@
                 }
             } catch (RemoteException re) {
                 // shouldn't happen
-                Slog.wtf(LOG_TAG, "Failed to install " + packageName, re);
+                Slogf.wtf(LOG_TAG, "Failed to install " + packageName, re);
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
             }
@@ -11116,7 +11116,7 @@
                         .getList();
 
                 if (VERBOSE_LOG) {
-                    Slog.d(LOG_TAG, "Enabling system activities: " + activitiesToEnable);
+                    Slogf.d(LOG_TAG, "Enabling system activities: " + activitiesToEnable);
                 }
                 if (activitiesToEnable != null) {
                     for (ResolveInfo info : activitiesToEnable) {
@@ -11129,7 +11129,7 @@
                                         PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
                                         PackageManager.INSTALL_REASON_POLICY, null);
                             } else {
-                                Slog.d(LOG_TAG, "Not enabling " + packageName + " since is not a"
+                                Slogf.d(LOG_TAG, "Not enabling " + packageName + " since is not a"
                                         + " system app");
                             }
                         }
@@ -11137,7 +11137,7 @@
                 }
             } catch (RemoteException e) {
                 // shouldn't happen
-                Slog.wtf(LOG_TAG, "Failed to resolve intent for: " + intent);
+                Slogf.wtf(LOG_TAG, "Failed to resolve intent for: " + intent);
                 return 0;
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
@@ -11181,7 +11181,7 @@
             final long id = mInjector.binderClearCallingIdentity();
             try {
                 if (VERBOSE_LOG) {
-                    Slog.v(LOG_TAG, "installing " + packageName + " for " + caller.getUserId());
+                    Slogf.v(LOG_TAG, "installing " + packageName + " for " + caller.getUserId());
                 }
 
                 // Install the package.
@@ -11294,7 +11294,7 @@
                 mIPackageManager.setBlockUninstallForUser(packageName, uninstallBlocked, userId);
             } catch (RemoteException re) {
                 // Shouldn't happen.
-                Slog.e(LOG_TAG, "Failed to setBlockUninstallForUser", re);
+                Slogf.e(LOG_TAG, "Failed to setBlockUninstallForUser", re);
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
             }
@@ -11332,7 +11332,7 @@
                 return mIPackageManager.getBlockUninstallForUser(packageName, userId);
             } catch (RemoteException re) {
                 // Shouldn't happen.
-                Slog.e(LOG_TAG, "Failed to getBlockUninstallForUser", re);
+                Slogf.e(LOG_TAG, "Failed to getBlockUninstallForUser", re);
             } finally {
                 mInjector.binderRestoreCallingIdentity(id);
             }
@@ -11457,7 +11457,7 @@
                 }
                 if (isCrossProfileQuickContactDisabled(managedUserId)) {
                     if (VERBOSE_LOG) {
-                        Slog.v(LOG_TAG, "Cross-profile contacts access disabled for user %d",
+                        Slogf.v(LOG_TAG, "Cross-profile contacts access disabled for user %d",
                                 managedUserId);
                     }
                     return;
@@ -11481,16 +11481,16 @@
      * Otherwise -1.
      */
     public int getManagedUserId(@UserIdInt int callingUserId) {
-        if (VERBOSE_LOG) Slog.v(LOG_TAG, "getManagedUserId: callingUserId=%d", callingUserId);
+        if (VERBOSE_LOG) Slogf.v(LOG_TAG, "getManagedUserId: callingUserId=%d", callingUserId);
 
         for (UserInfo ui : mUserManager.getProfiles(callingUserId)) {
             if (ui.id == callingUserId || !ui.isManagedProfile()) {
                 continue; // Caller user self, or not a managed profile.  Skip.
             }
-            if (VERBOSE_LOG) Slog.v(LOG_TAG, "Managed user=%d", ui.id);
+            if (VERBOSE_LOG) Slogf.v(LOG_TAG, "Managed user=%d", ui.id);
             return ui.id;
         }
-        if (VERBOSE_LOG)  Slog.v(LOG_TAG, "Managed user not found.");
+        if (VERBOSE_LOG)  Slogf.v(LOG_TAG, "Managed user not found.");
         return -1;
     }
 
@@ -11724,13 +11724,13 @@
 
                 final List<String> lockTaskPackages = getUserData(userId).mLockTaskPackages;
                 if (!lockTaskPackages.isEmpty()) {
-                    Slog.d(LOG_TAG,
+                    Slogf.d(LOG_TAG,
                             "User id " + userId + " not affiliated. Clearing lock task packages");
                     setLockTaskPackagesLocked(userId, Collections.<String>emptyList());
                 }
                 final int lockTaskFeatures = getUserData(userId).mLockTaskFeatures;
                 if (lockTaskFeatures != DevicePolicyManager.LOCK_TASK_FEATURE_NONE){
-                    Slog.d(LOG_TAG,
+                    Slogf.d(LOG_TAG,
                             "User id " + userId + " not affiliated. Clearing lock task features");
                     setLockTaskFeaturesLocked(userId, DevicePolicyManager.LOCK_TASK_FEATURE_NONE);
                 }
@@ -11789,7 +11789,7 @@
             // Some settings are no supported any more. However we do not want to throw a
             // SecurityException to avoid breaking apps.
             if (GLOBAL_SETTINGS_DEPRECATED.contains(setting)) {
-                Slog.i(LOG_TAG, "Global setting no longer supported: %s", setting);
+                Slogf.i(LOG_TAG, "Global setting no longer supported: %s", setting);
                 return;
             }
 
@@ -11910,7 +11910,7 @@
         if (targetInfo != null) {
             intent.setComponent(targetInfo.getComponentName());
         } else {
-            Slog.wtf(LOG_TAG, "Failed to resolve intent for location settings");
+            Slogf.wtf(LOG_TAG, "Failed to resolve intent for location settings");
         }
 
         // Simple notification clicks are immutable
@@ -12005,7 +12005,7 @@
                             + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY + " instead.");
                 }
                 if (!mUserManager.isManagedProfile(callingUserId)) {
-                    Slog.e(LOG_TAG, "Ignoring setSecureSetting request for "
+                    Slogf.e(LOG_TAG, "Ignoring setSecureSetting request for "
                             + setting + ". User restriction "
                             + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES + " or "
                             + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY
@@ -12020,7 +12020,7 @@
                                 .setStrings(setting, value)
                                 .write();
                     } catch (NumberFormatException exc) {
-                        Slog.e(LOG_TAG, "Invalid value: " + value + " for setting " + setting);
+                        Slogf.e(LOG_TAG, "Invalid value: " + value + " for setting " + setting);
                     }
                 }
                 return;
@@ -12171,7 +12171,7 @@
                     isLockTaskMode = mInjector.getIActivityTaskManager().getLockTaskModeState()
                             != LOCK_TASK_MODE_NONE;
                 } catch (RemoteException e) {
-                    Slog.e(LOG_TAG, "Failed to get LockTask mode");
+                    Slogf.e(LOG_TAG, "Failed to get LockTask mode");
                 }
                 if (!isLockTaskMode) {
                     if (!setStatusBarDisabledInternal(disabled, userId)) {
@@ -12203,7 +12203,7 @@
                 return true;
             }
         } catch (RemoteException e) {
-            Slog.e(LOG_TAG, "Failed to disable the status bar", e);
+            Slogf.e(LOG_TAG, "Failed to disable the status bar", e);
         } finally {
             mInjector.binderRestoreCallingIdentity(ident);
         }
@@ -12535,7 +12535,7 @@
             synchronized (getLockObject()) {
                 if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_PRINTING,
                         UserHandle.of(userId))) {
-                    Slog.e(LOG_TAG, "printing is enabled for user %d", userId);
+                    Slogf.e(LOG_TAG, "printing is enabled for user %d", userId);
                     return null;
                 }
                 String ownerPackage = mOwners.getProfileOwnerPackage(userId);
@@ -12548,22 +12548,22 @@
                     try {
                         return pm.getPackageInfo(packageName, 0);
                     } catch (NameNotFoundException e) {
-                        Slog.e(LOG_TAG, "getPackageInfo error", e);
+                        Slogf.e(LOG_TAG, "getPackageInfo error", e);
                         return null;
                     }
                 });
                 if (packageInfo == null) {
-                    Slog.e(LOG_TAG, "packageInfo is inexplicably null");
+                    Slogf.e(LOG_TAG, "packageInfo is inexplicably null");
                     return null;
                 }
                 ApplicationInfo appInfo = packageInfo.applicationInfo;
                 if (appInfo == null) {
-                    Slog.e(LOG_TAG, "appInfo is inexplicably null");
+                    Slogf.e(LOG_TAG, "appInfo is inexplicably null");
                     return null;
                 }
                 CharSequence appLabel = pm.getApplicationLabel(appInfo);
                 if (appLabel == null) {
-                    Slog.e(LOG_TAG, "appLabel is inexplicably null");
+                    Slogf.e(LOG_TAG, "appLabel is inexplicably null");
                     return null;
                 }
                 return ((Context) ActivityThread.currentActivityThread().getSystemUiContext())
@@ -12617,7 +12617,7 @@
             Objects.requireNonNull(intent);
             Objects.requireNonNull(parentHandle);
             final int userId = parentHandle.getIdentifier();
-            Slog.i(LOG_TAG, "Sending %s broadcast to manifest receivers.", intent.getAction());
+            Slogf.i(LOG_TAG, "Sending %s broadcast to manifest receivers.", intent.getAction());
             try {
                 final List<ResolveInfo> receivers = mIPackageManager.queryIntentReceivers(
                         intent, /* resolvedType= */ null,
@@ -12627,7 +12627,7 @@
                     if (checkCrossProfilePackagePermissions(packageName, userId,
                             requiresPermission)
                             || checkModifyQuietModePermission(packageName, userId)) {
-                        Slog.i(LOG_TAG, "Sending %s broadcast to %s.", intent.getAction(),
+                        Slogf.i(LOG_TAG, "Sending %s broadcast to %s.", intent.getAction(),
                                 packageName);
                         final Intent packageIntent = new Intent(intent)
                                 .setComponent(receiver.getComponentInfo().getComponentName())
@@ -12636,7 +12636,7 @@
                     }
                 }
             } catch (RemoteException ex) {
-                Slog.w(LOG_TAG, "Cannot get list of broadcast receivers for %s because: %s.",
+                Slogf.w(LOG_TAG, "Cannot get list of broadcast receivers for %s because: %s.",
                         intent.getAction(), ex);
             }
         }
@@ -12655,7 +12655,7 @@
                         android.Manifest.permission.MODIFY_QUIET_MODE, uid, /* owningUid= */
                         -1, /* exported= */ true);
             } catch (NameNotFoundException ex) {
-                Slog.w(LOG_TAG, "Cannot find the package %s to check for permissions.",
+                Slogf.w(LOG_TAG, "Cannot find the package %s to check for permissions.",
                         packageName);
                 return false;
             }
@@ -12685,7 +12685,7 @@
                 return crossProfileAppsService.verifyPackageHasInteractAcrossProfilePermission(
                         packageName, userId);
             } catch (NameNotFoundException ex) {
-                Slog.w(LOG_TAG, "Cannot find the package %s to check for permissions.",
+                Slogf.w(LOG_TAG, "Cannot find the package %s to check for permissions.",
                         packageName);
                 return false;
             }
@@ -12760,7 +12760,7 @@
             // TODO(b/178494483): use EventLog instead
             // TODO(b/178494483): log metrics?
             if (VERBOSE_LOG) {
-                Slog.v(LOG_TAG, "notifyUnsafeOperationStateChanged(): %s=%b",
+                Slogf.v(LOG_TAG, "notifyUnsafeOperationStateChanged(): %s=%b",
                         DevicePolicyManager.operationSafetyReasonToString(reason), isSafe);
             }
             Preconditions.checkArgument(mSafetyChecker == checker,
@@ -12771,12 +12771,12 @@
             extras.putBoolean(DeviceAdminReceiver.EXTRA_OPERATION_SAFETY_STATE, isSafe);
 
             if (mOwners.hasDeviceOwner()) {
-                if (VERBOSE_LOG) Slog.v(LOG_TAG, "Notifying DO");
+                if (VERBOSE_LOG) Slogf.v(LOG_TAG, "Notifying DO");
                 sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED,
                         extras);
             }
             for (int profileOwnerId : mOwners.getProfileOwnerKeys()) {
-                if (VERBOSE_LOG) Slog.v(LOG_TAG, "Notifying PO for user " + profileOwnerId);
+                if (VERBOSE_LOG) Slogf.v(LOG_TAG, "Notifying PO for user " + profileOwnerId);
                 sendProfileOwnerCommand(DeviceAdminReceiver.ACTION_OPERATION_SAFETY_STATE_CHANGED,
                         extras, profileOwnerId);
             }
@@ -12893,7 +12893,7 @@
         synchronized (getLockObject()) {
             SystemUpdatePolicy policy =  mOwners.getSystemUpdatePolicy();
             if (policy != null && !policy.isValid()) {
-                Slog.w(LOG_TAG, "Stored system update policy is invalid, return null instead.");
+                Slogf.w(LOG_TAG, "Stored system update policy is invalid, return null instead.");
                 return null;
             }
             return policy;
@@ -12922,7 +12922,7 @@
      * @see SystemUpdatePolicy#validateAgainstPreviousFreezePeriod
      */
     private void updateSystemUpdateFreezePeriodsRecord(boolean saveIfChanged) {
-        Slog.d(LOG_TAG, "updateSystemUpdateFreezePeriodsRecord");
+        Slogf.d(LOG_TAG, "updateSystemUpdateFreezePeriodsRecord");
         synchronized (getLockObject()) {
             final SystemUpdatePolicy policy = mOwners.getSystemUpdatePolicy();
             if (policy == null) {
@@ -12972,7 +12972,7 @@
                         + "clearSystemUpdatePolicyFreezePeriodRecord");
         synchronized (getLockObject()) {
             // Print out current record to help diagnosed CTS failures
-            Slog.i(LOG_TAG, "Clear freeze period record: "
+            Slogf.i(LOG_TAG, "Clear freeze period record: "
                     + mOwners.getSystemUpdateFreezePeriodRecordAsString());
             if (mOwners.setSystemUpdateFreezePeriodRecord(null, null)) {
                 mOwners.writeDeviceOwner();
@@ -13013,8 +13013,8 @@
                 "Only the system update service can broadcast update information");
 
         if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
-            Slog.w(LOG_TAG, "Only the system update service in the system user " +
-                    "can broadcast update information.");
+            Slogf.w(LOG_TAG, "Only the system update service in the system user can broadcast "
+                    + "update information.");
             return;
         }
 
@@ -13043,7 +13043,7 @@
                 runningUserIds = mInjector.getIActivityManager().getRunningUserIds();
             } catch (RemoteException e) {
                 // Shouldn't happen.
-                Slog.e(LOG_TAG, "Could not retrieve the list of running users", e);
+                Slogf.e(LOG_TAG, "Could not retrieve the list of running users", e);
                 return;
             }
             // Send broadcasts to corresponding profile owners if any.
@@ -13171,7 +13171,7 @@
                                     });
                 }
             } catch (SecurityException e) {
-                Slog.e(LOG_TAG, "Could not set permission grant state", e);
+                Slogf.e(LOG_TAG, "Could not set permission grant state", e);
 
                 callback.sendResult(null);
             } finally {
@@ -13288,7 +13288,7 @@
         }
         final int code = checkProvisioningPreConditionSkipPermissionNoLog(action, packageName);
         if (code != CODE_OK) {
-            Slog.d(LOG_TAG, "checkProvisioningPreCondition(" + action + ", " + packageName
+            Slogf.d(LOG_TAG, "checkProvisioningPreCondition(" + action + ", " + packageName
                     + ") failed: "
                     + computeProvisioningErrorString(code, mInjector.userHandleGetCallingUserId()));
         }
@@ -13353,7 +13353,7 @@
 
         if (isHeadlessSystemUserMode) {
             if (deviceOwnerUserId != UserHandle.USER_SYSTEM) {
-                Slog.e(LOG_TAG, "In headless system user mode, "
+                Slogf.e(LOG_TAG, "In headless system user mode, "
                         + "device owner can only be set on headless system user.");
                 return CODE_NOT_SYSTEM_USER;
             }
@@ -13377,7 +13377,7 @@
                 if (callingUserId != currentForegroundUser
                         && mInjector.userManagerIsHeadlessSystemUserMode()
                         && currentForegroundUser == UserHandle.USER_SYSTEM) {
-                    Slog.wtf(LOG_TAG, "In headless system user mode, "
+                    Slogf.wtf(LOG_TAG, "In headless system user mode, "
                             + "current user cannot be system user when setting device owner");
                     return CODE_SYSTEM_USER;
                 }
@@ -13404,7 +13404,7 @@
             final int deviceOwnerUserId = mInjector.userManagerIsHeadlessSystemUserMode()
                     ? UserHandle.USER_SYSTEM
                     : callingUserId;
-            Slog.i(LOG_TAG, "Calling user %d, device owner will be set on user %d",
+            Slogf.i(LOG_TAG, "Calling user %d, device owner will be set on user %d",
                     callingUserId, deviceOwnerUserId);
             // hasIncompatibleAccountsOrNonAdb doesn't matter since the caller is not adb.
             return checkDeviceOwnerProvisioningPreConditionLocked(/* owner unknown */ null,
@@ -13435,7 +13435,8 @@
                     UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserHandle);
 
             if (mUserManager.getUserInfo(callingUserId).isProfile()) {
-                Slog.i(LOG_TAG, "Calling user %d is a profile, cannot add another.", callingUserId);
+                Slogf.i(LOG_TAG, "Calling user %d is a profile, cannot add another.",
+                        callingUserId);
                 // The check is called from inside a managed profile. A managed profile cannot
                 // be provisioned from within another managed profile.
                 return CODE_CANNOT_ADD_MANAGED_PROFILE;
@@ -13443,12 +13444,12 @@
 
             // If there's a device owner, the restriction on adding a managed profile must be set.
             if (hasDeviceOwner && !addingProfileRestricted) {
-                Slog.wtf(LOG_TAG, "Has a device owner but no restriction on adding a profile.");
+                Slogf.wtf(LOG_TAG, "Has a device owner but no restriction on adding a profile.");
             }
 
             // Do not allow adding a managed profile if there's a restriction.
             if (addingProfileRestricted) {
-                Slog.i(LOG_TAG, "Adding a profile is restricted: User %s Has device owner? %b",
+                Slogf.i(LOG_TAG, "Adding a profile is restricted: User %s Has device owner? %b",
                         callingUserHandle, hasDeviceOwner);
                 return CODE_CANNOT_ADD_MANAGED_PROFILE;
             }
@@ -13456,7 +13457,7 @@
             // Bail out if we are trying to provision a work profile but one already exists.
             if (!mUserManager.canAddMoreManagedProfiles(
                     callingUserId, /* allowedToRemoveOne= */ false)) {
-                Slog.i(LOG_TAG, "A work profile already exists.");
+                Slogf.i(LOG_TAG, "A work profile already exists.");
                 return CODE_CANNOT_ADD_MANAGED_PROFILE;
             }
         } finally {
@@ -13944,7 +13945,7 @@
                     who.flattenToString(), userId));
         }
 
-        Slog.i(LOG_TAG, "Marking %s as profile owner on organization-owned device for user %d",
+        Slogf.i(LOG_TAG, "Marking %s as profile owner on organization-owned device for user %d",
                 who.flattenToString(), userId);
 
         // First, set restriction on removing the profile.
@@ -14089,7 +14090,7 @@
             for (int i = 0; i < userInfos.size(); i++) {
                 int userId = userInfos.get(i).id;
                 if (!isUserAffiliatedWithDeviceLocked(userId)) {
-                    Slog.d(LOG_TAG, "User id " + userId + " not affiliated.");
+                    Slogf.d(LOG_TAG, "User id " + userId + " not affiliated.");
                     return false;
                 }
             }
@@ -14218,7 +14219,7 @@
             }
             return new ParceledListSlice<SecurityEvent>(output);
         } catch (IOException e) {
-            Slog.w(LOG_TAG, "Fail to read previous events" , e);
+            Slogf.w(LOG_TAG, "Fail to read previous events" , e);
             return new ParceledListSlice<SecurityEvent>(Collections.<SecurityEvent>emptyList());
         }
     }
@@ -14387,7 +14388,7 @@
         try { // force stop the package before uninstalling
             mInjector.getIActivityManager().forceStopPackage(packageName, userId);
         } catch (RemoteException re) {
-            Slog.e(LOG_TAG, "Failure talking to ActivityManager while force stopping package");
+            Slogf.e(LOG_TAG, "Failure talking to ActivityManager while force stopping package");
         }
         final Uri packageURI = Uri.parse("package:" + packageName);
         final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
@@ -14423,7 +14424,7 @@
             updateMaximumTimeToLockLocked(userHandle);
             policy.mRemovingAdmins.remove(adminReceiver);
 
-            Slog.i(LOG_TAG, "Device admin " + adminReceiver + " removed from user " + userHandle);
+            Slogf.i(LOG_TAG, "Device admin " + adminReceiver + " removed from user " + userHandle);
         }
         // The removed admin might have disabled camera, so update user
         // restrictions.
@@ -14643,7 +14644,7 @@
             }
             synchronized (getLockObject()) {
                 if (owner == null || !isAdminTestOnlyLocked(owner, userId)) {
-                    Slog.w(LOG_TAG,
+                    Slogf.w(LOG_TAG,
                             "Non test-only owner can't be installed with existing accounts.");
                     return true;
                 }
@@ -14657,20 +14658,20 @@
             boolean compatible = true;
             for (Account account : accounts) {
                 if (hasAccountFeatures(am, account, feature_disallow)) {
-                    Slog.e(LOG_TAG, "%s has %s", account, feature_disallow[0]);
+                    Slogf.e(LOG_TAG, "%s has %s", account, feature_disallow[0]);
                     compatible = false;
                     break;
                 }
                 if (!hasAccountFeatures(am, account, feature_allow)) {
-                    Slog.e(LOG_TAG, "%s doesn't have %s", account, feature_allow[0]);
+                    Slogf.e(LOG_TAG, "%s doesn't have %s", account, feature_allow[0]);
                     compatible = false;
                     break;
                 }
             }
             if (compatible) {
-                Slog.w(LOG_TAG, "All accounts are compatible");
+                Slogf.w(LOG_TAG, "All accounts are compatible");
             } else {
-                Slog.e(LOG_TAG, "Found incompatible accounts");
+                Slogf.e(LOG_TAG, "Found incompatible accounts");
             }
             return !compatible;
         });
@@ -14680,7 +14681,7 @@
         try {
             return am.hasFeatures(account, features, null, null).getResult();
         } catch (Exception e) {
-            Slog.w(LOG_TAG, "Failed to get account feature", e);
+            Slogf.w(LOG_TAG, "Failed to get account feature", e);
             return false;
         }
     }
@@ -14740,14 +14741,14 @@
                     }
                     if (!mNetworkLogger.startNetworkLogging()) {
                         mNetworkLogger = null;
-                        Slog.wtf(LOG_TAG, "Network logging could not be started due to the logging"
+                        Slogf.wtf(LOG_TAG, "Network logging could not be started due to the logging"
                                 + " service not being available yet.");
                     }
                     maybePauseDeviceWideLoggingLocked();
                     sendNetworkLoggingNotificationLocked();
                 } else {
                     if (mNetworkLogger != null && !mNetworkLogger.stopNetworkLogging()) {
-                        Slog.wtf(LOG_TAG, "Network logging could not be stopped due to the logging"
+                        Slogf.wtf(LOG_TAG, "Network logging could not be stopped due to the logging"
                                 + " service not being available yet.");
                     }
                     mNetworkLogger = null;
@@ -14799,14 +14800,14 @@
     private void maybePauseDeviceWideLoggingLocked() {
         if (!areAllUsersAffiliatedWithDeviceLocked()) {
             if (mOwners.hasDeviceOwner()) {
-                Slog.i(LOG_TAG, "There are unaffiliated users, network logging will be "
+                Slogf.i(LOG_TAG, "There are unaffiliated users, network logging will be "
                         + "paused if enabled.");
                 if (mNetworkLogger != null) {
                     mNetworkLogger.pause();
                 }
             }
             if (!isOrganizationOwnedDeviceWithManagedProfile()) {
-                Slog.i(LOG_TAG, "Not org-owned managed profile device, security logging will be "
+                Slogf.i(LOG_TAG, "Not org-owned managed profile device, security logging will be "
                         + "paused if enabled.");
                 mSecurityLogMonitor.pause();
             }
@@ -14980,7 +14981,7 @@
                 0,  // flags
                 targetUserId);
         if (info == null || info.serviceInfo == null) {
-            Slog.e(LOG_TAG, "Fail to look up the service: %s or user %d is not running", rawIntent,
+            Slogf.e(LOG_TAG, "Fail to look up the service: %s or user %d is not running", rawIntent,
                     targetUserId);
             return null;
         }
@@ -15116,7 +15117,7 @@
                 return resetPasswordInternal(password, policy.mPasswordTokenHandle, token,
                         flags, caller);
             } else {
-                Slog.w(LOG_TAG, "No saved token handle");
+                Slogf.w(LOG_TAG, "No saved token handle");
             }
         }
         return false;
@@ -15166,8 +15167,8 @@
             // This can happen e.g. for device admin packages, do not throw out the exception,
             // because callers have no means to know beforehand for which packages this might
             // happen. If so, we send back that removal failed.
-            Slog.w(LOG_TAG, "Not allowed to clear application user data for package " + packageName,
-                    se);
+            Slogf.w(LOG_TAG, "Not allowed to clear application user data for package "
+                    + packageName, se);
             try {
                 callback.onRemoveCompleted(packageName, false);
             } catch (RemoteException re) {
@@ -15323,7 +15324,7 @@
             int profileOwnerUserId) {
         transferActiveAdminUncheckedLocked(target, admin, profileOwnerUserId);
         mOwners.transferProfileOwner(target, profileOwnerUserId);
-        Slog.i(LOG_TAG, "Profile owner set: " + target + " on user " + profileOwnerUserId);
+        Slogf.i(LOG_TAG, "Profile owner set: " + target + " on user " + profileOwnerUserId);
         mOwners.writeProfileOwner(profileOwnerUserId);
         mDeviceAdminServiceController.startServiceForOwner(
                 target.getPackageName(), profileOwnerUserId, "transfer-profile-owner");
@@ -15335,7 +15336,7 @@
     private void transferDeviceOwnershipLocked(ComponentName admin, ComponentName target, int userId) {
         transferActiveAdminUncheckedLocked(target, admin, userId);
         mOwners.transferDeviceOwnership(target);
-        Slog.i(LOG_TAG, "Device owner set: " + target + " on user " + userId);
+        Slogf.i(LOG_TAG, "Device owner set: " + target + " on user " + userId);
         mOwners.writeDeviceOwner();
         mDeviceAdminServiceController.startServiceForOwner(
                 target.getPackageName(), userId, "transfer-device-owner");
@@ -15453,7 +15454,7 @@
                 parser.next();
                 return PersistableBundle.restoreFromXml(parser);
             } catch (IOException | XmlPullParserException | IllegalArgumentException e) {
-                Slog.e(LOG_TAG, "Caught exception while trying to load the "
+                Slogf.e(LOG_TAG, "Caught exception while trying to load the "
                         + "owner transfer parameters from file " + bundleFile, e);
                 return null;
             }
@@ -15475,7 +15476,7 @@
             return mInjector.binderWithCleanCallingIdentity(
                     () -> tm.addDevicePolicyOverrideApn(mContext, apnSetting));
         } else {
-            Slog.w(LOG_TAG, "TelephonyManager is null when trying to add override apn");
+            Slogf.w(LOG_TAG, "TelephonyManager is null when trying to add override apn");
             return Telephony.Carriers.INVALID_APN_ID;
         }
     }
@@ -15499,7 +15500,7 @@
             return mInjector.binderWithCleanCallingIdentity(
                     () -> tm.modifyDevicePolicyOverrideApn(mContext, apnId, apnSetting));
         } else {
-            Slog.w(LOG_TAG, "TelephonyManager is null when trying to modify override apn");
+            Slogf.w(LOG_TAG, "TelephonyManager is null when trying to modify override apn");
             return false;
         }
     }
@@ -15542,7 +15543,7 @@
             return mInjector.binderWithCleanCallingIdentity(
                     () -> tm.getDevicePolicyOverrideApns(mContext));
         }
-        Slog.w(LOG_TAG, "TelephonyManager is null when trying to get override apns");
+        Slogf.w(LOG_TAG, "TelephonyManager is null when trying to get override apns");
         return Collections.emptyList();
     }
 
@@ -15587,7 +15588,7 @@
                 return enforceCursor.getInt(enforceCursor.getColumnIndex(ENFORCE_KEY)) == 1;
             }
         } catch (IllegalArgumentException e) {
-            Slog.e(LOG_TAG, "Cursor returned from ENFORCE_MANAGED_URI doesn't contain "
+            Slogf.e(LOG_TAG, "Cursor returned from ENFORCE_MANAGED_URI doesn't contain "
                     + "correct info.", e);
         } finally {
             enforceCursor.close();
@@ -15612,7 +15613,7 @@
             serializer.endDocument();
             atomicFile.finishWrite(stream);
         } catch (IOException | XmlPullParserException e) {
-            Slog.e(LOG_TAG, "Caught exception while trying to save the "
+            Slogf.e(LOG_TAG, "Caught exception while trying to save the "
                     + "owner transfer parameters to file " + parametersFile, e);
             parametersFile.delete();
             atomicFile.failWrite(stream);
@@ -16042,7 +16043,7 @@
                 return false;
             }
             if (!isPackageAllowedToAccessCalendarForUser(packageName, workProfileUserId)) {
-                Slog.d(LOG_TAG, "Package %s is not allowed to access cross-profile calendar APIs",
+                Slogf.d(LOG_TAG, "Package %s is not allowed to access cross-profile calendar APIs",
                         packageName);
                 return false;
             }
@@ -16057,7 +16058,7 @@
             try {
                 mContext.startActivityAsUser(intent, UserHandle.of(workProfileUserId));
             } catch (ActivityNotFoundException e) {
-                Slog.e(LOG_TAG, "View event activity not found", e);
+                Slogf.e(LOG_TAG, "View event activity not found", e);
                 return false;
             }
             return true;
@@ -16071,7 +16072,7 @@
                         packageName, UserHandle.getUserId(callingUid));
                 return packageUid == callingUid;
             } catch (NameNotFoundException e) {
-                Slog.d(LOG_TAG, "Calling package not found", e);
+                Slogf.d(LOG_TAG, "Calling package not found", e);
                 return false;
             }
         });
@@ -16181,7 +16182,7 @@
             final long deadline = admin.mProfileOffDeadline;
             final int result = makeSuspensionReasons(admin.mSuspendPersonalApps,
                     deadline != 0 && mInjector.systemCurrentTimeMillis() > deadline);
-            Slog.d(LOG_TAG, "getPersonalAppsSuspendedReasons user: %d; result: %d",
+            Slogf.d(LOG_TAG, "getPersonalAppsSuspendedReasons user: %d; result: %d",
                     mInjector.userHandleGetCallingUserId(), result);
             return result;
         }
@@ -16239,7 +16240,7 @@
         synchronized (getLockObject()) {
             final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(profileUserId);
             if (profileOwner == null) {
-                Slog.wtf(LOG_TAG, "Profile owner not found for compliance check");
+                Slogf.wtf(LOG_TAG, "Profile owner not found for compliance check");
                 return;
             }
             if (suspended) {
@@ -16271,7 +16272,7 @@
                         updateProfileOffDeadlineLocked(profileUserId, profileOwner, unlocked);
                 final boolean suspendedExplicitly = profileOwner.mSuspendPersonalApps;
                 final boolean suspendedByTimeout = profileOwner.mProfileOffDeadline == -1;
-                Slog.d(LOG_TAG,
+                Slogf.d(LOG_TAG,
                         "Personal apps suspended explicitly: %b, by timeout: %b, notification: %d",
                         suspendedExplicitly, suspendedByTimeout, notificationState);
                 updateProfileOffDeadlineNotificationLocked(
@@ -16296,7 +16297,7 @@
             int profileUserId, ActiveAdmin profileOwner, boolean unlocked) {
         final long now = mInjector.systemCurrentTimeMillis();
         if (profileOwner.mProfileOffDeadline != 0 && now > profileOwner.mProfileOffDeadline) {
-            Slog.i(LOG_TAG, "Profile off deadline has been reached, unlocked: " + unlocked);
+            Slogf.i(LOG_TAG, "Profile off deadline has been reached, unlocked: " + unlocked);
             if (profileOwner.mProfileOffDeadline != -1) {
                 // Move the deadline far to the past so that it cannot be rolled back by TZ change.
                 profileOwner.mProfileOffDeadline = -1;
@@ -16315,14 +16316,14 @@
                 && (profileOwner.mProfileMaximumTimeOffMillis == 0)) {
             // There is a deadline but either there is no policy -> clear
             // the deadline.
-            Slog.i(LOG_TAG, "Profile off deadline is reset to zero");
+            Slogf.i(LOG_TAG, "Profile off deadline is reset to zero");
             profileOwner.mProfileOffDeadline = 0;
             shouldSaveSettings = true;
         } else if (profileOwner.mProfileOffDeadline == 0
                 && (profileOwner.mProfileMaximumTimeOffMillis != 0 && !unlocked)) {
             // There profile is locked and there is a policy, but the deadline is not set -> set the
             // deadline.
-            Slog.i(LOG_TAG, "Profile off deadline is set.");
+            Slogf.i(LOG_TAG, "Profile off deadline is set.");
             profileOwner.mProfileOffDeadline = now + profileOwner.mProfileMaximumTimeOffMillis;
             shouldSaveSettings = true;
         }
@@ -16357,10 +16358,10 @@
                         | PendingIntent.FLAG_IMMUTABLE);
 
         if (alarmTime == 0) {
-            Slog.i(LOG_TAG, "Profile off deadline alarm is removed.");
+            Slogf.i(LOG_TAG, "Profile off deadline alarm is removed.");
             am.cancel(pi);
         } else {
-            Slog.i(LOG_TAG, "Profile off deadline alarm is set.");
+            Slogf.i(LOG_TAG, "Profile off deadline alarm is set.");
             am.set(AlarmManager.RTC, alarmTime, pi);
         }
 
@@ -16371,7 +16372,7 @@
         if (getUserData(userId).mAppsSuspended == suspended) {
             return;
         }
-        Slog.i(LOG_TAG, "%s personal apps for user %d", suspended ? "Suspending" : "Unsuspending",
+        Slogf.i(LOG_TAG, "%s personal apps for user %d", suspended ? "Suspending" : "Unsuspending",
                 userId);
 
         if (suspended) {
@@ -16394,11 +16395,11 @@
                 final String[] failedApps = mIPackageManager.setPackagesSuspendedAsUser(
                         appsToSuspend, true, null, null, null, PLATFORM_PACKAGE_NAME, userId);
                 if (!ArrayUtils.isEmpty(failedApps)) {
-                    Slog.wtf(LOG_TAG, "Failed to suspend apps: " + String.join(",", failedApps));
+                    Slogf.wtf(LOG_TAG, "Failed to suspend apps: " + String.join(",", failedApps));
                 }
             } catch (RemoteException re) {
                 // Shouldn't happen.
-                Slog.e(LOG_TAG, "Failed talking to the package manager", re);
+                Slogf.e(LOG_TAG, "Failed talking to the package manager", re);
             }
         });
     }
@@ -16568,17 +16569,17 @@
                 poAppInfo = mIPackageManager.getApplicationInfo(
                         poAdmin.info.getPackageName(), 0 /* flags */, userId);
             } catch (RemoteException e) {
-                Slog.e(LOG_TAG, "Failed to query PO app info", e);
+                Slogf.e(LOG_TAG, "Failed to query PO app info", e);
                 return false;
             }
             if (poAppInfo == null) {
-                Slog.wtf(LOG_TAG, "Cannot find AppInfo for profile owner");
+                Slogf.wtf(LOG_TAG, "Cannot find AppInfo for profile owner");
                 return false;
             }
             if (!poAppInfo.isEncryptionAware()) {
                 return false;
             }
-            Slog.d(LOG_TAG, "PO should be able to reset password from direct boot");
+            Slogf.d(LOG_TAG, "PO should be able to reset password from direct boot");
             return true;
         }
     }
@@ -16618,7 +16619,7 @@
         Preconditions.checkArgument(!TextUtils.isEmpty(organizationId),
                 "Enterprise ID may not be empty.");
 
-        Slog.i(LOG_TAG, "Setting Enterprise ID to %s for user %d", organizationId, userId);
+        Slogf.i(LOG_TAG, "Setting Enterprise ID to %s for user %d", organizationId, userId);
 
         final String ownerPackage;
         synchronized (getLockObject()) {
@@ -16873,11 +16874,11 @@
         final UserHandle sourceUser = UserHandle.of(sourceUserId);
         final UserHandle targetUser = UserHandle.of(targetUserId);
         if (accountToMigrate == null) {
-            Slog.d(LOG_TAG, "No account to migrate.");
+            Slogf.d(LOG_TAG, "No account to migrate.");
             return;
         }
         if (sourceUser.equals(targetUser)) {
-            Slog.w(LOG_TAG, "sourceUser and targetUser are the same, won't migrate account.");
+            Slogf.w(LOG_TAG, "sourceUser and targetUser are the same, won't migrate account.");
             return;
         }
         copyAccount(targetUser, sourceUser, accountToMigrate, callerPackage);
@@ -16906,15 +16907,15 @@
                         callerPackage);
             } else {
                 logCopyAccountStatus(COPY_ACCOUNT_FAILED, callerPackage);
-                Slog.e(LOG_TAG, "Failed to copy account to " + targetUser);
+                Slogf.e(LOG_TAG, "Failed to copy account to " + targetUser);
             }
         } catch (OperationCanceledException e) {
             // Account migration is not considered a critical operation.
             logCopyAccountStatus(COPY_ACCOUNT_TIMED_OUT, callerPackage);
-            Slog.e(LOG_TAG, "Exception copying account to " + targetUser, e);
+            Slogf.e(LOG_TAG, "Exception copying account to " + targetUser, e);
         } catch (AuthenticatorException | IOException e) {
             logCopyAccountStatus(COPY_ACCOUNT_EXCEPTION, callerPackage);
-            Slog.e(LOG_TAG, "Exception copying account to " + targetUser, e);
+            Slogf.e(LOG_TAG, "Exception copying account to " + targetUser, e);
         }
     }
 
@@ -16934,22 +16935,22 @@
         try {
             final Bundle result = bundle.getResult();
             if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, /* default */ false)) {
-                Slog.i(LOG_TAG, "Account removed from the primary user.");
+                Slogf.i(LOG_TAG, "Account removed from the primary user.");
             } else {
                 // TODO(174768447): Revisit start activity logic.
                 final Intent removeIntent = result.getParcelable(AccountManager.KEY_INTENT);
                 removeIntent.addFlags(FLAG_ACTIVITY_NEW_TASK);
                 if (removeIntent != null) {
-                    Slog.i(LOG_TAG, "Starting activity to remove account");
+                    Slogf.i(LOG_TAG, "Starting activity to remove account");
                     new Handler(Looper.getMainLooper()).post(() -> {
                         mContext.startActivity(removeIntent);
                     });
                 } else {
-                    Slog.e(LOG_TAG, "Could not remove account from the primary user.");
+                    Slogf.e(LOG_TAG, "Could not remove account from the primary user.");
                 }
             }
         } catch (OperationCanceledException | AuthenticatorException | IOException e) {
-            Slog.e(LOG_TAG, "Exception removing account from the primary user.", e);
+            Slogf.e(LOG_TAG, "Exception removing account from the primary user.", e);
         }
     }
 
@@ -17042,7 +17043,7 @@
             }
         } catch (Exception e) {
             // Do not stop provisioning and ignore this error.
-            Slog.e(LOG_TAG, "Alarm manager failed to set the system time/timezone.", e);
+            Slogf.e(LOG_TAG, "Alarm manager failed to set the system time/timezone.", e);
         }
     }
 
@@ -17056,7 +17057,7 @@
             LocalePicker.updateLocale(locale);
         } catch (Exception e) {
             // Do not stop provisioning and ignore this error.
-            Slog.e(LOG_TAG, "Failed to set the system locale.", e);
+            Slogf.e(LOG_TAG, "Failed to set the system locale.", e);
         }
     }
 
@@ -17069,21 +17070,21 @@
 
         removeNonInstalledPackages(packagesToDelete, userId);
         if (packagesToDelete.isEmpty()) {
-            Slog.i(LOG_TAG, "No packages to delete on user " + userId);
+            Slogf.i(LOG_TAG, "No packages to delete on user " + userId);
             return true;
         }
 
         NonRequiredPackageDeleteObserver packageDeleteObserver =
                 new NonRequiredPackageDeleteObserver(packagesToDelete.size());
         for (String packageName : packagesToDelete) {
-            Slog.i(LOG_TAG, "Deleting package [" + packageName + "] as user " + userId);
+            Slogf.i(LOG_TAG, "Deleting package [" + packageName + "] as user " + userId);
             mContext.getPackageManager().deletePackageAsUser(
                     packageName,
                     packageDeleteObserver,
                     PackageManager.DELETE_SYSTEM_APP,
                     userId);
         }
-        Slog.i(LOG_TAG, "Waiting for non required apps to be deleted");
+        Slogf.i(LOG_TAG, "Waiting for non required apps to be deleted");
         return packageDeleteObserver.awaitPackagesDeletion();
     }
 
@@ -17099,7 +17100,7 @@
 
     private void disallowAddUser() {
         if (mInjector.userManagerIsHeadlessSystemUserMode()) {
-            Slog.i(LOG_TAG, "Not setting DISALLOW_ADD_USER on headless system user mode.");
+            Slogf.i(LOG_TAG, "Not setting DISALLOW_ADD_USER on headless system user mode.");
             return;
         }
         for (UserInfo userInfo : mUserManager.getUsers()) {
@@ -17286,7 +17287,7 @@
         }
         if (!mInjector.binderWithCleanCallingIdentity(
                 () -> mInjector.getUsbManager().enableUsbDataSignal(usbEnabled))) {
-            Slog.w(LOG_TAG, "Failed to set usb data signaling state");
+            Slogf.w(LOG_TAG, "Failed to set usb data signaling state");
         }
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
index 28a6987..964be38 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
@@ -26,10 +26,10 @@
 import android.os.UserManager;
 import android.os.storage.StorageManager;
 import android.service.persistentdata.PersistentDataBlockManager;
-import android.util.Slog;
 
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.Preconditions;
+import com.android.server.utils.Slogf;
 
 import java.io.IOException;
 import java.util.Objects;
@@ -68,16 +68,16 @@
         IResultReceiver receiver = new IResultReceiver.Stub() {
             @Override
             public void send(int resultCode, Bundle resultData) throws RemoteException {
-                Slog.i(TAG, "Factory reset confirmed by %s, proceeding", mSafetyChecker);
+                Slogf.i(TAG, "Factory reset confirmed by %s, proceeding", mSafetyChecker);
                 try {
                     factoryResetInternalUnchecked();
                 } catch (IOException e) {
                     // Shouldn't happen
-                    Slog.wtf(TAG, e, "IOException calling underlying systems");
+                    Slogf.wtf(TAG, e, "IOException calling underlying systems");
                 }
             }
         };
-        Slog.i(TAG, "Delaying factory reset until %s confirms", mSafetyChecker);
+        Slogf.i(TAG, "Delaying factory reset until %s confirms", mSafetyChecker);
         mSafetyChecker.onFactoryReset(receiver);
         return false;
     }
@@ -112,7 +112,7 @@
     }
 
     private void factoryResetInternalUnchecked() throws IOException {
-        Slog.i(TAG, "factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, "
+        Slogf.i(TAG, "factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, "
                 + "wipeAdoptableStorage=%b, wipeFRP=%b", mReason, mShutdown, mForce, mWipeEuicc,
                 mWipeAdoptableStorage, mWipeFactoryResetProtection);
 
@@ -125,15 +125,15 @@
             PersistentDataBlockManager manager = mContext
                     .getSystemService(PersistentDataBlockManager.class);
             if (manager != null) {
-                Slog.w(TAG, "Wiping factory reset protection");
+                Slogf.w(TAG, "Wiping factory reset protection");
                 manager.wipe();
             } else {
-                Slog.w(TAG, "No need to wipe factory reset protection");
+                Slogf.w(TAG, "No need to wipe factory reset protection");
             }
         }
 
         if (mWipeAdoptableStorage) {
-            Slog.w(TAG, "Wiping adoptable storage");
+            Slogf.w(TAG, "Wiping adoptable storage");
             StorageManager sm = mContext.getSystemService(StorageManager.class);
             sm.wipeAdoptableDisks();
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
index 0b9ece4..48d2d73 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
@@ -39,13 +39,13 @@
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
-import android.util.Slog;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IAccessibilityManager;
 import android.view.inputmethod.InputMethodInfo;
 
 import com.android.internal.R;
 import com.android.server.inputmethod.InputMethodManagerInternal;
+import com.android.server.utils.Slogf;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -109,7 +109,7 @@
         }
 
         if (Log.isLoggable(LOG_TAG, Log.INFO)) {
-            Slog.i(LOG_TAG, "Packages subject to suspension: %s", String.join(",", result));
+            Slogf.i(LOG_TAG, "Packages subject to suspension: %s", String.join(",", result));
         }
         return result.toArray(new String[0]);
     }
@@ -123,7 +123,7 @@
         for (final ResolveInfo resolveInfo : matchingActivities) {
             if (resolveInfo.activityInfo == null
                     || TextUtils.isEmpty(resolveInfo.activityInfo.packageName)) {
-                Slog.wtf(LOG_TAG, "Could not find package name for launcher app %s", resolveInfo);
+                Slogf.wtf(LOG_TAG, "Could not find package name for launcher app %s", resolveInfo);
                 continue;
             }
             final String packageName = resolveInfo.activityInfo.packageName;
@@ -134,7 +134,7 @@
                     result.add(packageName);
                 }
             } catch (PackageManager.NameNotFoundException e) {
-                Slog.e(LOG_TAG, "Could not find application info for launcher app: %s",
+                Slogf.e(LOG_TAG, "Could not find application info for launcher app: %s",
                         packageName);
             }
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
index 5f35a26..d7cbd9b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
@@ -45,11 +45,11 @@
 import android.provider.Settings;
 import android.text.format.DateUtils;
 import android.util.Pair;
-import android.util.Slog;
 
 import com.android.internal.R;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
+import com.android.server.utils.Slogf;
 
 import java.io.FileNotFoundException;
 import java.lang.annotation.Retention;
@@ -135,7 +135,7 @@
         if (targetInfo != null) {
             dialogIntent.setComponent(targetInfo.getComponentName());
         } else {
-            Slog.wtf(LOG_TAG, "Failed to resolve intent for remote bugreport dialog");
+            Slogf.wtf(LOG_TAG, "Failed to resolve intent for remote bugreport dialog");
         }
 
         // Simple notification clicks are immutable
@@ -191,7 +191,7 @@
     public boolean requestBugreport() {
         if (mRemoteBugreportServiceIsActive.get()
                 || (mService.getDeviceOwnerRemoteBugreportUriAndHash() != null)) {
-            Slog.d(LOG_TAG, "Remote bugreport wasn't started because there's already one running.");
+            Slogf.d(LOG_TAG, "Remote bugreport wasn't started because there's already one running");
             return false;
         }
 
@@ -208,7 +208,7 @@
             return true;
         } catch (RemoteException re) {
             // should never happen
-            Slog.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re);
+            Slogf.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re);
             return false;
         } finally {
             mInjector.binderRestoreCallingIdentity(callingIdentity);
@@ -222,7 +222,7 @@
             mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished);
         } catch (IntentFilter.MalformedMimeTypeException e) {
             // should never happen, as setting a constant
-            Slog.w(LOG_TAG, e, "Failed to set type %s", BUGREPORT_MIMETYPE);
+            Slogf.w(LOG_TAG, e, "Failed to set type %s", BUGREPORT_MIMETYPE);
         }
         final IntentFilter filterConsent = new IntentFilter();
         filterConsent.addAction(ACTION_BUGREPORT_SHARING_DECLINED);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index fbf677d..9b2a1e7 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -454,6 +454,11 @@
     private static native void startSensorService();
 
     /**
+     * Start the memtrack proxy service.
+     */
+    private static native void startMemtrackProxyService();
+
+    /**
      * Start all HIDL services that are run inside the system server. This may take some time.
      */
     private static native void startHidlServices();
@@ -1024,6 +1029,12 @@
         mSystemServiceManager.startService(PowerStatsService.class);
         t.traceEnd();
 
+        // Start MemtrackProxyService before ActivityManager, so that early calls
+        // to Memtrack::getMemory() don't fail.
+        t.traceBegin("MemtrackProxyService");
+        startMemtrackProxyService();
+        t.traceEnd();
+
         // Activity manager runs the show.
         t.traceBegin("StartActivityManager");
         // TODO: Might need to move after migration to WM.
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
index 46487ea2..411c31c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
@@ -17,8 +17,12 @@
 package com.android.server.pm
 
 import android.os.Build
+import android.os.Handler
 import android.provider.DeviceConfig
 import android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
 import com.android.server.apphibernation.AppHibernationManagerInternal
 import com.android.server.extendedtestutils.wheneverStatic
 import com.android.server.testutils.whenever
@@ -28,12 +32,12 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RunWith(JUnit4::class)
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
 class PackageManagerServiceHibernationTests {
 
     companion object {
@@ -60,6 +64,8 @@
         rule.system().stageNominalSystemState()
         whenever(rule.mocks().injector.getLocalService(AppHibernationManagerInternal::class.java))
             .thenReturn(appHibernationManager)
+        whenever(rule.mocks().injector.handler)
+            .thenReturn(Handler(TestableLooper.get(this).looper))
     }
 
     @Test
@@ -74,6 +80,9 @@
         ps!!.setStopped(true, TEST_USER_ID)
 
         pm.setPackageStoppedState(TEST_PACKAGE_NAME, false, TEST_USER_ID)
+
+        TestableLooper.get(this).processAllMessages()
+
         verify(appHibernationManager).setHibernatingForUser(TEST_PACKAGE_NAME, TEST_USER_ID, false)
         verify(appHibernationManager).setHibernatingGlobally(TEST_PACKAGE_NAME, false)
     }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 81be2e7..b7f5f4d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -331,6 +331,29 @@
     }
 
     @Test
+    public void onAccessibilityActionPerformed_magnifierEnabled_showMagnificationButton()
+            throws RemoteException {
+        setMagnificationEnabled(MODE_WINDOW);
+
+        mMagnificationController.onAccessibilityActionPerformed(TEST_DISPLAY);
+
+        verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
+                eq(MODE_WINDOW));
+    }
+
+    @Test
+    public void onAccessibilityActionPerformed_capabilityNotAll_removeMagnificationButton()
+            throws RemoteException {
+        mMagnificationController.setMagnificationCapabilities(
+                ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+        setMagnificationEnabled(MODE_WINDOW);
+
+        mMagnificationController.onAccessibilityActionPerformed(TEST_DISPLAY);
+
+        verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY));
+    }
+
+    @Test
     public void onWindowMagnificationActivationState_windowActivated_logWindowDuration() {
         mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, true);
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index ffa0185..a20272a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -313,6 +313,17 @@
     }
 
     @Test
+    public void onAccessibilityActionPerformed_magnifierEnabled_notifyAction()
+            throws RemoteException {
+        mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
+
+        mMockConnection.getConnectionCallback().onAccessibilityActionPerformed(TEST_DISPLAY);
+
+        verify(mMockCallback).onAccessibilityActionPerformed(eq(TEST_DISPLAY));
+    }
+
+    @Test
     public void binderDied_windowMagnifierIsEnabled_resetState() throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
         mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index fc4804b..41237c8 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -72,6 +72,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.util.IntArray;
 
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
@@ -121,7 +122,8 @@
         UidRecord.CHANGE_GONE,
         UidRecord.CHANGE_GONE | UidRecord.CHANGE_IDLE,
         UidRecord.CHANGE_IDLE,
-        UidRecord.CHANGE_ACTIVE
+        UidRecord.CHANGE_ACTIVE,
+        UidRecord.CHANGE_CAPABILITY,
     };
 
     private static PackageManagerInternal sPackageManagerInternal;
@@ -528,8 +530,10 @@
             ActivityManager.UID_OBSERVER_GONE,
             ActivityManager.UID_OBSERVER_IDLE,
             ActivityManager.UID_OBSERVER_ACTIVE,
+            ActivityManager.UID_OBSERVER_CAPABILITY,
             ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
                     | ActivityManager.UID_OBSERVER_ACTIVE | ActivityManager.UID_OBSERVER_IDLE
+                    | ActivityManager.UID_OBSERVER_CAPABILITY
         };
         final IUidObserver[] observers = new IUidObserver.Stub[changesToObserve.length];
         for (int i = 0; i < observers.length; ++i) {
@@ -553,7 +557,16 @@
             ActivityManager.PROCESS_STATE_NONEXISTENT,
             ActivityManager.PROCESS_STATE_CACHED_EMPTY,
             ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
-            ActivityManager.PROCESS_STATE_TOP
+            ActivityManager.PROCESS_STATE_TOP,
+            ActivityManager.PROCESS_STATE_TOP,
+        };
+        final int[] capabilitiesForPendingUidRecords = {
+            ActivityManager.PROCESS_CAPABILITY_ALL,
+            ActivityManager.PROCESS_CAPABILITY_NONE,
+            ActivityManager.PROCESS_CAPABILITY_NONE,
+            ActivityManager.PROCESS_CAPABILITY_NONE,
+            ActivityManager.PROCESS_CAPABILITY_NONE,
+            ActivityManager.PROCESS_CAPABILITY_NETWORK,
         };
         final Map<Integer, ChangeRecord> changeItems = new HashMap<>();
         for (int i = 0; i < changesForPendingUidRecords.length; ++i) {
@@ -562,6 +575,7 @@
             pendingChange.uid = i;
             pendingChange.procState = procStatesForPendingUidRecords[i];
             pendingChange.procStateSeq = i;
+            pendingChange.capability = capabilitiesForPendingUidRecords[i];
             changeItems.put(changesForPendingUidRecords[i], pendingChange);
             addPendingUidChange(pendingChange);
         }
@@ -606,20 +620,26 @@
                             verify(observer).onUidGone(changeItem.uid, changeItem.ephemeral);
                         });
             }
-            if ((changeToObserve & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) {
+            if ((changeToObserve & ActivityManager.UID_OBSERVER_PROCSTATE) != 0
+                    || (changeToObserve & ActivityManager.UID_OBSERVER_CAPABILITY) != 0) {
                 // Observer listens to uid procState changes, so change items corresponding to
                 // UidRecord.CHANGE_PROCSTATE or UidRecord.CHANGE_IDLE or UidRecord.CHANGE_ACTIVE
                 // needs to be delivered to this observer.
-                final int[] changesToVerify = {
-                        UidRecord.CHANGE_PROCSTATE,
-                        UidRecord.CHANGE_ACTIVE,
-                        UidRecord.CHANGE_IDLE
-                };
-                verifyObserverReceivedChanges(observerToTest, changesToVerify, changeItems,
+                final IntArray changesToVerify = new IntArray();
+                if ((changeToObserve & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) {
+                    changesToVerify.add(UidRecord.CHANGE_CAPABILITY);
+                } else {
+                    changesToVerify.add(UidRecord.CHANGE_PROCSTATE);
+                    changesToVerify.add(UidRecord.CHANGE_ACTIVE);
+                    changesToVerify.add(UidRecord.CHANGE_IDLE);
+                    changesToVerify.add(UidRecord.CHANGE_CAPABILITY);
+                }
+                verifyObserverReceivedChanges(observerToTest, changesToVerify.toArray(),
+                        changeItems,
                         (observer, changeItem) -> {
                             verify(observer).onUidStateChanged(changeItem.uid,
                                     changeItem.procState, changeItem.procStateSeq,
-                                    ActivityManager.PROCESS_CAPABILITY_NONE);
+                                    changeItem.capability);
                         });
             }
             // Verify there are no other callbacks for this observer.
@@ -725,11 +745,11 @@
             ActivityManager.PROCESS_STATE_CACHED_EMPTY,
             ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
             ActivityManager.PROCESS_STATE_SERVICE,
-            ActivityManager.PROCESS_STATE_RECEIVER
+            ActivityManager.PROCESS_STATE_RECEIVER,
         };
         final ArrayList<ChangeRecord> pendingItemsForUids =
-                new ArrayList<>(changesForPendingItems.length);
-        for (int i = 0; i < changesForPendingItems.length; ++i) {
+                new ArrayList<>(procStatesForPendingItems.length);
+        for (int i = 0; i < procStatesForPendingItems.length; ++i) {
             final ChangeRecord item = new ChangeRecord();
             item.uid = i;
             item.change = changesForPendingItems[i];
diff --git a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
index 1de5f6f..0db118d 100644
--- a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_NETWORK;
 import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
 import static android.app.ActivityManager.PROCESS_STATE_CACHED_RECENT;
 import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
@@ -109,6 +110,7 @@
 
     @Test
     public void testMergeWithPendingChange() {
+        // Map of expectedChange -> {(currentChange, pendingChange)}
         final SparseArray<Pair<Integer, Integer>> changesToVerify = new SparseArray<>();
 
         changesToVerify.put(UidRecord.CHANGE_ACTIVE,
@@ -127,6 +129,8 @@
                 Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_ACTIVE));
         changesToVerify.put(UidRecord.CHANGE_GONE,
                 Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_CACHED));
+        changesToVerify.put(UidRecord.CHANGE_PROCSTATE | UidRecord.CHANGE_CAPABILITY,
+                Pair.create(UidRecord.CHANGE_PROCSTATE, UidRecord.CHANGE_CAPABILITY));
 
         for (int i = 0; i < changesToVerify.size(); ++i) {
             final int expectedChange = changesToVerify.keyAt(i);
@@ -149,7 +153,8 @@
                 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_ACTIVE,
                 PROCESS_STATE_IMPORTANT_FOREGROUND, TEST_PKG2, TEST_UID2);
         final IUidObserver observer2 = mock(IUidObserver.Stub.class);
-        registerObserver(observer2, ActivityManager.UID_OBSERVER_PROCSTATE,
+        registerObserver(observer2,
+                ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_CAPABILITY,
                 PROCESS_STATE_SERVICE, TEST_PKG3, TEST_UID3);
 
         mUidObserverController.dispatchUidsChanged();
@@ -177,6 +182,14 @@
         verifyNoMoreInteractions(observer1);
         verifyNoMoreInteractions(observer2);
 
+        addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE | UidRecord.CHANGE_CAPABILITY,
+                PROCESS_STATE_RECEIVER, 111, PROCESS_CAPABILITY_NETWORK, false);
+        mUidObserverController.dispatchUidsChanged();
+        verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_RECEIVER,
+                111, PROCESS_CAPABILITY_NETWORK);
+        verifyNoMoreInteractions(observer1);
+        verifyNoMoreInteractions(observer2);
+
         unregisterObserver(observer1);
 
         addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_TOP,
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index c34c00d..ba4d585 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
@@ -988,6 +988,49 @@
     }
 
     @Test
+    public void testClearPackageData() throws AppSearchException {
+        List<SchemaTypeConfigProto> existingSchemas =
+                mAppSearchImpl.getSchemaProtoLocked().getTypesList();
+
+        // Insert package schema
+        List<AppSearchSchema> schema =
+                ImmutableList.of(new AppSearchSchema.Builder("schema").build());
+        mAppSearchImpl.setSchema(
+                "package",
+                "database",
+                schema,
+                /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+                /*schemasPackageAccessible=*/ Collections.emptyMap(),
+                /*forceOverride=*/ false,
+                /*version=*/ 0);
+
+        // Insert package document
+        GenericDocument document =
+                new GenericDocument.Builder<>("namespace", "uri", "schema").build();
+        mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null);
+
+        // Verify the document is indexed.
+        SearchSpec searchSpec =
+                new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
+        SearchResultPage searchResultPage =
+                mAppSearchImpl.query("package", "database", /*queryExpression=*/ "", searchSpec);
+        assertThat(searchResultPage.getResults()).hasSize(1);
+        assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document);
+
+        // Remove the package
+        mAppSearchImpl.clearPackageData("package");
+
+        // Verify the document is cleared.
+        searchResultPage =
+                mAppSearchImpl.query("package2", "database2", /*queryExpression=*/ "", searchSpec);
+        assertThat(searchResultPage.getResults()).isEmpty();
+
+        // Verify the schema is cleared.
+        assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
+                .containsExactlyElementsIn(existingSchemas);
+    }
+
+    @Test
     public void testGetPackageToDatabases() throws Exception {
         Map<String, Set<String>> existingMapping = mAppSearchImpl.getPackageToDatabases();
         Map<String, Set<String>> expectedMapping = new ArrayMap<>();
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 4295172..7cd6028 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -346,6 +346,9 @@
         DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo();
         displayDeviceInfo.width = 100;
         displayDeviceInfo.height = 200;
+        displayDeviceInfo.supportedModes = new Display.Mode[1];
+        displayDeviceInfo.supportedModes[0] = new Display.Mode(1, 100, 200, 60f);
+        displayDeviceInfo.modeId = 1;
         final Rect zeroRect = new Rect();
         displayDeviceInfo.displayCutout = new DisplayCutout(
                 Insets.of(0, 10, 0, 0),
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index bcd853c..d784a22 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -239,6 +239,9 @@
         displayDeviceInfo.width = width;
         displayDeviceInfo.height = height;
         displayDeviceInfo.flags = flags;
+        displayDeviceInfo.supportedModes = new Display.Mode[1];
+        displayDeviceInfo.supportedModes[0] = new Display.Mode(1, width, height, 60f);
+        displayDeviceInfo.modeId = 1;
         displayDeviceInfo.address = new DisplayAddressImpl();
         return device;
     }
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 f5876fa..e9e2486 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -18,6 +18,12 @@
 
 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
 import static android.Manifest.permission.NETWORK_STACK;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED;
+import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY;
+import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE;
+import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
@@ -29,10 +35,17 @@
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
+import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_FOREGROUND;
+import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_SYSTEM;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_FOREGROUND;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+import static android.net.NetworkPolicyManager.allowedReasonsToString;
+import static android.net.NetworkPolicyManager.blockedReasonsToString;
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
 import static android.net.NetworkPolicyManager.uidRulesToString;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
@@ -59,6 +72,7 @@
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
+import static com.android.server.net.NetworkPolicyManagerService.UidBlockedState.getEffectiveBlockedReasons;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -134,8 +148,10 @@
 import android.util.ArrayMap;
 import android.util.DataUnit;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Range;
 import android.util.RecurrenceRule;
+import android.util.SparseArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.FlakyTest;
@@ -1896,6 +1912,65 @@
         assertFalse(mService.isUidNetworkingBlocked(UID_E, false));
     }
 
+    @Test
+    public void testUpdateEffectiveBlockedReasons() {
+        final SparseArray<Pair<Integer, Integer>> effectiveBlockedReasons = new SparseArray<>();
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_REASON_NONE, ALLOWED_REASON_NONE));
+
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER, ALLOWED_REASON_SYSTEM));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_REASON_DOZE,
+                        ALLOWED_REASON_SYSTEM));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_METERED_REASON_DATA_SAVER,
+                        ALLOWED_METERED_REASON_SYSTEM));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_METERED_REASON_DATA_SAVER
+                                | BLOCKED_METERED_REASON_USER_RESTRICTED,
+                        ALLOWED_METERED_REASON_SYSTEM));
+
+        effectiveBlockedReasons.put(BLOCKED_METERED_REASON_DATA_SAVER,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_METERED_REASON_DATA_SAVER,
+                        ALLOWED_REASON_SYSTEM));
+        effectiveBlockedReasons.put(BLOCKED_REASON_APP_STANDBY,
+                Pair.create(BLOCKED_REASON_APP_STANDBY | BLOCKED_METERED_REASON_USER_RESTRICTED,
+                        ALLOWED_METERED_REASON_SYSTEM));
+
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER, ALLOWED_REASON_FOREGROUND));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_REASON_DOZE,
+                        ALLOWED_REASON_FOREGROUND));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_METERED_REASON_DATA_SAVER, ALLOWED_METERED_REASON_FOREGROUND));
+        effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
+                Pair.create(BLOCKED_METERED_REASON_DATA_SAVER
+                                | BLOCKED_METERED_REASON_USER_RESTRICTED,
+                        ALLOWED_METERED_REASON_FOREGROUND));
+        effectiveBlockedReasons.put(BLOCKED_METERED_REASON_DATA_SAVER,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_METERED_REASON_DATA_SAVER,
+                        ALLOWED_REASON_FOREGROUND));
+        effectiveBlockedReasons.put(BLOCKED_REASON_BATTERY_SAVER,
+                Pair.create(BLOCKED_REASON_BATTERY_SAVER
+                                | BLOCKED_METERED_REASON_USER_RESTRICTED,
+                        ALLOWED_METERED_REASON_FOREGROUND));
+        // TODO: test more combinations of blocked reasons.
+
+        for (int i = 0; i < effectiveBlockedReasons.size(); ++i) {
+            final int expectedEffectiveBlockedReasons = effectiveBlockedReasons.keyAt(i);
+            final int blockedReasons = effectiveBlockedReasons.valueAt(i).first;
+            final int allowedReasons = effectiveBlockedReasons.valueAt(i).second;
+            final String errorMsg = "Expected="
+                    + blockedReasonsToString(expectedEffectiveBlockedReasons)
+                    + "; blockedReasons=" + blockedReasonsToString(blockedReasons)
+                    + ", allowedReasons=" + allowedReasonsToString(allowedReasons);
+            assertEquals(errorMsg, expectedEffectiveBlockedReasons,
+                    getEffectiveBlockedReasons(blockedReasons, allowedReasons));
+        }
+    }
+
     private String formatBlockedStateError(int uid, int rule, boolean metered,
             boolean backgroundRestricted) {
         return String.format(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
index c69ef8d..a2ad89e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ArchiveTest.java
@@ -56,7 +56,7 @@
     }
 
     private StatusBarNotification getNotification(String pkg, int id, UserHandle user) {
-        Notification n = new Notification.Builder(getContext(), "test")
+        Notification n = new Notification.Builder(getContext(), "test" + id)
                 .setContentTitle("A")
                 .setWhen(1205)
                 .build();
@@ -140,4 +140,23 @@
             assertThat(expected).contains(sbn.getKey());
         }
     }
+
+    @Test
+    public void testRemoveChannelNotifications() {
+        List<String> expected = new ArrayList<>();
+        for (int i = 0; i < SIZE; i++) {
+            StatusBarNotification sbn = getNotification("pkg", i, UserHandle.of(USER_CURRENT));
+            mArchive.record(sbn, REASON_CANCEL);
+            if (i != 3) {
+                // Will delete notification for this user in channel "test3".
+                expected.add(sbn.getKey());
+            }
+        }
+        mArchive.removeChannelNotifications("pkg", USER_CURRENT, "test3");
+        List<StatusBarNotification> actual = Arrays.asList(mArchive.getArray(SIZE, true));
+        assertThat(actual).hasSize(expected.size());
+        for (StatusBarNotification sbn : actual) {
+            assertThat(expected).contains(sbn.getKey());
+        }
+    }
 }
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 ab9aa26..124f6dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2349,7 +2349,7 @@
         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
         activity.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false);
+                false, false);
         waitUntilHandlersIdle();
         assertHasStartingWindow(activity);
         activity.removeStartingWindow();
@@ -2365,7 +2365,7 @@
         for (int i = 0; i < 1000; i++) {
             appToken.addStartingWindow(mPackageName,
                     android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                    false);
+                    false, false);
             appToken.removeStartingWindow();
             waitUntilHandlersIdle();
             assertNoStartingWindow(appToken);
@@ -2379,11 +2379,11 @@
         final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         activity1.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false);
+                false, false);
         waitUntilHandlersIdle();
         activity2.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(),
-                true, true, false, true, false);
+                true, true, false, true, false, false);
         waitUntilHandlersIdle();
         assertNoStartingWindow(activity1);
         assertHasStartingWindow(activity2);
@@ -2400,11 +2400,11 @@
                     activity2.addStartingWindow(mPackageName,
                             android.R.style.Theme, null, "Test", 0, 0, 0, 0,
                             activity1.appToken.asBinder(), true, true, false,
-                            true, false);
+                            true, false, false);
                 });
         activity1.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false);
+                false, false);
         waitUntilHandlersIdle();
         assertNoStartingWindow(activity1);
         assertHasStartingWindow(activity2);
@@ -2417,11 +2417,11 @@
         final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         activity1.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false);
+                false, false);
         waitUntilHandlersIdle();
         activity2.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity1.appToken.asBinder(),
-                true, true, false, true, false);
+                true, true, false, true, false, false);
         waitUntilHandlersIdle();
         assertNoStartingWindow(activity1);
         assertHasStartingWindow(activity2);
@@ -2443,20 +2443,21 @@
                 "Test", 0 /* labelRes */, 0 /* icon */, 0 /* logo */, 0 /* windowFlags */,
                 null /* transferFrom */, true /* newTask */, true /* taskSwitch */,
                 false /* processRunning */, false /* allowTaskSnapshot */,
-                false /* activityCreate */);
+                false /* activityCreate */, false /* samePackage */);
         waitUntilHandlersIdle();
         assertHasStartingWindow(activity);
         activity.mStartingWindowState = ActivityRecord.STARTING_WINDOW_SHOWN;
 
         doCallRealMethod().when(task).startActivityLocked(
-                any(), any(), anyBoolean(), anyBoolean(), any());
+                any(), any(), anyBoolean(), anyBoolean(), any(), anyBoolean());
         // In normal case, resumeFocusedTasksTopActivities() should be called after
         // startActivityLocked(). So skip resumeFocusedTasksTopActivities() in ActivityBuilder.
         doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities();
         // Make mVisibleSetFromTransferredStartingWindow true.
         final ActivityRecord middle = new ActivityBuilder(mAtm).setTask(task).build();
         task.startActivityLocked(middle, null /* focusedTopActivity */,
-                false /* newTask */, false /* keepCurTransition */, null /* options */);
+                false /* newTask */, false /* keepCurTransition */, null /* options */,
+                false /* samePackage */);
         middle.makeFinishingLocked();
 
         assertNull(activity.mStartingWindow);
@@ -2468,7 +2469,8 @@
         top.setVisible(false);
         // The finishing middle should be able to transfer starting window to top.
         task.startActivityLocked(top, null /* focusedTopActivity */,
-                false /* newTask */, false /* keepCurTransition */, null /* options */);
+                false /* newTask */, false /* keepCurTransition */, null /* options */,
+                false /* samePackage */);
 
         assertNull(middle.mStartingWindow);
         assertHasStartingWindow(top);
@@ -2488,7 +2490,7 @@
         task.positionChildAt(topActivity, POSITION_TOP);
         activity.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false);
+                false, false);
         waitUntilHandlersIdle();
 
         // Make activities to have different rotation from it display and set fixed rotation
@@ -2505,7 +2507,7 @@
         // on activity2.
         topActivity.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, activity.appToken.asBinder(),
-                false, false, false, true, false);
+                false, false, false, true, false, false);
         waitUntilHandlersIdle();
         assertTrue(topActivity.hasFixedRotationTransform());
     }
@@ -2521,7 +2523,7 @@
         // Add a starting window.
         activityTop.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
-                false);
+                false, false);
         waitUntilHandlersIdle();
 
         // Make the top one invisible, and try transferring the starting window from the top to the
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index e9e0c99..39fdb2d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1138,7 +1138,7 @@
                 spyOn(rootTask);
             }
             doNothing().when(rootTask).startActivityLocked(
-                    any(), any(), anyBoolean(), anyBoolean(), any());
+                    any(), any(), anyBoolean(), anyBoolean(), any(), anyBoolean());
 
             // Create child task with activity.
             if (mCreateActivity) {
diff --git a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
index f805904..55cbc72 100644
--- a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
+++ b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
@@ -174,7 +174,7 @@
         try {
             callbackRunnable.runOrThrow();
         } catch (RemoteException ex) {
-            Slog.w(TAG, "Failed running callback method", ex);
+            Slog.i(TAG, "Failed running callback method: " + ex);
         }
     }
 
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index ccaeaf9..9aded89 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1439,7 +1439,6 @@
 
         @Override
         public ComponentName getActiveServiceComponentName() {
-            enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
             synchronized (this) {
                 return mImpl != null ? mImpl.mComponent : null;
             }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 24fcf46..cc3b6c5 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3298,6 +3298,14 @@
     public static final String KEY_USE_CALLER_ID_USSD_BOOL = "use_caller_id_ussd_bool";
 
     /**
+     * Call waiting uses USSD command without SS command.
+     * When {@code true}, the call waiting query/set by ussd command.
+     * When {@code false}, doesn't use USSD to query/set call waiting.
+     * @hide
+     */
+    public static final String KEY_USE_CALL_WAITING_USSD_BOOL = "use_call_waiting_ussd_bool";
+
+    /**
      * Specifies the service class for call waiting service.
      * Default value is
      * {@link com.android.internal.telephony.CommandsInterface#SERVICE_CLASS_VOICE}.
@@ -5377,6 +5385,7 @@
         sDefaults.putBoolean(KEY_SUPPORT_EMERGENCY_DIALER_SHORTCUT_BOOL, true);
         sDefaults.putBoolean(KEY_USE_CALL_FORWARDING_USSD_BOOL, false);
         sDefaults.putBoolean(KEY_USE_CALLER_ID_USSD_BOOL, false);
+        sDefaults.putBoolean(KEY_USE_CALL_WAITING_USSD_BOOL, false);
         sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */);
         sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
                 "connected_mmwave:5G,connected:5G,not_restricted_rrc_idle:5G,"
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 793d103..d8ac082 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -3493,8 +3493,8 @@
      * @param subscriptionId the unique Subscription ID in database
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
-    public void setDeviceToDeviceStatusSharingPreference(
-            @DeviceToDeviceStatusSharingPreference int sharing, int subscriptionId) {
+    public void setDeviceToDeviceStatusSharingPreference(int subscriptionId,
+            @DeviceToDeviceStatusSharingPreference int sharing) {
         if (VDBG) {
             logd("[setDeviceToDeviceStatusSharing] + sharing: " + sharing + " subId: "
                     + subscriptionId);
@@ -3525,8 +3525,8 @@
      * @param subscriptionId The unique Subscription ID in database
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
-    public void setDeviceToDeviceStatusSharingContacts(@NonNull List<Uri> contacts,
-            int subscriptionId) {
+    public void setDeviceToDeviceStatusSharingContacts(int subscriptionId,
+            @NonNull List<Uri> contacts) {
         String contactString = serializeUriLists(contacts);
         if (VDBG) {
             logd("[setDeviceToDeviceStatusSharingContacts] + contacts: " + contactString
diff --git a/telephony/java/android/telephony/ims/RcsConfig.java b/telephony/java/android/telephony/ims/RcsConfig.java
index 07e95cc..8a31211 100644
--- a/telephony/java/android/telephony/ims/RcsConfig.java
+++ b/telephony/java/android/telephony/ims/RcsConfig.java
@@ -48,6 +48,9 @@
     private static final String LOG_TAG = "RcsConfig";
     private static final boolean DBG = Build.IS_ENG;
 
+    // Tag for Rcs Volte single registration defined in RCC.07 A.1.6.2
+    private static final String TAG_SINGLE_REGISTRATION = "rcsVolteSingleRegistration";
+
     private final HashMap<String, String> mValues = new HashMap<>();
 
     private RcsConfig(HashMap<String, String> values) {
@@ -145,6 +148,14 @@
         return mValues.containsKey(tag);
     }
 
+    /**
+     * Check whether Rcs Volte single registration is supported by the config.
+     */
+    public boolean isRcsVolteSingleRegistrationSupported() {
+        return getBoolean(TAG_SINGLE_REGISTRATION, false)
+                || getInteger(TAG_SINGLE_REGISTRATION, 0) != 0;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index a133ead..acfa133 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -282,20 +282,6 @@
      * <p>
      * Note: this is only populated if {@link #getCapabilityMechanism} is
      * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS}
-     * @hide
-     */
-    public @NonNull List<String> getOptionsFeatureTags() {
-        if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) {
-            return Collections.emptyList();
-        }
-        return Collections.unmodifiableList(new ArrayList<>(mFeatureTags));
-    }
-
-    /**
-     * @return The feature tags present in the OPTIONS response from the network.
-     * <p>
-     * Note: this is only populated if {@link #getCapabilityMechanism} is
-     * {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS}
      */
     public @NonNull Set<String> getFeatureTags() {
         if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) {
diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java
index b529563..d21fcab 100644
--- a/telephony/java/android/telephony/ims/SipMessage.java
+++ b/telephony/java/android/telephony/ims/SipMessage.java
@@ -204,7 +204,9 @@
 
     /**
      * @return the UTF-8 encoded SIP message.
+     * @deprecated Use {@link #toEncodedMessage} instead
      */
+    @Deprecated
     public @NonNull byte[] getEncodedMessage() {
         byte[] header = new StringBuilder()
                 .append(mStartLine)
@@ -216,4 +218,26 @@
         System.arraycopy(mContent, 0, sipMessage, header.length, mContent.length);
         return sipMessage;
     }
+
+    /**
+     * According RFC-3261 section 7, SIP is a text protocol and uses the UTF-8 charset. Its format
+     * consists of a start-line, one or more header fields, an empty line indicating the end of the
+     * header fields, and an optional message-body.
+     *
+     * <p>
+     * Returns a byte array with UTF-8 format representation of the encoded SipMessage.
+     *
+     * @return byte array with UTF-8 format representation of the encoded SipMessage.
+     */
+    public @NonNull byte[] toEncodedMessage() {
+        byte[] header = new StringBuilder()
+                .append(mStartLine)
+                .append(mHeaderSection)
+                .append(CRLF)
+                .toString().getBytes(UTF_8);
+        byte[] sipMessage = new byte[header.length + mContent.length];
+        System.arraycopy(header, 0, sipMessage, 0, header.length);
+        System.arraycopy(mContent, 0, sipMessage, header.length, mContent.length);
+        return sipMessage;
+    }
 }
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index a117adc..57616d35 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -441,30 +441,6 @@
      * @param contactUri The URI of the remote user that we wish to get the capabilities of.
      * @param myCapabilities The capabilities of this device to send to the remote user.
      * @param callback The callback of this request which is sent from the remote user.
-     * @hide
-     */
-    // executor used is defined in the constructor.
-    @SuppressLint("ExecutorRegistration")
-    public void sendOptionsCapabilityRequest(@NonNull Uri contactUri,
-            @NonNull List<String> myCapabilities, @NonNull OptionsResponseCallback callback) {
-        // Stub - to be implemented by service
-        Log.w(LOG_TAG, "sendOptionsCapabilityRequest called with no implementation.");
-        try {
-            callback.onCommandError(COMMAND_CODE_NOT_SUPPORTED);
-        } catch (ImsException e) {
-            // Do not do anything, this is a stub implementation.
-        }
-    }
-
-    /**
-     * Push one's own capabilities to a remote user via the SIP OPTIONS presence exchange mechanism
-     * in order to receive the capabilities of the remote user in response.
-     * <p>
-     * The implementer must use {@link OptionsResponseCallback} to send the response of
-     * this query from the network back to the framework.
-     * @param contactUri The URI of the remote user that we wish to get the capabilities of.
-     * @param myCapabilities The capabilities of this device to send to the remote user.
-     * @param callback The callback of this request which is sent from the remote user.
      */
     // executor used is defined in the constructor.
     @SuppressLint("ExecutorRegistration")
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index af7eb59..93a2bb0 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -63,6 +63,7 @@
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_BIP;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
@@ -89,6 +90,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VSIM;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP;
 import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
@@ -3030,10 +3032,11 @@
         // Verify NOT_RESTRICTED is set appropriately
         final NetworkCapabilities nc = new NetworkRequest.Builder().addCapability(capability)
                 .build().networkCapabilities;
-        if (capability == NET_CAPABILITY_CBS || capability == NET_CAPABILITY_DUN ||
-                capability == NET_CAPABILITY_EIMS || capability == NET_CAPABILITY_FOTA ||
-                capability == NET_CAPABILITY_IA || capability == NET_CAPABILITY_IMS ||
-                capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP
+        if (capability == NET_CAPABILITY_CBS || capability == NET_CAPABILITY_DUN
+                || capability == NET_CAPABILITY_EIMS || capability == NET_CAPABILITY_FOTA
+                || capability == NET_CAPABILITY_IA || capability == NET_CAPABILITY_IMS
+                || capability == NET_CAPABILITY_RCS || capability == NET_CAPABILITY_XCAP
+                || capability == NET_CAPABILITY_VSIM || capability == NET_CAPABILITY_BIP
                 || capability == NET_CAPABILITY_ENTERPRISE) {
             assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
         } else {
@@ -3168,6 +3171,8 @@
         tryNetworkFactoryRequests(NET_CAPABILITY_INTERNET);
         tryNetworkFactoryRequests(NET_CAPABILITY_TRUSTED);
         tryNetworkFactoryRequests(NET_CAPABILITY_NOT_VPN);
+        tryNetworkFactoryRequests(NET_CAPABILITY_VSIM);
+        tryNetworkFactoryRequests(NET_CAPABILITY_BIP);
         // Skipping VALIDATED and CAPTIVE_PORTAL as they're disallowed.
     }
 
@@ -9251,7 +9256,7 @@
 
         final int expectedOwnerUidWithoutIncludeFlag =
                 shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag
-                        ? Process.myUid() : INVALID_UID;
+                        ? myUid : INVALID_UID;
         assertEquals(expectedOwnerUidWithoutIncludeFlag, getOwnerUidNetCapsPermission(
                 myUid, myUid, false /* includeLocationSensitiveInfo */));
 
@@ -9270,22 +9275,26 @@
 
     }
 
+    private void verifyOwnerUidAndTransportInfoNetCapsPermissionPreS() {
+        verifyOwnerUidAndTransportInfoNetCapsPermission(
+                // Ensure that owner uid is included even if the request asks to remove it (which is
+                // the default) since the app has necessary permissions and targetSdk < S.
+                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                // Ensure that location info is removed if the request asks to remove it even if the
+                // app has necessary permissions.
+                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
+                true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
+        );
+    }
+
     @Test
-    public void testCreateWithLocationInfoSanitizedWithFineLocationAfterQ()
+    public void testCreateWithLocationInfoSanitizedWithFineLocationAfterQPreS()
             throws Exception {
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        verifyOwnerUidAndTransportInfoNetCapsPermission(
-                // Ensure that we include owner uid even if the request asks to remove it since the
-                // app has necessary permissions and targetSdk < S.
-                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
-                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
-                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
-                // Ensure that we remove location info if the request asks to remove it even if the
-                // app has necessary permissions.
-                true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
-        );
+        verifyOwnerUidAndTransportInfoNetCapsPermissionPreS();
     }
 
     @Test
@@ -9294,16 +9303,7 @@
         setupLocationPermissions(Build.VERSION_CODES.R, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        verifyOwnerUidAndTransportInfoNetCapsPermission(
-                // Ensure that we include owner uid even if the request asks to remove it since the
-                // app has necessary permissions and targetSdk < S.
-                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
-                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
-                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
-                // Ensure that we remove location info if the request asks to remove it even if the
-                // app has necessary permissions.
-                true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
-        );
+        verifyOwnerUidAndTransportInfoNetCapsPermissionPreS();
     }
 
     @Test
@@ -9314,13 +9314,13 @@
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
         verifyOwnerUidAndTransportInfoNetCapsPermission(
-                // Ensure that we owner UID if the request asks us to remove it even if the app
-                // has necessary permissions since targetSdk >= S.
+                // Ensure that the owner UID is removed if the request asks us to remove it even
+                // if the app has necessary permissions since targetSdk >= S.
                 false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
                 true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
-                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
-                // Ensure that we remove location info if the request asks to remove it even if the
+                // Ensure that location info is removed if the request asks to remove it even if the
                 // app has necessary permissions.
+                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
                 true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
         );
     }
@@ -9331,15 +9331,15 @@
         setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION,
                 Manifest.permission.ACCESS_COARSE_LOCATION);
 
+        verifyOwnerUidAndTransportInfoNetCapsPermissionPreS();
+    }
+
+    private void verifyOwnerUidAndTransportInfoNetCapsNotIncluded() {
         verifyOwnerUidAndTransportInfoNetCapsPermission(
-                // Ensure that we owner UID if the request asks us to remove it even if the app
-                // has necessary permissions since targetSdk >= S.
-                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
-                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
                 false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
-                // Ensure that we remove location info if the request asks to remove it even if the
-                // app has necessary permissions.
-                true /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
+                false /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
         );
     }
 
@@ -9349,12 +9349,7 @@
         setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        verifyOwnerUidAndTransportInfoNetCapsPermission(
-                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
-                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
-                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
-                false /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
-        );
+        verifyOwnerUidAndTransportInfoNetCapsNotIncluded();
     }
 
     @Test
@@ -9376,26 +9371,17 @@
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION,
                 Manifest.permission.ACCESS_COARSE_LOCATION);
 
-        verifyOwnerUidAndTransportInfoNetCapsPermission(
-                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
-                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
-                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
-                false /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
-        );
+        verifyOwnerUidAndTransportInfoNetCapsNotIncluded();
     }
 
     @Test
-    public void testCreateWithLocationInfoSanitizedWithoutLocationPermission()
+    public void testCreateWithLocationInfoSanitizedWithCoarseLocationAfterS()
             throws Exception {
         // Test that not having fine location permission leads to sanitization.
-        setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */);
+        setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_COARSE_LOCATION,
+                Manifest.permission.ACCESS_COARSE_LOCATION);
 
-        verifyOwnerUidAndTransportInfoNetCapsPermission(
-                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
-                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
-                false, /* shouldInclLocationSensitiveTransportInfoWithoutIncludeFlag */
-                false /* shouldInclLocationSensitiveTransportInfoWithIncludeFlag */
-        );
+        verifyOwnerUidAndTransportInfoNetCapsNotIncluded();
     }
 
     @Test
diff --git a/tools/hiddenapi/OWNERS b/tools/hiddenapi/OWNERS
new file mode 100644
index 0000000..afbeef5
--- /dev/null
+++ b/tools/hiddenapi/OWNERS
@@ -0,0 +1,7 @@
+# compat-team@ for changes to hiddenapi files
+andreionea@google.com
+mathewi@google.com
+satayev@google.com
+
+# soong-team@ as the files these tools protect are tightly coupled with Soong
+file:platform/build/soong:/OWNERS
diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh
index 73eacc0..2924e01 100755
--- a/tools/hiddenapi/exclude.sh
+++ b/tools/hiddenapi/exclude.sh
@@ -7,11 +7,9 @@
 # the team email to use in the event of this detecting an entry in a <team> package. Also
 # add <team> to the TEAMS list. 
 LIBCORE_PACKAGES="\
-  android.icu \
   android.system \
   android.test \
   com.android.bouncycastle \
-  com.android.conscrypt \
   com.android.i18n.phonenumbers \
   com.android.okhttp \
   com.sun \
@@ -24,37 +22,54 @@
   org.json \
   org.w3c.dom \
   org.xml.sax \
+  org.xmlpull.v1 \
   sun \
   "
 LIBCORE_EMAIL=libcore-team@android.com
 
+I18N_PACKAGES="\
+  android.icu \
+  "
+
+I18N_EMAIL=$LIBCORE_EMAIL
+
+CONSCRYPT_PACKAGES="\
+  com.android.org.conscrypt \
+  "
+
+CONSCRYPT_EMAIL=$LIBCORE_EMAIL
+
 # List of teams.
-TEAMS=LIBCORE
+TEAMS="LIBCORE I18N CONSCRYPT"
+
+SHA=$1
 
 # Generate the list of packages and convert to a regular expression.
 PACKAGES=$(for t in $TEAMS; do echo $(eval echo \${${t}_PACKAGES}); done)
 RE=$(echo ${PACKAGES} | sed "s/ /|/g")
-git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do
-    ENTRIES=$(grep -E "^L(${RE})/" || true <(git show $1:$file))
+EXIT_CODE=0
+for file in $(git show --name-only --pretty=format: $SHA | grep "config/hiddenapi-.*txt"); do
+    ENTRIES=$(grep -E "^\+L(${RE})/" <(git diff ${SHA}~1 ${SHA} $file) | sed "s|^\+||" || echo)
     if [[ -n "${ENTRIES}" ]]; then
-      echo -e "\e[1m\e[31m$file $1 contains the following entries\e[0m"
+      echo -e "\e[1m\e[31m$file $SHA contains the following entries\e[0m"
       echo -e "\e[1m\e[31mfor packages that are handled using UnsupportedAppUsage. Please remove\e[0m"
       echo -e "\e[1m\e[31mthese entries and add annotations instead.\e[0m"
       # Partition the entries by team and provide contact details to aid in fixing the issue.
       for t in ${TEAMS}
       do
         PACKAGES=$(eval echo \${${t}_PACKAGES})
-        RE=$(echo ${PACKAGES} | sed "s/ /|/g")
-        TEAM_ENTRIES=$(grep -E "^L(${RE})/" <(echo "${ENTRIES}"))
+        TEAM_RE=$(echo ${PACKAGES} | sed "s/ /|/g")
+        TEAM_ENTRIES=$(grep -E "^L(${TEAM_RE})/" <(echo "${ENTRIES}") || echo)
         if [[ -n "${TEAM_ENTRIES}" ]]; then
           EMAIL=$(eval echo \${${t}_EMAIL})
-          echo -e "\e[33mContact ${EMAIL} or compat- for help with the following:\e[0m"
-          for i in ${ENTRIES}
+          echo -e "\e[33mContact ${EMAIL} for help with the following:\e[0m"
+          for i in ${TEAM_ENTRIES}
           do
             echo -e "\e[33m  ${i}\e[0m"
           done
         fi
       done
-      exit 1
+      EXIT_CODE=1
     fi
 done
+exit $EXIT_CODE