Merge "Forcefully convert to basic Intent before calling PM API." into tm-dev
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d6a067d..36e1c94 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -314,7 +314,6 @@
   public final class NotificationChannel implements android.os.Parcelable {
     method public int getOriginalImportance();
     method public boolean isImportanceLockedByCriticalDeviceFunction();
-    method public boolean isImportanceLockedByOEM();
     method public void lockFields(int);
     method public void setDeleted(boolean);
     method public void setDeletedTimeMs(long);
@@ -800,6 +799,7 @@
     method public boolean hasRequestForegroundServiceExemption();
     method public boolean isPrivilegedApp();
     method public boolean isSystemApp();
+    method public void setEnableOnBackInvokedCallback(boolean);
     field public static final int PRIVATE_FLAG_PRIVILEGED = 8; // 0x8
     field public int privateFlags;
   }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c7c654a..3b1943b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2537,8 +2537,8 @@
      * restriction} for a certain app-op.
      */
     private static RestrictionBypass[] sOpAllowSystemRestrictionBypass = new RestrictionBypass[] {
-            null, //COARSE_LOCATION
-            null, //FINE_LOCATION
+            new RestrictionBypass(true, false, false), //COARSE_LOCATION
+            new RestrictionBypass(true, false, false), //FINE_LOCATION
             null, //GPS
             null, //VIBRATE
             null, //READ_CONTACTS
@@ -2547,7 +2547,7 @@
             null, //WRITE_CALL_LOG
             null, //READ_CALENDAR
             null, //WRITE_CALENDAR
-            new RestrictionBypass(true, false), //WIFI_SCAN
+            new RestrictionBypass(false, true, false), //WIFI_SCAN
             null, //POST_NOTIFICATION
             null, //NEIGHBORING_CELLS
             null, //CALL_PHONE
@@ -2561,10 +2561,10 @@
             null, //READ_ICC_SMS
             null, //WRITE_ICC_SMS
             null, //WRITE_SETTINGS
-            new RestrictionBypass(true, false), //SYSTEM_ALERT_WINDOW
+            new RestrictionBypass(false, true, false), //SYSTEM_ALERT_WINDOW
             null, //ACCESS_NOTIFICATIONS
             null, //CAMERA
-            new RestrictionBypass(false, true), //RECORD_AUDIO
+            new RestrictionBypass(false, false, true), //RECORD_AUDIO
             null, //PLAY_AUDIO
             null, //READ_CLIPBOARD
             null, //WRITE_CLIPBOARD
@@ -2582,7 +2582,7 @@
             null, //MONITOR_HIGH_POWER_LOCATION
             null, //GET_USAGE_STATS
             null, //MUTE_MICROPHONE
-            new RestrictionBypass(true, false), //TOAST_WINDOW
+            new RestrictionBypass(false, true, false), //TOAST_WINDOW
             null, //PROJECT_MEDIA
             null, //ACTIVATE_VPN
             null, //WALLPAPER
@@ -2614,7 +2614,7 @@
             null, // ACCEPT_HANDOVER
             null, // MANAGE_IPSEC_HANDOVERS
             null, // START_FOREGROUND
-            new RestrictionBypass(true, false), // BLUETOOTH_SCAN
+            new RestrictionBypass(false, true, false), // BLUETOOTH_SCAN
             null, // USE_BIOMETRIC
             null, // ACTIVITY_RECOGNITION
             null, // SMS_FINANCIAL_TRANSACTIONS
@@ -3331,6 +3331,9 @@
      * @hide
      */
     public static class RestrictionBypass {
+        /** Does the app need to be system uid to bypass the restriction */
+        public boolean isSystemUid;
+
         /** Does the app need to be privileged to bypass the restriction */
         public boolean isPrivileged;
 
@@ -3340,12 +3343,14 @@
          */
         public boolean isRecordAudioRestrictionExcept;
 
-        public RestrictionBypass(boolean isPrivileged, boolean isRecordAudioRestrictionExcept) {
+        public RestrictionBypass(boolean isSystemUid, boolean isPrivileged,
+                boolean isRecordAudioRestrictionExcept) {
+            this.isSystemUid = isSystemUid;
             this.isPrivileged = isPrivileged;
             this.isRecordAudioRestrictionExcept = isRecordAudioRestrictionExcept;
         }
 
-        public static RestrictionBypass UNRESTRICTED = new RestrictionBypass(true, true);
+        public static RestrictionBypass UNRESTRICTED = new RestrictionBypass(false, true, true);
     }
 
     /**
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
index 436eac3..7c83d58 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -45,7 +45,9 @@
  * referenced in the manifest via {@code android:localeConfig} on
  * {@code <application>}.
  *
- * For more information, see TODO(b/214154050): add link to guide
+ * <p>For more information, see
+ * <a href="https://developer.android.com/about/versions/13/features/app-languages#use-localeconfig">
+ * the section on per-app language preferences</a>.
  *
  * @attr ref android.R.styleable#LocaleConfig_Locale_name
  * @attr ref android.R.styleable#AndroidManifestApplication_localeConfig
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 91ab19b..6f0b03a 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -867,14 +867,6 @@
      * @hide
      */
     @TestApi
-    public boolean isImportanceLockedByOEM() {
-        return mImportanceLockedByOEM;
-    }
-
-    /**
-     * @hide
-     */
-    @TestApi
     public boolean isImportanceLockedByCriticalDeviceFunction() {
         return mImportanceLockedDefaultApp;
     }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 315bd71..0a2b421 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1811,10 +1811,6 @@
      * #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra contain {@link
      * #PROVISIONING_MODE_MANAGED_PROFILE} and {@link #PROVISIONING_MODE_FULLY_MANAGED_DEVICE}.
      *
-     * <p>Also, if this flag is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity
-     * will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link
-     * #EXTRA_PROVISIONING_SERIAL_NUMBER} extras.
-     *
      * <p>This flag can be combined with {@link #FLAG_SUPPORTED_MODES_PERSONALLY_OWNED}. In
      * that case, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity will have
      * the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra contain {@link
@@ -1834,6 +1830,10 @@
      * activity to have the {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra
      * contain only {@link #PROVISIONING_MODE_MANAGED_PROFILE}.
      *
+     * <p>Also, if this flag is set, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity
+     * will not receive the {@link #EXTRA_PROVISIONING_IMEI} and {@link
+     * #EXTRA_PROVISIONING_SERIAL_NUMBER} extras.
+     *
      * <p>This flag can be combined with {@link #FLAG_SUPPORTED_MODES_ORGANIZATION_OWNED}. In
      * that case, the admin app's {@link #ACTION_GET_PROVISIONING_MODE} activity will have the
      * {@link #EXTRA_PROVISIONING_ALLOWED_PROVISIONING_MODES} array extra contain {@link
@@ -13249,8 +13249,9 @@
      * Called by a device owner, profile owner of a managed profile or delegated app with
      * {@link #DELEGATION_NETWORK_LOGGING} to control the network logging feature.
      *
-     * <p> When network logging is enabled by a profile owner, the network logs will only include
-     * work profile network activity, not activity on the personal profile.
+     * <p> Supported for a device owner from Android 8. Supported for a profile owner of a managed
+     * profile from Android 12. When network logging is enabled by a profile owner, the network logs
+     * will only include work profile network activity, not activity on the personal profile.
      *
      * <p> Network logs contain DNS lookup and connect() library call events. The following library
      *     functions are recorded while network logging is active:
diff --git a/core/java/android/app/admin/ProvisioningIntentHelper.java b/core/java/android/app/admin/ProvisioningIntentHelper.java
index fbad90c..1c38559 100644
--- a/core/java/android/app/admin/ProvisioningIntentHelper.java
+++ b/core/java/android/app/admin/ProvisioningIntentHelper.java
@@ -17,8 +17,10 @@
 package android.app.admin;
 
 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ROLE_HOLDER_EXTRAS_BUNDLE;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_TRIGGER;
 import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC;
 import static android.app.admin.DevicePolicyManager.PROVISIONING_TRIGGER_NFC;
@@ -36,12 +38,14 @@
 import android.nfc.NfcAdapter;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.util.Log;
 
 import java.io.IOException;
 import java.io.StringReader;
 import java.util.Enumeration;
 import java.util.Properties;
+import java.util.Set;
 
 /**
  * Utility class that provides functionality to create provisioning intents from nfc intents.
@@ -124,12 +128,46 @@
             ComponentName componentName = ComponentName.unflattenFromString(
                     properties.getProperty(propertyName));
             bundle.putParcelable(propertyName, componentName);
+        } else if (EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE.equals(propertyName)
+                || EXTRA_PROVISIONING_ROLE_HOLDER_EXTRAS_BUNDLE.equals(propertyName)) {
+            try {
+                bundle.putParcelable(propertyName,
+                        deserializeExtrasBundle(properties, propertyName));
+            } catch (IOException e) {
+                Log.e(TAG,
+                        "Failed to parse " + propertyName + ".", e);
+            }
         }
         else {
             bundle.putString(propertyName, properties.getProperty(propertyName));
         }
     }
 
+    /**
+     * Get a {@link PersistableBundle} from a {@code String} property in a {@link Properties}
+     * object.
+     * @param properties the source of the extra
+     * @param extraName key into the {@link Properties} object
+     * @return the {@link PersistableBundle} or {@code null} if there was no property with the
+     * given name
+     * @throws IOException if there was an error parsing the property
+     */
+    private static PersistableBundle deserializeExtrasBundle(
+            Properties properties, String extraName) throws IOException {
+        String serializedExtras = properties.getProperty(extraName);
+        if (serializedExtras == null) {
+            return null;
+        }
+        Properties bundleProperties = new Properties();
+        bundleProperties.load(new StringReader(serializedExtras));
+        PersistableBundle extrasBundle = new PersistableBundle(bundleProperties.size());
+        Set<String> propertyNames = bundleProperties.stringPropertyNames();
+        for (String propertyName : propertyNames) {
+            extrasBundle.putString(propertyName, bundleProperties.getProperty(propertyName));
+        }
+        return extrasBundle;
+    }
+
     private static Intent createProvisioningIntentFromBundle(Bundle bundle) {
         requireNonNull(bundle);
 
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 2961b55..24c3836 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -2752,4 +2752,21 @@
             mKnownActivityEmbeddingCerts.add(knownCert.toUpperCase(Locale.US));
         }
     }
+
+    /**
+     * Sets whether the application will use the {@link android.window.OnBackInvokedCallback}
+     * navigation system instead of the {@link android.view.KeyEvent#KEYCODE_BACK} and related
+     * callbacks. Intended to be used from tests only.
+     *
+     * @see #isOnBackInvokedCallbackEnabled()
+     * @hide
+     */
+    @TestApi
+    public void setEnableOnBackInvokedCallback(boolean isEnable) {
+        if (isEnable) {
+            privateFlagsExt |= PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
+        } else {
+            privateFlagsExt &= ~PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
+        }
+    }
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 44dc28d..52e64e8 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2618,6 +2618,15 @@
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                targetCode)) {
+            Slog.w(TAG, "Package requires development platform " + targetCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return Build.VERSION.SDK_INT;
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             outError[0] = "Requires development platform " + targetCode
@@ -2689,6 +2698,15 @@
             return Build.VERSION_CODES.CUR_DEVELOPMENT;
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                minCode)) {
+            Slog.w(TAG, "Package requires min development platform " + minCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return Build.VERSION.SDK_INT;
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             outError[0] = "Requires development platform " + minCode
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index cb55e30..20a4fdf 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -567,9 +567,14 @@
                     targetCode = minCode;
                 }
 
+                boolean allowUnknownCodenames = false;
+                if ((flags & FrameworkParsingPackageUtils.PARSE_APK_IN_APEX) != 0) {
+                    allowUnknownCodenames = true;
+                }
+
                 ParseResult<Integer> targetResult = FrameworkParsingPackageUtils.computeTargetSdkVersion(
                         targetVer, targetCode, SDK_CODENAMES, input,
-                        /* allowUnknownCodenames= */ false);
+                        allowUnknownCodenames);
                 if (targetResult.isError()) {
                     return input.error(targetResult);
                 }
diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
index 6d74b81..8cc4cdb 100644
--- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
@@ -58,6 +58,7 @@
     private static final int MAX_FILE_NAME_SIZE = 223;
 
     public static final int PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY = 1 << 7;
+    public static final int PARSE_APK_IN_APEX = 1 << 9;
 
     /**
      * Check if the given name is valid.
@@ -315,6 +316,15 @@
             return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                        minCode)) {
+            Slog.w(TAG, "Parsed package requires min development platform " + minCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return input.success(Build.VERSION.SDK_INT);
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
@@ -367,16 +377,29 @@
             return input.success(targetVers);
         }
 
-        if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) {
-            return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
-        }
-
         // If it's a pre-release SDK and the codename matches this platform, it
         // definitely targets this SDK.
         if (matchTargetCode(platformSdkCodenames, targetCode)) {
             return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
         }
 
+        // STOPSHIP: hack for the pre-release SDK
+        if (platformSdkCodenames.length == 0
+                && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+                        targetCode)) {
+            Slog.w(TAG, "Parsed package requires development platform " + targetCode
+                    + ", returning current version " + Build.VERSION.SDK_INT);
+            return input.success(Build.VERSION.SDK_INT);
+        }
+
+        try {
+            if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) {
+                return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+            }
+        } catch (IllegalArgumentException e) {
+            return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK");
+        }
+
         // Otherwise, we're looking at an incompatible pre-release SDK.
         if (platformSdkCodenames.length > 0) {
             return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index f9ed0e3d..3f49b73 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -37,6 +37,7 @@
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodSession;
 import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
 
 import com.android.internal.inputmethod.CancellationGroup;
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
@@ -194,7 +195,9 @@
             case DO_START_INPUT: {
                 final SomeArgs args = (SomeArgs) msg.obj;
                 final IBinder startInputToken = (IBinder) args.arg1;
-                final IInputContext inputContext = (IInputContext) args.arg2;
+                final IInputContext inputContext = (IInputContext) ((SomeArgs) args.arg2).arg1;
+                final ImeOnBackInvokedDispatcher imeDispatcher =
+                        (ImeOnBackInvokedDispatcher) ((SomeArgs) args.arg2).arg2;
                 final EditorInfo info = (EditorInfo) args.arg3;
                 final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
                 final boolean restarting = args.argi5 == 1;
@@ -205,7 +208,7 @@
                         : null;
                 info.makeCompatible(mTargetSdkVersion);
                 inputMethod.dispatchStartInputWithToken(ic, info, restarting, startInputToken,
-                        navButtonFlags);
+                        navButtonFlags, imeDispatcher);
                 args.recycle();
                 return;
             }
@@ -348,13 +351,17 @@
     @Override
     public void startInput(IBinder startInputToken, IInputContext inputContext,
             EditorInfo attribute, boolean restarting,
-            @InputMethodNavButtonFlags int navButtonFlags) {
+            @InputMethodNavButtonFlags int navButtonFlags,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         if (mCancellationGroup == null) {
             Log.e(TAG, "startInput must be called after bindInput.");
             mCancellationGroup = new CancellationGroup();
         }
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = inputContext;
+        args.arg2 = imeDispatcher;
         mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOII(DO_START_INPUT, startInputToken,
-                inputContext, attribute, mCancellationGroup, restarting ? 1 : 0, navButtonFlags));
+                args, attribute, mCancellationGroup, restarting ? 1 : 0, navButtonFlags));
     }
 
     @BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index efd4f06..25296bc 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -101,6 +101,7 @@
 import android.view.Choreographer;
 import android.view.Gravity;
 import android.view.InputChannel;
+import android.view.InputDevice;
 import android.view.InputEventReceiver;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
@@ -134,6 +135,9 @@
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
 import android.widget.TextView;
+import android.window.ImeOnBackInvokedDispatcher;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
 import android.window.WindowMetricsHelper;
 
 import com.android.internal.annotations.GuardedBy;
@@ -344,6 +348,9 @@
      * A circular buffer of size MAX_EVENTS_BUFFER in case IME is taking too long to add ink view.
      **/
     private RingBuffer<MotionEvent> mPendingEvents;
+    private ImeOnBackInvokedDispatcher mImeDispatcher;
+    private Boolean mBackCallbackRegistered = false;
+    private final OnBackInvokedCallback mCompatBackCallback = this::compatHandleBack;
 
     /**
      * Returns whether {@link InputMethodService} is responsible for rendering the back button and
@@ -797,7 +804,13 @@
         @Override
         public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
                 @NonNull EditorInfo editorInfo, boolean restarting,
-                @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags) {
+                @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags,
+                @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+            mImeDispatcher = imeDispatcher;
+            if (mWindow != null) {
+                mWindow.getOnBackInvokedDispatcher().setImeOnBackInvokedDispatcher(
+                        imeDispatcher);
+            }
             mPrivOps.reportStartInputAsync(startInputToken);
             mNavigationBarController.onNavButtonFlagsChanged(navButtonFlags);
             if (restarting) {
@@ -1496,6 +1509,10 @@
                 Context.LAYOUT_INFLATER_SERVICE);
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initSoftInputWindow");
         mWindow = new SoftInputWindow(this, mTheme, mDispatcherState);
+        if (mImeDispatcher != null) {
+            mWindow.getOnBackInvokedDispatcher()
+                    .setImeOnBackInvokedDispatcher(mImeDispatcher);
+        }
         mNavigationBarController.onSoftInputWindowCreated(mWindow);
         {
             final Window window = mWindow.getWindow();
@@ -1608,6 +1625,8 @@
             // when IME developers are doing something unsupported.
             InputMethodPrivilegedOperationsRegistry.remove(mToken);
         }
+        unregisterCompatOnBackInvokedCallback();
+        mImeDispatcher = null;
     }
 
     /**
@@ -2568,9 +2587,47 @@
         cancelImeSurfaceRemoval();
         mInShowWindow = false;
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+        registerCompatOnBackInvokedCallback();
     }
 
 
+    /**
+     * Registers an {@link OnBackInvokedCallback} to handle back invocation when ahead-of-time
+     *  back dispatching is enabled. We keep the {@link KeyEvent#KEYCODE_BACK} based legacy code
+     *  around to handle back on older devices.
+     */
+    private void registerCompatOnBackInvokedCallback() {
+        if (mBackCallbackRegistered) {
+            return;
+        }
+        if (mWindow != null) {
+            mWindow.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+                    OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatBackCallback);
+            mBackCallbackRegistered = true;
+        }
+    }
+
+    private void unregisterCompatOnBackInvokedCallback() {
+        if (!mBackCallbackRegistered) {
+            return;
+        }
+        if (mWindow != null) {
+            mWindow.getOnBackInvokedDispatcher()
+                    .unregisterOnBackInvokedCallback(mCompatBackCallback);
+            mBackCallbackRegistered = false;
+        }
+    }
+
+    private KeyEvent createBackKeyEvent(int action, boolean isTracking) {
+        final long when = SystemClock.uptimeMillis();
+        return new KeyEvent(when, when, action,
+                KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */,
+                KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
+                KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY
+                        | (isTracking ? KeyEvent.FLAG_TRACKING : 0),
+                InputDevice.SOURCE_KEYBOARD);
+    }
+
     private boolean prepareWindow(boolean showInput) {
         boolean doShowInput = false;
         mDecorViewVisible = true;
@@ -2658,6 +2715,7 @@
         }
         mLastWasInFullscreenMode = mIsFullscreen;
         updateFullscreenMode();
+        unregisterCompatOnBackInvokedCallback();
     }
 
     /**
@@ -3797,4 +3855,14 @@
             proto.end(token);
         }
     };
+
+    private void compatHandleBack() {
+        final KeyEvent downEvent = createBackKeyEvent(
+                KeyEvent.ACTION_DOWN, false /* isTracking */);
+        onKeyDown(KeyEvent.KEYCODE_BACK, downEvent);
+        final boolean hasStartedTracking =
+                (downEvent.getFlags() & KeyEvent.FLAG_START_TRACKING) != 0;
+        final KeyEvent upEvent = createBackKeyEvent(KeyEvent.ACTION_UP, hasStartedTracking);
+        onKeyUp(KeyEvent.KEYCODE_BACK, upEvent);
+    }
 }
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 37f44e9..9a2f7ba 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -738,6 +738,14 @@
      */
     public static final String NAMESPACE_VENDOR_SYSTEM_NATIVE = "vendor_system_native";
 
+    /**
+     * Namespace for DevicePolicyManager related features.
+     *
+     * @hide
+     */
+    public static final String NAMESPACE_DEVICE_POLICY_MANAGER =
+            "device_policy_manager";
+
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
     private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index dac54cf..d2a86eb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9695,6 +9695,40 @@
                 "active_unlock_on_biometric_fail";
 
         /**
+         * If active unlock triggers on biometric failures, include the following error codes
+         * as a biometric failure. See {@link android.hardware.biometrics.BiometricFaceConstants}.
+         * Error codes should be separated by a pipe. For example: "1|4|5". If active unlock
+         * should never trigger on any face errors, this should be set to an empty string.
+         * A null value will use the system default value (TIMEOUT).
+         * @hide
+         */
+        public static final String ACTIVE_UNLOCK_ON_FACE_ERRORS =
+                "active_unlock_on_face_errors";
+
+        /**
+         * If active unlock triggers on biometric failures, include the following acquired info
+         * as a "biometric failure". See {@link android.hardware.biometrics.BiometricFaceConstants}.
+         * Acquired codes should be separated by a pipe. For example: "1|4|5". If active unlock
+         * should never on trigger on any acquired info messages, this should be
+         * set to an empty string. A null value will use the system default value (none).
+         * @hide
+         */
+        public static final String ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO =
+                "active_unlock_on_face_acquire_info";
+
+        /**
+         * If active unlock triggers on biometric failures, then also request active unlock on
+         * unlock intent when each setting (BiometricType) is the only biometric type enrolled.
+         * Biometric types should be separated by a pipe. For example: "0|3" or "0". If this
+         * setting should be disabled, then this should be set to an empty string. A null value
+         * will use the system default value (0 / None).
+         *   0 = None, 1 = Any face, 2 = Any fingerprint, 3 = Under display fingerprint
+         * @hide
+         */
+        public static final String ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED =
+                "active_unlock_on_unlock_intent_when_biometric_enrolled";
+
+        /**
          * Whether the assist gesture should be enabled.
          *
          * @hide
@@ -10121,15 +10155,6 @@
         public static final String NOTIFICATION_DISMISS_RTL = "notification_dismiss_rtl";
 
         /**
-         * Whether the app-level notification setting is represented by a manifest permission.
-         *
-         * @hide
-         */
-        @Readable
-        public static final String NOTIFICATION_PERMISSION_ENABLED =
-                "notification_permission_enabled";
-
-        /**
          * Comma separated list of QS tiles that have been auto-added already.
          * @hide
          */
diff --git a/core/java/android/service/quickaccesswallet/TEST_MAPPING b/core/java/android/service/quickaccesswallet/TEST_MAPPING
index 4d97ab6..5d2a3a8 100644
--- a/core/java/android/service/quickaccesswallet/TEST_MAPPING
+++ b/core/java/android/service/quickaccesswallet/TEST_MAPPING
@@ -1,5 +1,5 @@
 {
-  "presubmit": [
+  "presubmit-large": [
     {
       "name": "CtsQuickAccessWalletTestCases"
     }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2475e2c..d04b07c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9922,7 +9922,7 @@
      * <ol>
      *   <li>It should only be called when content capture is enabled for the view.
      *   <li>It must call viewAppeared() before viewDisappeared()
-     *   <li>viewAppearead() can only be called when the view is visible and laidout
+     *   <li>viewAppeared() can only be called when the view is visible and laid out
      *   <li>It should not call the same event twice.
      * </ol>
      */
@@ -9999,6 +9999,11 @@
                     Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on disappeared for " + this);
                 }
             }
+
+            // We reset any translation state as views may be re-used (e.g., as in ListView and
+            // RecyclerView). We only need to do this for views important for content capture since
+            // views unimportant for content capture won't be translated anyway.
+            clearTranslationState();
         }
     }
 
@@ -12719,6 +12724,21 @@
     }
 
     /**
+     * @hide
+     */
+    public void clearTranslationState() {
+        if (mViewTranslationCallback != null) {
+            mViewTranslationCallback.onClearTranslation(this);
+        }
+        clearViewTranslationCallback();
+        clearViewTranslationResponse();
+        if (hasTranslationTransientState()) {
+            setHasTransientState(false);
+            setHasTranslationTransientState(false);
+        }
+    }
+
+    /**
      * Returns true if this view is currently attached to a window.
      */
     public boolean isAttachedToWindow() {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0bdbfbc..48c102b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6095,6 +6095,28 @@
 
         @Override
         protected int onProcess(QueuedInputEvent q) {
+            if (q.mEvent instanceof KeyEvent) {
+                final KeyEvent event = (KeyEvent) q.mEvent;
+
+                // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the
+                // view tree or IME, and invoke the appropriate {@link OnBackInvokedCallback}.
+                if (isBack(event)
+                        && mContext != null
+                        && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
+                    OnBackInvokedCallback topCallback =
+                            getOnBackInvokedDispatcher().getTopCallback();
+                    if (event.getAction() == KeyEvent.ACTION_UP) {
+                        if (topCallback != null) {
+                            topCallback.onBackInvoked();
+                            return FINISH_HANDLED;
+                        }
+                    } else {
+                        // Drop other actions such as {@link KeyEvent.ACTION_DOWN}.
+                        return FINISH_NOT_HANDLED;
+                    }
+                }
+            }
+
             if (mInputQueue != null && q.mEvent instanceof KeyEvent) {
                 mInputQueue.sendInputEvent(q.mEvent, q, true, this);
                 return DEFER;
@@ -6446,24 +6468,6 @@
                 return FINISH_HANDLED;
             }
 
-            // If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the
-            // view tree and invoke the appropriate {@link OnBackInvokedCallback}.
-            if (isBack(event)
-                    && mContext != null
-                    && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
-                OnBackInvokedCallback topCallback =
-                        getOnBackInvokedDispatcher().getTopCallback();
-                if (event.getAction() == KeyEvent.ACTION_UP) {
-                    if (topCallback != null) {
-                        topCallback.onBackInvoked();
-                        return FINISH_HANDLED;
-                    }
-                } else {
-                    // Drop other actions such as {@link KeyEvent.ACTION_DOWN}.
-                    return FINISH_NOT_HANDLED;
-                }
-            }
-
             // Deliver the key to the view hierarchy.
             if (mView.dispatchKeyEvent(event)) {
                 return FINISH_HANDLED;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 5bc340b..00052f6 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3108,10 +3108,14 @@
 
         /**
          * The preferred refresh rate for the window.
-         *
+         * <p>
          * This must be one of the supported refresh rates obtained for the display(s) the window
          * is on. The selected refresh rate will be applied to the display's default mode.
-         *
+         * <p>
+         * This should be used in favor of {@link LayoutParams#preferredDisplayModeId} for
+         * applications that want to specify the refresh rate, but do not want to specify a
+         * preference for any other displayMode properties (e.g., resolution).
+         * <p>
          * This value is ignored if {@link #preferredDisplayModeId} is set.
          *
          * @see Display#getSupportedRefreshRates()
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 6209b46..dbdc0da 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -29,6 +29,7 @@
 import android.view.InputChannel;
 import android.view.MotionEvent;
 import android.view.View;
+import android.window.ImeOnBackInvokedDispatcher;
 
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.InputMethodNavButtonFlags;
@@ -232,6 +233,10 @@
      *                        long as your implementation of {@link InputMethod} relies on such
      *                        IPCs
      * @param navButtonFlags {@link InputMethodNavButtonFlags} in the initial state of this session.
+     * @param imeDispatcher The {@link ImeOnBackInvokedDispatcher }} to be set on the
+     *                      IME's {@link android.window.WindowOnBackInvokedDispatcher}, so that IME
+     *                      {@link android.window.OnBackInvokedCallback}s can be forwarded to
+     *                      the client requesting to start input.
      * @see #startInput(InputConnection, EditorInfo)
      * @see #restartInput(InputConnection, EditorInfo)
      * @see EditorInfo
@@ -240,7 +245,8 @@
     @MainThread
     default void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
             @NonNull EditorInfo editorInfo, boolean restarting,
-            @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags) {
+            @NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         if (restarting) {
             restartInput(inputConnection, editorInfo);
         } else {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index d9bde58..e2e9a85 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -91,6 +91,8 @@
 import android.view.ViewRootImpl;
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
 import android.view.autofill.AutofillManager;
+import android.window.ImeOnBackInvokedDispatcher;
+import android.window.WindowOnBackInvokedDispatcher;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.inputmethod.DirectBootAwareness;
@@ -105,6 +107,7 @@
 import com.android.internal.inputmethod.StartInputReason;
 import com.android.internal.inputmethod.UnbindReason;
 import com.android.internal.os.SomeArgs;
+import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
 import com.android.internal.view.IInputMethodManager;
 import com.android.internal.view.IInputMethodSession;
@@ -279,6 +282,21 @@
     private static final String SUBTYPE_MODE_VOICE = "voice";
 
     /**
+     * Provide this to {@link IInputMethodManager#startInputOrWindowGainedFocus(
+     * int, IInputMethodClient, IBinder, int, int, int, EditorInfo, IInputContext, int)} to receive
+     * {@link android.window.OnBackInvokedCallback} registrations from IME.
+     */
+    private final ImeOnBackInvokedDispatcher mImeDispatcher =
+            new ImeOnBackInvokedDispatcher(Handler.getMain()) {
+        @Override
+        public WindowOnBackInvokedDispatcher getReceivingDispatcher() {
+            synchronized (mH) {
+                return mCurRootView != null ? mCurRootView.getOnBackInvokedDispatcher() : null;
+            }
+        }
+    };
+
+    /**
      * Ensures that {@link #sInstance} becomes non-{@code null} for application that have directly
      * or indirectly relied on {@link #sInstance} via reflection or something like that.
      *
@@ -740,7 +758,8 @@
                             windowFlags,
                             null,
                             null, null,
-                            mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
+                            mCurRootView.mContext.getApplicationInfo().targetSdkVersion,
+                            mImeDispatcher);
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
@@ -1687,6 +1706,8 @@
             mServedConnecting = false;
             clearConnectionLocked();
         }
+        // Clear the back callbacks held by the ime dispatcher to avoid memory leaks.
+        mImeDispatcher.clear();
     }
 
     public void displayCompletions(View view, CompletionInfo[] completions) {
@@ -2359,7 +2380,8 @@
                         softInputMode, windowFlags, tba, servedInputConnection,
                         servedInputConnection == null ? null
                                 : servedInputConnection.asIRemoteAccessibilityInputConnection(),
-                        view.getContext().getApplicationInfo().targetSdkVersion);
+                        view.getContext().getApplicationInfo().targetSdkVersion,
+                        mImeDispatcher);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 8cf032b..6bf2474 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -158,12 +158,7 @@
             case STATE_UI_TRANSLATION_FINISHED:
                 destroyTranslators();
                 runForEachView((view, callback) -> {
-                    callback.onClearTranslation(view);
-                    view.clearViewTranslationResponse();
-                    if (view.hasTranslationTransientState()) {
-                        view.setHasTransientState(false);
-                        view.setHasTranslationTransientState(false);
-                    }
+                    view.clearTranslationState();
                 });
                 notifyTranslationFinished(/* activityDestroyed= */ false);
                 synchronized (mLock) {
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.aidl b/core/java/android/window/ImeOnBackInvokedDispatcher.aidl
new file mode 100644
index 0000000..04e6420
--- /dev/null
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/os/ParcelFileDescriptor.aidl
+**
+** Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.window;
+
+parcelable ImeOnBackInvokedDispatcher;
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
new file mode 100644
index 0000000..d5763aa
--- /dev/null
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * A {@link OnBackInvokedDispatcher} for IME that forwards {@link OnBackInvokedCallback}
+ * registrations from the IME process to the app process to be registered on the app window.
+ * <p>
+ * The app process creates and propagates an instance of {@link ImeOnBackInvokedDispatcher}
+ * to the IME to be set on the IME window's {@link WindowOnBackInvokedDispatcher}.
+ * <p>
+ * @see WindowOnBackInvokedDispatcher#setImeOnBackInvokedDispatcher
+ *
+ * @hide
+ */
+public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parcelable {
+
+    private static final String TAG = "ImeBackDispatcher";
+    static final String RESULT_KEY_ID = "id";
+    static final String RESULT_KEY_CALLBACK = "callback";
+    static final String RESULT_KEY_PRIORITY = "priority";
+    static final int RESULT_CODE_REGISTER = 0;
+    static final int RESULT_CODE_UNREGISTER = 1;
+    @NonNull
+    private final ResultReceiver mResultReceiver;
+
+    public ImeOnBackInvokedDispatcher(Handler handler) {
+        mResultReceiver = new ResultReceiver(handler) {
+            @Override
+            public void onReceiveResult(int resultCode, Bundle resultData) {
+                WindowOnBackInvokedDispatcher dispatcher = getReceivingDispatcher();
+                if (dispatcher != null) {
+                    receive(resultCode, resultData, dispatcher);
+                }
+            }
+        };
+    }
+
+    /**
+     * Override this method to return the {@link WindowOnBackInvokedDispatcher} of the window
+     * that should receive the forwarded callback.
+     */
+    @Nullable
+    protected WindowOnBackInvokedDispatcher getReceivingDispatcher() {
+        return null;
+    }
+
+    ImeOnBackInvokedDispatcher(Parcel in) {
+        mResultReceiver = in.readTypedObject(ResultReceiver.CREATOR);
+    }
+
+    @Override
+    public void registerOnBackInvokedCallback(
+            @OnBackInvokedDispatcher.Priority int priority,
+            @NonNull OnBackInvokedCallback callback) {
+        final Bundle bundle = new Bundle();
+        final IOnBackInvokedCallback iCallback =
+                new WindowOnBackInvokedDispatcher.OnBackInvokedCallbackWrapper(callback);
+        bundle.putBinder(RESULT_KEY_CALLBACK, iCallback.asBinder());
+        bundle.putInt(RESULT_KEY_PRIORITY, priority);
+        bundle.putInt(RESULT_KEY_ID, callback.hashCode());
+        mResultReceiver.send(RESULT_CODE_REGISTER, bundle);
+    }
+
+    @Override
+    public void unregisterOnBackInvokedCallback(
+            @NonNull OnBackInvokedCallback callback) {
+        Bundle bundle = new Bundle();
+        bundle.putInt(RESULT_KEY_ID, callback.hashCode());
+        mResultReceiver.send(RESULT_CODE_UNREGISTER, bundle);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeTypedObject(mResultReceiver, flags);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ImeOnBackInvokedDispatcher> CREATOR =
+            new Parcelable.Creator<ImeOnBackInvokedDispatcher>() {
+                public ImeOnBackInvokedDispatcher createFromParcel(Parcel in) {
+                    return new ImeOnBackInvokedDispatcher(in);
+                }
+                public ImeOnBackInvokedDispatcher[] newArray(int size) {
+                    return new ImeOnBackInvokedDispatcher[size];
+                }
+            };
+
+    private final HashMap<Integer, OnBackInvokedCallback> mImeCallbackMap = new HashMap<>();
+
+    private void receive(
+            int resultCode, Bundle resultData,
+            @NonNull OnBackInvokedDispatcher receivingDispatcher) {
+        final int callbackId = resultData.getInt(RESULT_KEY_ID);
+        if (resultCode == RESULT_CODE_REGISTER) {
+            int priority = resultData.getInt(RESULT_KEY_PRIORITY);
+            final IOnBackInvokedCallback callback = IOnBackInvokedCallback.Stub.asInterface(
+                    resultData.getBinder(RESULT_KEY_CALLBACK));
+            registerReceivedCallback(
+                    callback, priority, callbackId, receivingDispatcher);
+        } else if (resultCode == RESULT_CODE_UNREGISTER) {
+            unregisterReceivedCallback(callbackId, receivingDispatcher);
+        }
+    }
+
+    private void registerReceivedCallback(
+            @NonNull IOnBackInvokedCallback iCallback,
+            @OnBackInvokedDispatcher.Priority int priority,
+            int callbackId,
+            @NonNull OnBackInvokedDispatcher receivingDispatcher) {
+        final ImeOnBackInvokedCallback imeCallback =
+                new ImeOnBackInvokedCallback(iCallback);
+        mImeCallbackMap.put(callbackId, imeCallback);
+        receivingDispatcher.registerOnBackInvokedCallback(priority, imeCallback);
+    }
+
+    private void unregisterReceivedCallback(
+            int callbackId, OnBackInvokedDispatcher receivingDispatcher) {
+        final OnBackInvokedCallback callback = mImeCallbackMap.get(callbackId);
+        if (callback == null) {
+            Log.e(TAG, "Ime callback not found. Ignoring unregisterReceivedCallback. "
+                    + "callbackId: " + callbackId);
+            return;
+        }
+        receivingDispatcher.unregisterOnBackInvokedCallback(callback);
+    }
+
+    /** Clears all registered callbacks on the instance. */
+    public void clear() {
+        mImeCallbackMap.clear();
+    }
+
+    static class ImeOnBackInvokedCallback implements OnBackInvokedCallback {
+        @NonNull
+        private final IOnBackInvokedCallback mIOnBackInvokedCallback;
+
+        ImeOnBackInvokedCallback(@NonNull IOnBackInvokedCallback iCallback) {
+            mIOnBackInvokedCallback = iCallback;
+        }
+
+        @Override
+        public void onBackInvoked() {
+            try {
+                if (mIOnBackInvokedCallback != null) {
+                    mIOnBackInvokedCallback.onBackInvoked();
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Exception when invoking forwarded callback. e: ", e);
+            }
+        }
+
+        IOnBackInvokedCallback getIOnBackInvokedCallback() {
+            return mIOnBackInvokedCallback;
+        }
+    }
+}
diff --git a/core/java/android/window/OnBackInvokedDispatcher.java b/core/java/android/window/OnBackInvokedDispatcher.java
index 6bc2b50..3539049a 100644
--- a/core/java/android/window/OnBackInvokedDispatcher.java
+++ b/core/java/android/window/OnBackInvokedDispatcher.java
@@ -96,4 +96,19 @@
      * @hide
      */
     default void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) { }
+
+
+    /**
+     * Sets an {@link ImeOnBackInvokedDispatcher} to forward {@link OnBackInvokedCallback}s
+     * from IME to the app process to be registered on the app window.
+     *
+     * Only call this on the IME window. Create the {@link ImeOnBackInvokedDispatcher} from
+     * the application process and override
+     * {@link ImeOnBackInvokedDispatcher#getReceivingDispatcher()} to point to the app
+     * window's {@link WindowOnBackInvokedDispatcher}.
+     *
+     * @hide
+     */
+    default void setImeOnBackInvokedDispatcher(
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) { }
 }
diff --git a/core/java/android/window/ProxyOnBackInvokedDispatcher.java b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
index 4409397..bedf503 100644
--- a/core/java/android/window/ProxyOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
@@ -49,6 +49,7 @@
     private final List<Pair<OnBackInvokedCallback, Integer>> mCallbacks = new ArrayList<>();
     private final Object mLock = new Object();
     private OnBackInvokedDispatcher mActualDispatcher = null;
+    private ImeOnBackInvokedDispatcher mImeDispatcher;
 
     @Override
     public void registerOnBackInvokedCallback(
@@ -108,6 +109,9 @@
             Log.v(TAG, String.format("Proxy transferring %d callbacks to %s", mCallbacks.size(),
                     mActualDispatcher));
         }
+        if (mImeDispatcher != null) {
+            mActualDispatcher.setImeOnBackInvokedDispatcher(mImeDispatcher);
+        }
         for (Pair<OnBackInvokedCallback, Integer> callbackPair : mCallbacks) {
             int priority = callbackPair.second;
             if (priority >= 0) {
@@ -117,6 +121,7 @@
             }
         }
         mCallbacks.clear();
+        mImeDispatcher = null;
     }
 
     private void clearCallbacksOnDispatcher() {
@@ -142,6 +147,7 @@
         }
         synchronized (mLock) {
             mCallbacks.clear();
+            mImeDispatcher = null;
         }
     }
 
@@ -169,4 +175,14 @@
             transferCallbacksToDispatcher();
         }
     }
+
+    @Override
+    public void setImeOnBackInvokedDispatcher(
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+        if (mActualDispatcher != null) {
+            mActualDispatcher.setImeOnBackInvokedDispatcher(imeDispatcher);
+        } else {
+            mImeDispatcher = imeDispatcher;
+        }
+    }
 }
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 781859c..edfdbcc 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -55,6 +55,8 @@
             .getInt("persist.wm.debug.predictive_back", 1) != 0;
     private static final boolean ALWAYS_ENFORCE_PREDICTIVE_BACK = SystemProperties
             .getInt("persist.wm.debug.predictive_back_always_enforce", 0) != 0;
+    @Nullable
+    private ImeOnBackInvokedDispatcher mImeDispatcher;
 
     /** Convenience hashmap to quickly decide if a callback has been added. */
     private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>();
@@ -94,6 +96,10 @@
 
     private void registerOnBackInvokedCallbackUnchecked(
             @NonNull OnBackInvokedCallback callback, @Priority int priority) {
+        if (mImeDispatcher != null) {
+            mImeDispatcher.registerOnBackInvokedCallback(priority, callback);
+            return;
+        }
         if (!mOnBackInvokedCallbacks.containsKey(priority)) {
             mOnBackInvokedCallbacks.put(priority, new ArrayList<>());
         }
@@ -120,6 +126,10 @@
 
     @Override
     public void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) {
+        if (mImeDispatcher != null) {
+            mImeDispatcher.unregisterOnBackInvokedCallback(callback);
+            return;
+        }
         if (!mAllCallbacks.containsKey(callback)) {
             if (DEBUG) {
                 Log.i(TAG, "Callback not found. returning...");
@@ -153,6 +163,9 @@
         }
         mAllCallbacks.clear();
         mOnBackInvokedCallbacks.clear();
+        if (mImeDispatcher != null) {
+            mImeDispatcher = null;
+        }
     }
 
     private void setTopOnBackInvokedCallback(@Nullable OnBackInvokedCallback callback) {
@@ -160,14 +173,18 @@
             return;
         }
         try {
-            if (callback == null) {
-                mWindowSession.setOnBackInvokedCallbackInfo(mWindow, null);
-            } else {
+            OnBackInvokedCallbackInfo callbackInfo = null;
+            if (callback != null) {
                 int priority = mAllCallbacks.get(callback);
-                mWindowSession.setOnBackInvokedCallbackInfo(
-                        mWindow, new OnBackInvokedCallbackInfo(
-                                new OnBackInvokedCallbackWrapper(callback), priority));
+                final IOnBackInvokedCallback iCallback =
+                        callback instanceof ImeOnBackInvokedDispatcher
+                                    .ImeOnBackInvokedCallback
+                                ? ((ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback)
+                                        callback).getIOnBackInvokedCallback()
+                                : new OnBackInvokedCallbackWrapper(callback);
+                callbackInfo = new OnBackInvokedCallbackInfo(iCallback, priority);
             }
+            mWindowSession.setOnBackInvokedCallbackInfo(mWindow, callbackInfo);
             if (DEBUG && callback == null) {
                 Log.d(TAG, TextUtils.formatSimple("setTopOnBackInvokedCallback(null) Callers:%s",
                         Debug.getCallers(5, "  ")));
@@ -190,7 +207,7 @@
         return null;
     }
 
-    private static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
+    static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
         private final WeakReference<OnBackInvokedCallback> mCallback;
 
         OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback) {
@@ -270,4 +287,10 @@
 
         return featureFlagEnabled && (appRequestsPredictiveBack || ALWAYS_ENFORCE_PREDICTIVE_BACK);
     }
+
+    @Override
+    public void setImeOnBackInvokedDispatcher(
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+        mImeDispatcher = imeDispatcher;
+    }
 }
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 7db4243..0976f45 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -21,8 +21,10 @@
 
 import android.annotation.AnyThread;
 import android.annotation.BinderThread;
+import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityThread;
 import android.app.IWindowToken;
 import android.app.ResourcesManager;
 import android.content.Context;
@@ -33,7 +35,6 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.IWindowManager;
@@ -42,6 +43,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.lang.ref.WeakReference;
 
@@ -76,7 +78,7 @@
 
     private boolean mAttachToWindowContainer;
 
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final Handler mHandler = ActivityThread.currentActivityThread().getHandler();
 
     /**
      * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient}
@@ -188,8 +190,8 @@
     @BinderThread
     @Override
     public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
-        mHandler.post(() -> onConfigurationChanged(newConfig, newDisplayId,
-                true /* shouldReportConfigChange */));
+        mHandler.post(PooledLambda.obtainRunnable(this::onConfigurationChanged, newConfig,
+                newDisplayId, true /* shouldReportConfigChange */).recycleOnUse());
     }
 
     // TODO(b/192048581): rewrite this method based on WindowContext and WindowProviderService
@@ -279,12 +281,16 @@
     @BinderThread
     @Override
     public void onWindowTokenRemoved() {
-        mHandler.post(() -> {
-            final Context context = mContextRef.get();
-            if (context != null) {
-                context.destroy();
-                mContextRef.clear();
-            }
-        });
+        mHandler.post(PooledLambda.obtainRunnable(
+                WindowTokenClient::onWindowTokenRemovedInner, this).recycleOnUse());
+    }
+
+    @MainThread
+    private void onWindowTokenRemovedInner() {
+        final Context context = mContextRef.get();
+        if (context != null) {
+            context.destroy();
+            mContextRef.clear();
+        }
     }
 }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 3fee914..781b6d5 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -971,7 +971,8 @@
             getChooserActivityLogger().logShareTargetSelected(
                     SELECTION_TYPE_COPY,
                     "",
-                    -1);
+                    -1,
+                    false);
 
             setResult(RESULT_OK);
             finish();
@@ -1155,7 +1156,8 @@
                     getChooserActivityLogger().logShareTargetSelected(
                             SELECTION_TYPE_NEARBY,
                             "",
-                            -1);
+                            -1,
+                            false);
                     // Action bar is user-independent, always start as primary
                     safelyStartActivityAsUser(ti, getPersonalProfileUserHandle());
                     finish();
@@ -1177,7 +1179,8 @@
                     getChooserActivityLogger().logShareTargetSelected(
                             SELECTION_TYPE_EDIT,
                             "",
-                            -1);
+                            -1,
+                            false);
                     // Action bar is user-independent, always start as primary
                     safelyStartActivityAsUser(ti, getPersonalProfileUserHandle());
                     finish();
@@ -1754,7 +1757,8 @@
                             target.getComponentName().getPackageName()
                                     + target.getTitle().toString(),
                             mMaxHashSaltDays);
-                    directTargetAlsoRanked = getRankedPosition((SelectableTargetInfo) targetInfo);
+                    SelectableTargetInfo selectableTargetInfo = (SelectableTargetInfo) targetInfo;
+                    directTargetAlsoRanked = getRankedPosition(selectableTargetInfo);
 
                     if (mCallerChooserTargets != null) {
                         numCallerProvided = mCallerChooserTargets.length;
@@ -1762,7 +1766,8 @@
                     getChooserActivityLogger().logShareTargetSelected(
                             SELECTION_TYPE_SERVICE,
                             targetInfo.getResolveInfo().activityInfo.processName,
-                            value
+                            value,
+                            selectableTargetInfo.isPinned()
                     );
                     break;
                 case ChooserListAdapter.TARGET_CALLER:
@@ -1773,7 +1778,8 @@
                     getChooserActivityLogger().logShareTargetSelected(
                             SELECTION_TYPE_APP,
                             targetInfo.getResolveInfo().activityInfo.processName,
-                            value
+                            value,
+                            targetInfo.isPinned()
                     );
                     break;
                 case ChooserListAdapter.TARGET_STANDARD_AZ:
@@ -1784,7 +1790,8 @@
                     getChooserActivityLogger().logShareTargetSelected(
                             SELECTION_TYPE_STANDARD,
                             targetInfo.getResolveInfo().activityInfo.processName,
-                            value
+                            value,
+                            false
                     );
                     break;
             }
diff --git a/core/java/com/android/internal/app/ChooserActivityLogger.java b/core/java/com/android/internal/app/ChooserActivityLogger.java
index 3217307..bb7d50a 100644
--- a/core/java/com/android/internal/app/ChooserActivityLogger.java
+++ b/core/java/com/android/internal/app/ChooserActivityLogger.java
@@ -34,7 +34,8 @@
             int appProvidedApp, boolean isWorkprofile, int previewType, String intent);
 
     /** Logs a UiEventReported event for the system sharesheet when the user selects a target. */
-    void logShareTargetSelected(int targetType, String packageName, int positionPicked);
+    void logShareTargetSelected(int targetType, String packageName, int positionPicked,
+            boolean isPinned);
 
     /** Logs a UiEventReported event for the system sharesheet being triggered by the user. */
     default void logSharesheetTriggered() {
diff --git a/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java b/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
index 48bdba3..e3cc4f1 100644
--- a/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
+++ b/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
@@ -51,12 +51,14 @@
     }
 
     @Override
-    public void logShareTargetSelected(int targetType, String packageName, int positionPicked) {
+    public void logShareTargetSelected(int targetType, String packageName, int positionPicked,
+            boolean isPinned) {
         FrameworkStatsLog.write(FrameworkStatsLog.RANKING_SELECTED,
                 /* event_id = 1 */ SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(),
                 /* package_name = 2 */ packageName,
                 /* instance_id = 3 */ getInstanceId().getId(),
-                /* position_picked = 4 */ positionPicked);
+                /* position_picked = 4 */ positionPicked,
+                /* is_pinned = 5 */ isPinned);
     }
 
     @Override
diff --git a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
index c5b21ac..e7f80a7 100644
--- a/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverRankerServiceResolverComparator.java
@@ -589,7 +589,7 @@
                 MetricsLogger metricsLogger = new MetricsLogger();
                 LogMaker log = new LogMaker(MetricsEvent.ACTION_TARGET_SELECTED);
                 log.setComponentName(mRankerServiceName);
-                log.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, mAnnotationsUsed);
+                log.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, mAnnotationsUsed ? 1 : 0);
                 log.addTaggedData(MetricsEvent.FIELD_RANKED_POSITION, selectedPos);
                 metricsLogger.write(log);
             }
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index 957a636..e56d92b 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -91,7 +91,12 @@
         } else {
             builder.setPositiveButton(R.string.ok, null);
         }
-        builder.show();
+        final AlertDialog dialog = builder.create();
+        dialog.create();
+        // Prevents screen overlay attack.
+        getWindow().setHideOverlayWindows(true);
+        dialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
+        dialog.show();
     }
 
     private String getDialogTitle() {
diff --git a/core/java/com/android/internal/logging/UiEventLoggerImpl.java b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
index 983e0fe..ffbf646 100644
--- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java
+++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
@@ -69,7 +69,8 @@
                     /* event_id = 1 */ eventID,
                     /* package_name = 2 */ packageName,
                     /* instance_id = 3 */ 0,
-                    /* position_picked = 4 */ position);
+                    /* position_picked = 4 */ position,
+                    /* is_pinned = 5 */ false);
         }
     }
 
@@ -82,7 +83,8 @@
                     /* event_id = 1 */ eventID,
                     /* package_name = 2 */ packageName,
                     /* instance_id = 3 */ instance.getId(),
-                    /* position_picked = 4 */ position);
+                    /* position_picked = 4 */ position,
+                    /* is_pinned = 5 */ false);
         } else if ((eventID > 0)) {
             logWithPosition(event, uid, packageName, position);
         }
diff --git a/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java b/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java
index 3eb9804..5adaf4f 100644
--- a/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java
+++ b/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java
@@ -28,18 +28,15 @@
 public final class NotificationAccessConfirmationActivityContract {
     public static final String EXTRA_USER_ID = "user_id";
     public static final String EXTRA_COMPONENT_NAME = "component_name";
-    public static final String EXTRA_PACKAGE_TITLE = "package_title";
 
     /**
      * Creates a launcher intent for NotificationAccessConfirmationActivity.
      */
-    public static Intent launcherIntent(Context context, int userId, ComponentName component,
-            String packageTitle) {
+    public static Intent launcherIntent(Context context, int userId, ComponentName component) {
         return new Intent()
                 .setComponent(ComponentName.unflattenFromString(context.getString(
                         R.string.config_notificationAccessConfirmationActivity)))
                 .putExtra(EXTRA_USER_ID, userId)
-                .putExtra(EXTRA_COMPONENT_NAME, component)
-                .putExtra(EXTRA_PACKAGE_TITLE, packageTitle);
+                .putExtra(EXTRA_COMPONENT_NAME, component);
     }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 3f87de2..b03a8cb 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -167,7 +167,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    static final int VERSION = 207;
+    static final int VERSION = 208;
 
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
@@ -3981,8 +3981,7 @@
         if (idxObj != null) {
             idx = idxObj;
             if ((idx & TAG_FIRST_OCCURRENCE_FLAG) != 0) {
-                idx &= ~TAG_FIRST_OCCURRENCE_FLAG;
-                mHistoryTagPool.put(tag, idx);
+                mHistoryTagPool.put(tag, idx & ~TAG_FIRST_OCCURRENCE_FLAG);
             }
             return idx;
         } else if (mNextHistoryTagIdx < HISTORY_TAG_INDEX_LIMIT) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 1db4bbb..ea5797d 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1262,7 +1262,7 @@
             }
         }
 
-        if (forceConsumingNavBar && !mDrawLegacyNavigationBarBackgroundHandled) {
+        if (forceConsumingNavBar && !hideNavigation && !mDrawLegacyNavigationBarBackgroundHandled) {
             mBackgroundInsets = Insets.of(mLastLeftInset, 0, mLastRightInset, mLastBottomInset);
         } else {
             mBackgroundInsets = Insets.NONE;
diff --git a/core/java/com/android/internal/util/ImageUtils.java b/core/java/com/android/internal/util/ImageUtils.java
index 397b2c0..62dea9d 100644
--- a/core/java/com/android/internal/util/ImageUtils.java
+++ b/core/java/com/android/internal/util/ImageUtils.java
@@ -137,6 +137,18 @@
      */
     public static Bitmap buildScaledBitmap(Drawable drawable, int maxWidth,
             int maxHeight) {
+        return buildScaledBitmap(drawable, maxWidth, maxHeight, false);
+    }
+
+    /**
+     * Convert a drawable to a bitmap, scaled to fit within maxWidth and maxHeight.
+     *
+     * @param allowUpscaling if true, the drawable will not only be scaled down, but also scaled up
+     *                       to fit within the maximum size given. This is useful for converting
+     *                       vectorized icons which usually have a very small intrinsic size.
+     */
+    public static Bitmap buildScaledBitmap(Drawable drawable, int maxWidth,
+            int maxHeight, boolean allowUpscaling) {
         if (drawable == null) {
             return null;
         }
@@ -155,7 +167,9 @@
         // a large notification icon if necessary
         float ratio = Math.min((float) maxWidth / (float) originalWidth,
                 (float) maxHeight / (float) originalHeight);
-        ratio = Math.min(1.0f, ratio);
+        if (!allowUpscaling) {
+            ratio = Math.min(1.0f, ratio);
+        }
         int scaledWidth = (int) (ratio * originalWidth);
         int scaledHeight = (int) (ratio * originalHeight);
         Bitmap result = Bitmap.createBitmap(scaledWidth, scaledHeight, Config.ARGB_8888);
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 40d89db..4e2526a 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -23,6 +23,7 @@
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.view.IInlineSuggestionsRequestCallback;
 import com.android.internal.view.IInputContext;
@@ -47,7 +48,8 @@
     void unbindInput();
 
     void startInput(in IBinder startInputToken, in IInputContext inputContext,
-            in EditorInfo attribute, boolean restarting, int navigationBarFlags);
+            in EditorInfo attribute, boolean restarting, int navigationBarFlags,
+            in ImeOnBackInvokedDispatcher imeDispatcher);
 
     void onNavButtonFlagsChanged(int navButtonFlags);
 
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index d7bb2cb..3157760 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -20,6 +20,7 @@
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.EditorInfo;
+import android.window.ImeOnBackInvokedDispatcher;
 
 import com.android.internal.inputmethod.InputBindResult;
 import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
@@ -57,7 +58,7 @@
             /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
             int windowFlags, in EditorInfo attribute, in IInputContext inputContext,
             in IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
-            int unverifiedTargetSdkVersion);
+            int unverifiedTargetSdkVersion, in ImeOnBackInvokedDispatcher imeDispatcher);
 
     void showInputMethodPickerFromClient(in IInputMethodClient client,
             int auxiliarySubtypeMode);
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index b6fbe20..f24c666 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1264,6 +1264,12 @@
     size_t numPositionMasks = 0;
     size_t numIndexMasks = 0;
 
+    int audioFormat = audioFormatFromNative(nAudioProfile->format);
+    if (audioFormat == ENCODING_INVALID) {
+        ALOGW("Unknown native audio format for JAVA API: %u", nAudioProfile->format);
+        return AUDIO_JAVA_BAD_VALUE;
+    }
+
     // count up how many masks are positional and indexed
     for (size_t index = 0; index < nAudioProfile->num_channel_masks; index++) {
         const audio_channel_mask_t mask = nAudioProfile->channel_masks[index];
@@ -1306,10 +1312,9 @@
         ALOGW("Unknown encapsulation type for JAVA API: %u", nAudioProfile->encapsulation_type);
     }
 
-    *jAudioProfile =
-            env->NewObject(gAudioProfileClass, gAudioProfileCstor,
-                           audioFormatFromNative(nAudioProfile->format), jSamplingRates.get(),
-                           jChannelMasks.get(), jChannelIndexMasks.get(), encapsulationType);
+    *jAudioProfile = env->NewObject(gAudioProfileClass, gAudioProfileCstor, audioFormat,
+                                    jSamplingRates.get(), jChannelMasks.get(),
+                                    jChannelIndexMasks.get(), encapsulationType);
 
     if (*jAudioProfile == nullptr) {
         return AUDIO_JAVA_ERROR;
@@ -1368,6 +1373,10 @@
         jobject jAudioProfile = nullptr;
         jStatus = convertAudioProfileFromNative(env, &jAudioProfile, &nAudioPort->audio_profiles[i],
                                                 useInMask);
+        if (jStatus == AUDIO_JAVA_BAD_VALUE) {
+            // skipping Java layer unsupported audio formats
+            continue;
+        }
         if (jStatus != NO_ERROR) {
             jStatus = (jint)AUDIO_JAVA_ERROR;
             goto exit;
@@ -2406,8 +2415,13 @@
         goto exit;
     }
     for (size_t i = 0; i < numSurroundFormats; i++) {
-        jobject surroundFormat = env->NewObject(gIntegerClass, gIntegerCstor,
-                                                audioFormatFromNative(surroundFormats[i]));
+        int audioFormat = audioFormatFromNative(surroundFormats[i]);
+        if (audioFormat == ENCODING_INVALID) {
+            // skipping Java layer unsupported audio formats
+            ALOGW("Unknown surround native audio format for JAVA API: %u", surroundFormats[i]);
+            continue;
+        }
+        jobject surroundFormat = env->NewObject(gIntegerClass, gIntegerCstor, audioFormat);
         jobject enabled = env->NewObject(gBooleanClass, gBooleanCstor, surroundFormatsEnabled[i]);
         env->CallObjectMethod(jSurroundFormats, gMapPut, surroundFormat, enabled);
         env->DeleteLocalRef(surroundFormat);
@@ -2453,8 +2467,13 @@
         goto exit;
     }
     for (size_t i = 0; i < numSurroundFormats; i++) {
-        jobject surroundFormat = env->NewObject(gIntegerClass, gIntegerCstor,
-                                                audioFormatFromNative(surroundFormats[i]));
+        int audioFormat = audioFormatFromNative(surroundFormats[i]);
+        if (audioFormat == ENCODING_INVALID) {
+            // skipping Java layer unsupported audio formats
+            ALOGW("Unknown surround native audio format for JAVA API: %u", surroundFormats[i]);
+            continue;
+        }
+        jobject surroundFormat = env->NewObject(gIntegerClass, gIntegerCstor, audioFormat);
         env->CallObjectMethod(jSurroundFormats, gArrayListMethods.add, surroundFormat);
         env->DeleteLocalRef(surroundFormat);
     }
@@ -2919,6 +2938,10 @@
     for (const auto &audioProfile : audioProfiles) {
         jobject jAudioProfile;
         jStatus = convertAudioProfileFromNative(env, &jAudioProfile, &audioProfile, false);
+        if (jStatus == AUDIO_JAVA_BAD_VALUE) {
+            // skipping Java layer unsupported audio formats
+            continue;
+        }
         if (jStatus != AUDIO_JAVA_SUCCESS) {
             return jStatus;
         }
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java
index 2ecc261..0dca638 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java
@@ -44,6 +44,7 @@
         // share completed fields
         public int targetType;
         public int positionPicked;
+        public boolean isPinned;
 
         CallRecord(int atomId, UiEventLogger.UiEventEnum eventId,
                 String packageName, InstanceId instanceId) {
@@ -68,12 +69,13 @@
         }
 
         CallRecord(int atomId, String packageName, InstanceId instanceId, int targetType,
-                int positionPicked) {
+                int positionPicked, boolean isPinned) {
             this.atomId = atomId;
             this.packageName = packageName;
             this.instanceId = instanceId;
             this.targetType = targetType;
             this.positionPicked = positionPicked;
+            this.isPinned = isPinned;
         }
 
     }
@@ -112,9 +114,11 @@
     }
 
     @Override
-    public void logShareTargetSelected(int targetType, String packageName, int positionPicked) {
+    public void logShareTargetSelected(int targetType, String packageName, int positionPicked,
+            boolean isPinned) {
         mCalls.add(new CallRecord(FrameworkStatsLog.RANKING_SELECTED, packageName, getInstanceId(),
-                SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(), positionPicked));
+                SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(), positionPicked,
+                isPinned));
     }
 
     @Override
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java
index 2262c05..3858792 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java
@@ -35,6 +35,7 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
+@SuppressWarnings("GuardedBy")
 public class BatteryStatsHistoryIteratorTest {
     private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
 
@@ -124,7 +125,10 @@
         // More than 32k strings
         final int eventCount = 0x7FFF + 100;
         for (int i = 0; i < eventCount; i++) {
-            mBatteryStats.noteAlarmStartLocked("a" + i, null, APP_UID, 3_000_000, 2_000_000);
+            // Names repeat in order to verify de-duping of identical history tags.
+            String name = "a" + (i % 10);
+            mBatteryStats.noteAlarmStartLocked(name, null, APP_UID, 3_000_000, 2_000_000);
+            mBatteryStats.noteAlarmFinishLocked(name, null, APP_UID, 3_500_000, 2_500_000);
         }
 
         final BatteryStatsHistoryIterator iterator =
@@ -149,10 +153,23 @@
         assertThat(item.time).isEqualTo(2_000_000);
 
         for (int i = 0; i < eventCount; i++) {
+            String name = "a" + (i % 10);
             assertThat(iterator.next(item)).isTrue();
+            // Skip a blank event inserted at the start of every buffer
+            if (item.eventCode == BatteryStats.HistoryItem.EVENT_NONE) {
+                assertThat(iterator.next(item)).isTrue();
+            }
             assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_ALARM
                     | BatteryStats.HistoryItem.EVENT_FLAG_START);
-            assertThat(item.eventTag.string).isEqualTo("a" + i);
+            assertThat(item.eventTag.string).isEqualTo(name);
+
+            assertThat(iterator.next(item)).isTrue();
+            if (item.eventCode == BatteryStats.HistoryItem.EVENT_NONE) {
+                assertThat(iterator.next(item)).isTrue();
+            }
+            assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_ALARM
+                    | BatteryStats.HistoryItem.EVENT_FLAG_FINISH);
+            assertThat(item.eventTag.string).isEqualTo(name);
         }
 
         assertThat(iterator.next(item)).isFalse();
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 0e8388b..60da2e8 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2485,12 +2485,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "323235828": {
-      "message": "Delaying app transition for recents animation to finish",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_APP_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/AppTransitionController.java"
-    },
     "327461496": {
       "message": "Complete pause: %s",
       "level": "VERBOSE",
@@ -2581,12 +2575,6 @@
       "group": "WM_DEBUG_ANIM",
       "at": "com\/android\/server\/wm\/WindowContainer.java"
     },
-    "397105698": {
-      "message": "grantEmbeddedWindowFocus remove request for win=%s dropped since no candidate was found",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_FOCUS",
-      "at": "com\/android\/server\/wm\/WindowManagerService.java"
-    },
     "397382873": {
       "message": "Moving to PAUSED: %s %s",
       "level": "VERBOSE",
@@ -3115,6 +3103,12 @@
       "group": "WM_DEBUG_LOCKTASK",
       "at": "com\/android\/server\/wm\/LockTaskController.java"
     },
+    "958338552": {
+      "message": "grantEmbeddedWindowFocus win=%s dropped focus so setting focus to null since no candidate was found",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_FOCUS",
+      "at": "com\/android\/server\/wm\/WindowManagerService.java"
+    },
     "959486822": {
       "message": "setSyncGroup #%d on %s",
       "level": "VERBOSE",
diff --git a/data/keyboards/Vendor_0957_Product_0001.kl b/data/keyboards/Vendor_0957_Product_0001.kl
index 13b4096..54f8808 100644
--- a/data/keyboards/Vendor_0957_Product_0001.kl
+++ b/data/keyboards/Vendor_0957_Product_0001.kl
@@ -44,33 +44,37 @@
 key 11    0
 
 # custom keys
-key usage 0x000c019C    PROFILE_SWITCH
-key usage 0x000c01A2    ALL_APPS
 key usage 0x000c01BB    TV_INPUT
-key usage 0x000c022A    BOOKMARK
-key usage 0x000c0096    SETTINGS
-key usage 0x000c009F    NOTIFICATION
-key usage 0x000c008D    GUIDE
-key usage 0x000c0089    TV
-key usage 0x000c009C    CHANNEL_UP
-key usage 0x000c009D    CHANNEL_DOWN
-key usage 0x000c00CD    MEDIA_PLAY_PAUSE
-key usage 0x000c00B2    MEDIA_RECORD
-key usage 0x000c00B4    MEDIA_SKIP_BACKWARD
-key usage 0x000c00B3    MEDIA_SKIP_FORWARD
-key usage 0x000c0226    MEDIA_STOP
 
-key usage 0x000c0077    BUTTON_3     WAKE #YouTube
-key usage 0x000c0078    BUTTON_4     WAKE #Netflix
-key usage 0x000c0079    BUTTON_6     WAKE #Disney+
-key usage 0x000c007A    BUTTON_7     WAKE #HBOmax
-
-key usage 0x00070037    PERIOD
-key usage 0x000c01BD    INFO
-key usage 0x000c0061    CAPTIONS
 key usage 0x000c0185    TV_TELETEXT
+key usage 0x000c0061    CAPTIONS
+
+key usage 0x000c01BD    INFO
+key usage 0x000c0037    PERIOD
 
 key usage 0x000c0069    PROG_RED
 key usage 0x000c006A    PROG_GREEN
+key usage 0x000c006C    PROG_YELLOW
 key usage 0x000c006B    PROG_BLUE
-key usage 0x000c006C    PROG_YELLOW
\ No newline at end of file
+key usage 0x000c00B4    MEDIA_SKIP_BACKWARD
+key usage 0x000c00CD    MEDIA_PLAY_PAUSE
+key usage 0x000c00B2    MEDIA_RECORD
+key usage 0x000c00B3    MEDIA_SKIP_FORWARD
+
+key usage 0x000c022A    BOOKMARK
+key usage 0x000c01A2    ALL_APPS
+key usage 0x000c019C    PROFILE_SWITCH
+
+key usage 0x000c0096    SETTINGS
+key usage 0x000c009F    NOTIFICATION
+
+key usage 0x000c008D    GUIDE
+key usage 0x000c0089    TV
+
+key usage 0x000c009C    CHANNEL_UP
+key usage 0x000c009D    CHANNEL_DOWN
+
+key usage 0x000c0077    BUTTON_3     WAKE #YouTube
+key usage 0x000c0078    BUTTON_4     WAKE #Netflix
+key usage 0x000c0079    BUTTON_6     WAKE
+key usage 0x000c007A    BUTTON_7     WAKE
\ No newline at end of file
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPrivateKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPrivateKey.java
new file mode 100644
index 0000000..4855ad0
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPrivateKey.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore2;
+
+import android.annotation.NonNull;
+import android.security.KeyStoreSecurityLevel;
+import android.system.keystore2.Authorization;
+import android.system.keystore2.KeyDescriptor;
+
+import java.security.PrivateKey;
+import java.security.interfaces.EdECKey;
+import java.security.spec.NamedParameterSpec;
+
+/**
+ * EdEC private key (instance of {@link PrivateKey} and {@link EdECKey}) backed by keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreEdECPrivateKey extends AndroidKeyStorePrivateKey implements EdECKey {
+    public AndroidKeyStoreEdECPrivateKey(
+            @NonNull KeyDescriptor descriptor, long keyId,
+            @NonNull Authorization[] authorizations,
+            @NonNull String algorithm,
+            @NonNull KeyStoreSecurityLevel securityLevel) {
+        super(descriptor, keyId, authorizations, algorithm, securityLevel);
+    }
+
+    @Override
+    public NamedParameterSpec getParams() {
+        return NamedParameterSpec.ED25519;
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java
new file mode 100644
index 0000000..642e088
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore2;
+
+import android.annotation.NonNull;
+import android.security.KeyStoreSecurityLevel;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyMetadata;
+
+import java.math.BigInteger;
+import java.security.interfaces.EdECPublicKey;
+import java.security.spec.EdECPoint;
+import java.security.spec.NamedParameterSpec;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * {@link EdECPublicKey} backed by keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreEdECPublicKey extends AndroidKeyStorePublicKey
+        implements EdECPublicKey {
+    /**
+     * DER sequence, as defined in https://datatracker.ietf.org/doc/html/rfc8410#section-4 and
+     * https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.
+     * SEQUENCE (2 elem)
+     *  SEQUENCE (1 elem)
+     *    OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm)
+     *    as defined in https://datatracker.ietf.org/doc/html/rfc8410#section-3
+     *  BIT STRING (256 bit) as defined in
+     *  https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.2
+     */
+    private static final byte[] DER_KEY_PREFIX = new byte[] {
+            0x30,
+            0x2a,
+            0x30,
+            0x05,
+            0x06,
+            0x03,
+            0x2b,
+            0x65,
+            0x70,
+            0x03,
+            0x21,
+            0x00,
+    };
+    private static final int ED25519_KEY_SIZE_BYTES = 32;
+
+    private byte[] mEncodedKey;
+    private EdECPoint mPoint;
+
+    public AndroidKeyStoreEdECPublicKey(
+            @NonNull KeyDescriptor descriptor,
+            @NonNull KeyMetadata metadata,
+            @NonNull String algorithm,
+            @NonNull KeyStoreSecurityLevel iSecurityLevel,
+            @NonNull byte[] encodedKey) {
+        super(descriptor, metadata, encodedKey, algorithm, iSecurityLevel);
+        mEncodedKey = encodedKey;
+
+        int preambleLength = matchesPreamble(DER_KEY_PREFIX, encodedKey);
+        if (preambleLength == 0) {
+            throw new IllegalArgumentException("Key size is not correct size");
+        }
+
+        mPoint = pointFromKeyByteArray(
+                Arrays.copyOfRange(encodedKey, preambleLength, encodedKey.length));
+    }
+
+    @Override
+    AndroidKeyStorePrivateKey getPrivateKey() {
+        return new AndroidKeyStoreEdECPrivateKey(
+                getUserKeyDescriptor(),
+                getKeyIdDescriptor().nspace,
+                getAuthorizations(),
+                "EdDSA",
+                getSecurityLevel());
+    }
+
+    @Override
+    public NamedParameterSpec getParams() {
+        return NamedParameterSpec.ED25519;
+    }
+
+    @Override
+    public EdECPoint getPoint() {
+        return mPoint;
+    }
+
+    private static int matchesPreamble(byte[] preamble, byte[] encoded) {
+        if (encoded.length != (preamble.length + ED25519_KEY_SIZE_BYTES)) {
+            return 0;
+        }
+        if (Arrays.compare(preamble, Arrays.copyOf(encoded, preamble.length)) != 0) {
+            return 0;
+        }
+        return preamble.length;
+    }
+
+    private static EdECPoint pointFromKeyByteArray(byte[] coordinates) {
+        Objects.requireNonNull(coordinates);
+
+        // Oddity of the key is the most-significant bit of the last byte.
+        boolean isOdd = (0x80 & coordinates[coordinates.length - 1]) != 0;
+        // Zero out the oddity bit.
+        coordinates[coordinates.length - 1] &= (byte) 0x7f;
+        // Representation of Y is in little-endian, according to rfc8032 section-3.1.
+        reverse(coordinates);
+        // The integer representing Y starts from the first bit in the coordinates array.
+        BigInteger y = new BigInteger(1, coordinates);
+        return new EdECPoint(isOdd, y);
+    }
+
+    private static void reverse(byte[] coordinateArray) {
+        int start = 0;
+        int end = coordinateArray.length - 1;
+        while (start < end) {
+            byte tmp = coordinateArray[start];
+            coordinateArray[start] = coordinateArray[end];
+            coordinateArray[end] = tmp;
+            start++;
+            end--;
+        }
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        return mEncodedKey.clone();
+    }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index d31499e..0355628 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -224,7 +224,6 @@
 
         String jcaKeyAlgorithm = publicKey.getAlgorithm();
 
-        KeyStoreSecurityLevel securityLevel = iSecurityLevel;
         if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(jcaKeyAlgorithm)) {
             return new AndroidKeyStoreECPublicKey(descriptor, metadata,
                     iSecurityLevel, (ECPublicKey) publicKey);
@@ -232,8 +231,9 @@
             return new AndroidKeyStoreRSAPublicKey(descriptor, metadata,
                     iSecurityLevel, (RSAPublicKey) publicKey);
         } else if (ED25519_OID.equalsIgnoreCase(jcaKeyAlgorithm)) {
-            //TODO(b/214203951) missing classes in conscrypt
-            throw new ProviderException("Curve " + ED25519_OID + " not supported yet");
+            final byte[] publicKeyEncoded = publicKey.getEncoded();
+            return new AndroidKeyStoreEdECPublicKey(descriptor, metadata, ED25519_OID,
+                    iSecurityLevel, publicKeyEncoded);
         } else if (X25519_ALIAS.equalsIgnoreCase(jcaKeyAlgorithm)) {
             //TODO(b/214203951) missing classes in conscrypt
             throw new ProviderException("Curve " + X25519_ALIAS + " not supported yet");
diff --git a/keystore/tests/src/android/security/keystore2/AndroidKeyStoreEdECPublicKeyTest.java b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreEdECPublicKeyTest.java
new file mode 100644
index 0000000..5bd5797
--- /dev/null
+++ b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreEdECPublicKeyTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore2;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import android.security.KeyStoreSecurityLevel;
+import android.system.keystore2.Authorization;
+import android.system.keystore2.Domain;
+import android.system.keystore2.KeyDescriptor;
+import android.system.keystore2.KeyMetadata;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.math.BigInteger;
+import java.util.Base64;
+
+@RunWith(AndroidJUnit4.class)
+public class AndroidKeyStoreEdECPublicKeyTest {
+    private static KeyDescriptor descriptor() {
+        final KeyDescriptor keyDescriptor = new KeyDescriptor();
+        keyDescriptor.alias = "key";
+        keyDescriptor.blob = null;
+        keyDescriptor.domain = Domain.APP;
+        keyDescriptor.nspace = -1;
+        return keyDescriptor;
+    }
+
+    private static KeyMetadata metadata(byte[] cert, byte[] certChain) {
+        KeyMetadata metadata = new KeyMetadata();
+        metadata.authorizations = new Authorization[0];
+        metadata.certificate = cert;
+        metadata.certificateChain = certChain;
+        metadata.key = descriptor();
+        metadata.modificationTimeMs = 0;
+        metadata.keySecurityLevel = 1;
+        return metadata;
+    }
+
+    @Mock
+    private KeyStoreSecurityLevel mKeystoreSecurityLevel;
+
+    private static class EdECTestVector {
+        public final byte[] encodedKeyBytes;
+        public final boolean isOdd;
+        public final BigInteger yValue;
+
+        EdECTestVector(String b64KeyBytes, boolean isOdd, String yValue) {
+            this.encodedKeyBytes = Base64.getDecoder().decode(b64KeyBytes);
+            this.isOdd = isOdd;
+            this.yValue = new BigInteger(yValue);
+        }
+    }
+
+    private static final EdECTestVector[] ED_EC_TEST_VECTORS = new EdECTestVector[]{
+            new EdECTestVector("MCowBQYDK2VwAyEADE+wvQqNHxaERPhAZ0rCFlgFbfWLs/YonPXdSTw0VSo=",
+                    false,
+                    "19147682157189290216699341180089409126316261024914226007941553249095116672780"
+                    ),
+            new EdECTestVector("MCowBQYDK2VwAyEA/0E1IRNzGj85Ot/TPeXqifkqTkdk4voleH0hIq59D9w=",
+                    true,
+                    "41640152188550647350742178040529506688513911269563908889464821205156322689535"
+                    ),
+            new EdECTestVector("MCowBQYDK2VwAyEAunOvGuenetl9GQSXGVo5L3RIr4OOIpFIv/Zre8qTc/8=",
+                    true,
+                    "57647939198144376128225770417635248407428273266444593100194116168980378907578"
+                    ),
+            new EdECTestVector("MCowBQYDK2VwAyEA2hHqaZ5IolswN1Yd58Y4hzhmUMCCqc4PW5A/SFLmTX8=",
+                    false,
+                    "57581368614046789120409806291852629847774713088410311752049592044694364885466"
+                    ),
+    };
+
+    @Test
+    public void testParsingOfValidKeys() {
+        for (EdECTestVector testVector : ED_EC_TEST_VECTORS) {
+            AndroidKeyStoreEdECPublicKey pkey = new AndroidKeyStoreEdECPublicKey(descriptor(),
+                    metadata(null, null), "EdDSA", mKeystoreSecurityLevel,
+                    testVector.encodedKeyBytes);
+
+            assertEquals(pkey.getPoint().isXOdd(), testVector.isOdd);
+            assertEquals(pkey.getPoint().getY(), testVector.yValue);
+        }
+    }
+
+    @Test
+    public void testFailedParsingOfKeysWithDifferentOid() {
+        final byte[] testVectorWithIncorrectOid = Base64.getDecoder().decode(
+                "MCowBQYDLGVwAyEADE+wvQqNHxaERPhAZ0rCFlgFbfWLs/YonPXdSTw0VSo=");
+        assertThrows("OID should be unrecognized", IllegalArgumentException.class,
+                () -> new AndroidKeyStoreEdECPublicKey(descriptor(), metadata(null, null), "EdDSA",
+                        mKeystoreSecurityLevel, testVectorWithIncorrectOid));
+    }
+
+    @Test
+    public void testFailedParsingOfKeysWithWrongSize() {
+        final byte[] testVectorWithIncorrectKeySize = Base64.getDecoder().decode(
+        "MCwwBQYDK2VwAyMADE+wvQqNHxaERPhAZ0rCFlgFbfWLs/YonPXdSTw0VSrOzg==");
+        assertThrows("Key length should be invalid", IllegalArgumentException.class,
+                () -> new AndroidKeyStoreEdECPublicKey(descriptor(), metadata(null, null), "EdDSA",
+                        mKeystoreSecurityLevel, testVectorWithIncorrectKeySize));
+    }
+}
+
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index c3fbe55..8fa9f56 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -246,9 +246,13 @@
      * {@link BackAnimationController}
      */
     public void onMotionEvent(MotionEvent event, int action, @BackEvent.SwipeEdge int swipeEdge) {
-        if (action == MotionEvent.ACTION_DOWN) {
-            initAnimation(event);
-        } else if (action == MotionEvent.ACTION_MOVE) {
+        if (action == MotionEvent.ACTION_MOVE) {
+            if (!mBackGestureStarted) {
+                // Let the animation initialized here to make sure the onPointerDownOutsideFocus
+                // could be happened when ACTION_DOWN, it may change the current focus that we
+                // would access it when startBackNavigation.
+                initAnimation(event);
+            }
             onMove(event, swipeEdge);
         } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
             ProtoLog.d(WM_SHELL_BACK_PREVIEW,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 145e527..dfd4362 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -143,8 +143,11 @@
     @Provides
     static TvPipNotificationController provideTvPipNotificationController(Context context,
             PipMediaController pipMediaController,
+            PipParamsChangedForwarder pipParamsChangedForwarder,
+            TvPipBoundsState tvPipBoundsState,
             @ShellMainThread Handler mainHandler) {
-        return new TvPipNotificationController(context, pipMediaController, mainHandler);
+        return new TvPipNotificationController(context, pipMediaController,
+                pipParamsChangedForwarder, tvPipBoundsState, mainHandler);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
index 8a50f22..65a12d6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java
@@ -32,6 +32,7 @@
 import android.graphics.drawable.Icon;
 import android.media.MediaMetadata;
 import android.media.session.MediaController;
+import android.media.session.MediaSession;
 import android.media.session.MediaSessionManager;
 import android.media.session.PlaybackState;
 import android.os.Handler;
@@ -64,7 +65,7 @@
      */
     public interface ActionListener {
         /**
-         * Called when the media actions changes.
+         * Called when the media actions changed.
          */
         void onMediaActionsChanged(List<RemoteAction> actions);
     }
@@ -74,11 +75,21 @@
      */
     public interface MetadataListener {
         /**
-         * Called when the media metadata changes.
+         * Called when the media metadata changed.
          */
         void onMediaMetadataChanged(MediaMetadata metadata);
     }
 
+    /**
+     * A listener interface to receive notification on changes to the media session token.
+     */
+    public interface TokenListener {
+        /**
+         * Called when the media session token changed.
+         */
+        void onMediaSessionTokenChanged(MediaSession.Token token);
+    }
+
     private final Context mContext;
     private final Handler mMainHandler;
     private final HandlerExecutor mHandlerExecutor;
@@ -133,6 +144,7 @@
 
     private final ArrayList<ActionListener> mActionListeners = new ArrayList<>();
     private final ArrayList<MetadataListener> mMetadataListeners = new ArrayList<>();
+    private final ArrayList<TokenListener> mTokenListeners = new ArrayList<>();
 
     public PipMediaController(Context context, Handler mainHandler) {
         mContext = context;
@@ -204,6 +216,31 @@
         mMetadataListeners.remove(listener);
     }
 
+    /**
+     * Adds a new token listener.
+     */
+    public void addTokenListener(TokenListener listener) {
+        if (!mTokenListeners.contains(listener)) {
+            mTokenListeners.add(listener);
+            listener.onMediaSessionTokenChanged(getToken());
+        }
+    }
+
+    /**
+     * Removes a token listener.
+     */
+    public void removeTokenListener(TokenListener listener) {
+        listener.onMediaSessionTokenChanged(null);
+        mTokenListeners.remove(listener);
+    }
+
+    private MediaSession.Token getToken() {
+        if (mMediaController == null) {
+            return null;
+        }
+        return mMediaController.getSessionToken();
+    }
+
     private MediaMetadata getMediaMetadata() {
         return mMediaController != null ? mMediaController.getMetadata() : null;
     }
@@ -294,6 +331,7 @@
             }
             notifyActionsChanged();
             notifyMetadataChanged(getMediaMetadata());
+            notifyTokenChanged(getToken());
 
             // TODO(winsonc): Consider if we want to close the PIP after a timeout (like on TV)
         }
@@ -317,4 +355,10 @@
             mMetadataListeners.forEach(l -> l.onMediaMetadataChanged(metadata));
         }
     }
+
+    private void notifyTokenChanged(MediaSession.Token token) {
+        if (!mTokenListeners.isEmpty()) {
+            mTokenListeners.forEach(l -> l.onMediaSessionTokenChanged(token));
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index fcd1f95..7667794 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -293,6 +293,8 @@
         }
         mTvPipBoundsState.setTvPipManuallyCollapsed(!expanding);
         mTvPipBoundsState.setTvPipExpanded(expanding);
+        mPipNotificationController.updateExpansionState();
+
         updatePinnedStackBounds();
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
index 4033f03..61a609d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java
@@ -16,36 +16,47 @@
 
 package com.android.wm.shell.pip.tv;
 
+import static android.app.Notification.Action.SEMANTIC_ACTION_DELETE;
+import static android.app.Notification.Action.SEMANTIC_ACTION_NONE;
+
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.RemoteAction;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
-import android.media.MediaMetadata;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.media.session.MediaSession;
+import android.os.Bundle;
 import android.os.Handler;
 import android.text.TextUtils;
 
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.ImageUtils;
 import com.android.wm.shell.R;
 import com.android.wm.shell.pip.PipMediaController;
+import com.android.wm.shell.pip.PipParamsChangedForwarder;
+import com.android.wm.shell.pip.PipUtils;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
 
-import java.util.Objects;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
- * A notification that informs users that PIP is running and also provides PIP controls.
- * <p>Once it's created, it will manage the PIP notification UI by itself except for handling
- * configuration changes.
+ * A notification that informs users that PiP is running and also provides PiP controls.
+ * <p>Once it's created, it will manage the PiP notification UI by itself except for handling
+ * configuration changes and user initiated expanded PiP toggling.
  */
 public class TvPipNotificationController {
     private static final String TAG = "TvPipNotification";
-    private static final boolean DEBUG = TvPipController.DEBUG;
 
     // Referenced in com.android.systemui.util.NotificationChannels.
     public static final String NOTIFICATION_CHANNEL = "TVPIP";
@@ -60,6 +71,8 @@
             "com.android.wm.shell.pip.tv.notification.action.MOVE_PIP";
     private static final String ACTION_TOGGLE_EXPANDED_PIP =
             "com.android.wm.shell.pip.tv.notification.action.TOGGLE_EXPANDED_PIP";
+    private static final String ACTION_FULLSCREEN =
+            "com.android.wm.shell.pip.tv.notification.action.FULLSCREEN";
 
     private final Context mContext;
     private final PackageManager mPackageManager;
@@ -68,44 +81,88 @@
     private final ActionBroadcastReceiver mActionBroadcastReceiver;
     private final Handler mMainHandler;
     private Delegate mDelegate;
+    private final TvPipBoundsState mTvPipBoundsState;
 
     private String mDefaultTitle;
 
+    private final List<RemoteAction> mCustomActions = new ArrayList<>();
+    private final List<RemoteAction> mMediaActions = new ArrayList<>();
+    private RemoteAction mCustomCloseAction;
+
+    private MediaSession.Token mMediaSessionToken;
+
     /** Package name for the application that owns PiP window. */
     private String mPackageName;
-    private boolean mNotified;
-    private String mMediaTitle;
-    private Bitmap mArt;
+
+    private boolean mIsNotificationShown;
+    private String mPipTitle;
+    private String mPipSubtitle;
+
+    private Bitmap mActivityIcon;
 
     public TvPipNotificationController(Context context, PipMediaController pipMediaController,
+            PipParamsChangedForwarder pipParamsChangedForwarder, TvPipBoundsState tvPipBoundsState,
             Handler mainHandler) {
         mContext = context;
         mPackageManager = context.getPackageManager();
         mNotificationManager = context.getSystemService(NotificationManager.class);
         mMainHandler = mainHandler;
+        mTvPipBoundsState = tvPipBoundsState;
 
         mNotificationBuilder = new Notification.Builder(context, NOTIFICATION_CHANNEL)
                 .setLocalOnly(true)
-                .setOngoing(false)
+                .setOngoing(true)
                 .setCategory(Notification.CATEGORY_SYSTEM)
                 .setShowWhen(true)
                 .setSmallIcon(R.drawable.pip_icon)
+                .setAllowSystemGeneratedContextualActions(false)
+                .setContentIntent(createPendingIntent(context, ACTION_FULLSCREEN))
+                .setDeleteIntent(getCloseAction().actionIntent)
                 .extend(new Notification.TvExtender()
                         .setContentIntent(createPendingIntent(context, ACTION_SHOW_PIP_MENU))
                         .setDeleteIntent(createPendingIntent(context, ACTION_CLOSE_PIP)));
 
         mActionBroadcastReceiver = new ActionBroadcastReceiver();
 
-        pipMediaController.addMetadataListener(this::onMediaMetadataChanged);
+        pipMediaController.addActionListener(this::onMediaActionsChanged);
+        pipMediaController.addTokenListener(this::onMediaSessionTokenChanged);
+
+        pipParamsChangedForwarder.addListener(
+                new PipParamsChangedForwarder.PipParamsChangedCallback() {
+                    @Override
+                    public void onExpandedAspectRatioChanged(float ratio) {
+                        updateExpansionState();
+                    }
+
+                    @Override
+                    public void onActionsChanged(List<RemoteAction> actions,
+                            RemoteAction closeAction) {
+                        mCustomActions.clear();
+                        mCustomActions.addAll(actions);
+                        mCustomCloseAction = closeAction;
+                        updateNotificationContent();
+                    }
+
+                    @Override
+                    public void onTitleChanged(String title) {
+                        mPipTitle = title;
+                        updateNotificationContent();
+                    }
+
+                    @Override
+                    public void onSubtitleChanged(String subtitle) {
+                        mPipSubtitle = subtitle;
+                        updateNotificationContent();
+                    }
+                });
 
         onConfigurationChanged(context);
     }
 
     void setDelegate(Delegate delegate) {
-        if (DEBUG) {
-            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                    "%s: setDelegate(), delegate=%s", TAG, delegate);
-        }
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: setDelegate(), delegate=%s",
+                TAG, delegate);
+
         if (mDelegate != null) {
             throw new IllegalStateException(
                     "The delegate has already been set and should not change.");
@@ -118,90 +175,181 @@
     }
 
     void show(String packageName) {
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: show %s", TAG, packageName);
         if (mDelegate == null) {
             throw new IllegalStateException("Delegate is not set.");
         }
 
+        mIsNotificationShown = true;
         mPackageName = packageName;
-        update();
+        mActivityIcon = getActivityIcon();
         mActionBroadcastReceiver.register();
+
+        updateNotificationContent();
     }
 
     void dismiss() {
-        mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP);
-        mNotified = false;
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: dismiss()", TAG);
+
+        mIsNotificationShown = false;
         mPackageName = null;
         mActionBroadcastReceiver.unregister();
+
+        mNotificationManager.cancel(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP);
     }
 
-    private void onMediaMetadataChanged(MediaMetadata metadata) {
-        if (updateMediaControllerMetadata(metadata) && mNotified) {
-            // update notification
-            update();
+    private Notification.Action getToggleAction(boolean expanded) {
+        if (expanded) {
+            return createSystemAction(R.drawable.pip_ic_collapse,
+                    R.string.pip_collapse, ACTION_TOGGLE_EXPANDED_PIP);
+        } else {
+            return createSystemAction(R.drawable.pip_ic_expand, R.string.pip_expand,
+                    ACTION_TOGGLE_EXPANDED_PIP);
         }
     }
 
+    private Notification.Action createSystemAction(int iconRes, int titleRes, String action) {
+        Notification.Action.Builder builder = new Notification.Action.Builder(
+                Icon.createWithResource(mContext, iconRes),
+                mContext.getString(titleRes),
+                createPendingIntent(mContext, action));
+        builder.setContextual(true);
+        return builder.build();
+    }
+
+    private void onMediaActionsChanged(List<RemoteAction> actions) {
+        mMediaActions.clear();
+        mMediaActions.addAll(actions);
+        if (mCustomActions.isEmpty()) {
+            updateNotificationContent();
+        }
+    }
+
+    private void onMediaSessionTokenChanged(MediaSession.Token token) {
+        mMediaSessionToken = token;
+        updateNotificationContent();
+    }
+
+    private Notification.Action remoteToNotificationAction(RemoteAction action) {
+        return remoteToNotificationAction(action, SEMANTIC_ACTION_NONE);
+    }
+
+    private Notification.Action remoteToNotificationAction(RemoteAction action,
+            int semanticAction) {
+        Notification.Action.Builder builder = new Notification.Action.Builder(action.getIcon(),
+                action.getTitle(),
+                action.getActionIntent());
+        if (action.getContentDescription() != null) {
+            Bundle extras = new Bundle();
+            extras.putCharSequence(Notification.EXTRA_PICTURE_CONTENT_DESCRIPTION,
+                    action.getContentDescription());
+            builder.addExtras(extras);
+        }
+        builder.setSemanticAction(semanticAction);
+        builder.setContextual(true);
+        return builder.build();
+    }
+
+    private Notification.Action[] getNotificationActions() {
+        final List<Notification.Action> actions = new ArrayList<>();
+
+        // 1. Fullscreen
+        actions.add(getFullscreenAction());
+        // 2. Close
+        actions.add(getCloseAction());
+        // 3. App actions
+        final List<RemoteAction> appActions =
+                mCustomActions.isEmpty() ? mMediaActions : mCustomActions;
+        for (RemoteAction appAction : appActions) {
+            if (PipUtils.remoteActionsMatch(mCustomCloseAction, appAction)
+                    || !appAction.isEnabled()) {
+                continue;
+            }
+            actions.add(remoteToNotificationAction(appAction));
+        }
+        // 4. Move
+        actions.add(getMoveAction());
+        // 5. Toggle expansion (if expanded PiP enabled)
+        if (mTvPipBoundsState.getDesiredTvExpandedAspectRatio() > 0
+                && mTvPipBoundsState.isTvExpandedPipSupported()) {
+            actions.add(getToggleAction(mTvPipBoundsState.isTvPipExpanded()));
+        }
+        return actions.toArray(new Notification.Action[0]);
+    }
+
+    private Notification.Action getCloseAction() {
+        if (mCustomCloseAction == null) {
+            return createSystemAction(R.drawable.pip_ic_close_white, R.string.pip_close,
+                    ACTION_CLOSE_PIP);
+        } else {
+            return remoteToNotificationAction(mCustomCloseAction, SEMANTIC_ACTION_DELETE);
+        }
+    }
+
+    private Notification.Action getFullscreenAction() {
+        return createSystemAction(R.drawable.pip_ic_fullscreen_white,
+                R.string.pip_fullscreen, ACTION_FULLSCREEN);
+    }
+
+    private Notification.Action getMoveAction() {
+        return createSystemAction(R.drawable.pip_ic_move_white, R.string.pip_move,
+                ACTION_MOVE_PIP);
+    }
+
     /**
-     * Called by {@link PipController} when the configuration is changed.
+     * Called by {@link TvPipController} when the configuration is changed.
      */
     void onConfigurationChanged(Context context) {
         mDefaultTitle = context.getResources().getString(R.string.pip_notification_unknown_title);
-        if (mNotified) {
-            // Update the notification.
-            update();
-        }
+        updateNotificationContent();
     }
 
-    private void update() {
-        mNotified = true;
+    void updateExpansionState() {
+        updateNotificationContent();
+    }
+
+    private void updateNotificationContent() {
+        if (mPackageManager == null || !mIsNotificationShown) {
+            return;
+        }
+
+        Notification.Action[] actions = getNotificationActions();
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                "%s: update(), title: %s, subtitle: %s, mediaSessionToken: %s, #actions: %s", TAG,
+                getNotificationTitle(), mPipSubtitle, mMediaSessionToken, actions.length);
+        for (Notification.Action action : actions) {
+            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: action: %s", TAG,
+                    action.toString());
+        }
+
         mNotificationBuilder
                 .setWhen(System.currentTimeMillis())
-                .setContentTitle(getNotificationTitle());
-        if (mArt != null) {
-            mNotificationBuilder.setStyle(new Notification.BigPictureStyle()
-                    .bigPicture(mArt));
-        } else {
-            mNotificationBuilder.setStyle(null);
-        }
+                .setContentTitle(getNotificationTitle())
+                .setContentText(mPipSubtitle)
+                .setSubText(getApplicationLabel(mPackageName))
+                .setActions(actions);
+        setPipIcon();
+
+        Bundle extras = new Bundle();
+        extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, mMediaSessionToken);
+        mNotificationBuilder.setExtras(extras);
+
+        // TvExtender not recognized if not set last.
+        mNotificationBuilder.extend(new Notification.TvExtender()
+                .setContentIntent(createPendingIntent(mContext, ACTION_SHOW_PIP_MENU))
+                .setDeleteIntent(createPendingIntent(mContext, ACTION_CLOSE_PIP)));
         mNotificationManager.notify(NOTIFICATION_TAG, SystemMessage.NOTE_TV_PIP,
                 mNotificationBuilder.build());
     }
 
-    private boolean updateMediaControllerMetadata(MediaMetadata metadata) {
-        String title = null;
-        Bitmap art = null;
-        if (metadata != null) {
-            title = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE);
-            if (TextUtils.isEmpty(title)) {
-                title = metadata.getString(MediaMetadata.METADATA_KEY_TITLE);
-            }
-            art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
-            if (art == null) {
-                art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
-            }
-        }
-
-        if (TextUtils.equals(title, mMediaTitle) && Objects.equals(art, mArt)) {
-            return false;
-        }
-
-        mMediaTitle = title;
-        mArt = art;
-
-        return true;
-    }
-
-
     private String getNotificationTitle() {
-        if (!TextUtils.isEmpty(mMediaTitle)) {
-            return mMediaTitle;
+        if (!TextUtils.isEmpty(mPipTitle)) {
+            return mPipTitle;
         }
-
         final String applicationTitle = getApplicationLabel(mPackageName);
         if (!TextUtils.isEmpty(applicationTitle)) {
             return applicationTitle;
         }
-
         return mDefaultTitle;
     }
 
@@ -214,10 +362,37 @@
         }
     }
 
+    private void setPipIcon() {
+        if (mActivityIcon != null) {
+            mNotificationBuilder.setLargeIcon(mActivityIcon);
+            return;
+        }
+        // Fallback: Picture-in-Picture icon
+        mNotificationBuilder.setLargeIcon(Icon.createWithResource(mContext, R.drawable.pip_icon));
+    }
+
+    private Bitmap getActivityIcon() {
+        if (mContext == null) return null;
+        ComponentName componentName = PipUtils.getTopPipActivity(mContext).first;
+        if (componentName == null) return null;
+
+        Drawable drawable;
+        try {
+            drawable = mPackageManager.getActivityIcon(componentName);
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+        int width = mContext.getResources().getDimensionPixelSize(
+                android.R.dimen.notification_large_icon_width);
+        int height = mContext.getResources().getDimensionPixelSize(
+                android.R.dimen.notification_large_icon_height);
+        return ImageUtils.buildScaledBitmap(drawable, width, height, /* allowUpscaling */ true);
+    }
+
     private static PendingIntent createPendingIntent(Context context, String action) {
         return PendingIntent.getBroadcast(context, 0,
                 new Intent(action).setPackage(context.getPackageName()),
-                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
     }
 
     private class ActionBroadcastReceiver extends BroadcastReceiver {
@@ -228,6 +403,7 @@
             mIntentFilter.addAction(ACTION_SHOW_PIP_MENU);
             mIntentFilter.addAction(ACTION_MOVE_PIP);
             mIntentFilter.addAction(ACTION_TOGGLE_EXPANDED_PIP);
+            mIntentFilter.addAction(ACTION_FULLSCREEN);
         }
         boolean mRegistered = false;
 
@@ -249,10 +425,8 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
-            if (DEBUG) {
-                ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                        "%s: on(Broadcast)Receive(), action=%s", TAG, action);
-            }
+            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                    "%s: on(Broadcast)Receive(), action=%s", TAG, action);
 
             if (ACTION_SHOW_PIP_MENU.equals(action)) {
                 mDelegate.showPictureInPictureMenu();
@@ -262,14 +436,21 @@
                 mDelegate.enterPipMovementMenu();
             } else if (ACTION_TOGGLE_EXPANDED_PIP.equals(action)) {
                 mDelegate.togglePipExpansion();
+            } else if (ACTION_FULLSCREEN.equals(action)) {
+                mDelegate.movePipToFullscreen();
             }
         }
     }
 
     interface Delegate {
         void showPictureInPictureMenu();
+
         void closePip();
+
         void enterPipMovementMenu();
+
         void togglePipExpansion();
+
+        void movePipToFullscreen();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 64017e1..d04c349 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -40,6 +40,8 @@
             Consts.TAG_WM_SHELL),
     WM_SHELL_PICTURE_IN_PICTURE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG,
             false, Consts.TAG_WM_SHELL),
+    WM_SHELL_SPLIT_SCREEN(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM_SHELL),
     TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
 
     private final boolean mEnabled;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 91f9d25..d543aa7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -54,6 +54,9 @@
 import static com.android.wm.shell.transition.Transitions.isClosingType;
 import static com.android.wm.shell.transition.Transitions.isOpeningType;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -68,6 +71,7 @@
 import android.graphics.Rect;
 import android.hardware.devicestate.DeviceStateManager;
 import android.os.Bundle;
+import android.os.Debug;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -147,7 +151,9 @@
 
     private final int mDisplayId;
     private SplitLayout mSplitLayout;
+    private ValueAnimator mDividerFadeInAnimator;
     private boolean mDividerVisible;
+    private boolean mKeyguardShowing;
     private final SyncTransactionQueue mSyncQueue;
     private final ShellTaskOrganizer mTaskOrganizer;
     private final Context mContext;
@@ -404,6 +410,7 @@
         mSplitLayout.init();
         // Set false to avoid record new bounds with old task still on top;
         mShouldUpdateRecents = false;
+        mIsDividerRemoteAnimating = true;
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         final WindowContainerTransaction evictWct = new WindowContainerTransaction();
         prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct);
@@ -417,7 +424,6 @@
                     RemoteAnimationTarget[] wallpapers,
                     RemoteAnimationTarget[] nonApps,
                     final IRemoteAnimationFinishedCallback finishedCallback) {
-                mIsDividerRemoteAnimating = true;
                 RemoteAnimationTarget[] augmentedNonApps =
                         new RemoteAnimationTarget[nonApps.length + 1];
                 for (int i = 0; i < nonApps.length; ++i) {
@@ -494,8 +500,10 @@
         }
         // Using legacy transitions, so we can't use blast sync since it conflicts.
         mTaskOrganizer.applyTransaction(wct);
-        mSyncQueue.runInSync(t ->
-                updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */));
+        mSyncQueue.runInSync(t -> {
+            setDividerVisibility(true, t);
+            updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
+        });
     }
 
     private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) {
@@ -510,10 +518,6 @@
                         ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
         } else {
             mSyncQueue.queue(evictWct);
-            mSyncQueue.runInSync(t -> {
-                setDividerVisibility(true, t);
-                updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
-            });
         }
     }
 
@@ -623,16 +627,12 @@
     }
 
     void onKeyguardVisibilityChanged(boolean showing) {
+        mKeyguardShowing = showing;
         if (!mMainStage.isActive()) {
             return;
         }
 
-        if (ENABLE_SHELL_TRANSITIONS) {
-            // Update divider visibility so it won't float on top of keyguard.
-            setDividerVisibility(!showing, null /* transaction */);
-        }
-
-        if (!showing && mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) {
+        if (!mKeyguardShowing && mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) {
             if (ENABLE_SHELL_TRANSITIONS) {
                 final WindowContainerTransaction wct = new WindowContainerTransaction();
                 prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct);
@@ -643,7 +643,10 @@
                         mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage,
                         EXIT_REASON_DEVICE_FOLDED);
             }
+            return;
         }
+
+        setDividerVisibility(!mKeyguardShowing, null);
     }
 
     void onFinishedWakingUp() {
@@ -727,6 +730,7 @@
             setResizingSplits(false /* resizing */);
             t.setWindowCrop(mMainStage.mRootLeash, null)
                     .setWindowCrop(mSideStage.mRootLeash, null);
+            setDividerVisibility(false, t);
         });
 
         // Hide divider and reset its position.
@@ -1055,8 +1059,31 @@
     }
 
     private void setDividerVisibility(boolean visible, @Nullable SurfaceControl.Transaction t) {
+        if (visible == mDividerVisible) {
+            return;
+        }
+
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+                "%s: Request to %s divider bar from %s.", TAG,
+                (visible ? "show" : "hide"), Debug.getCaller());
+
+        // Defer showing divider bar after keyguard dismissed, so it won't interfere with keyguard
+        // dismissing animation.
+        if (visible && mKeyguardShowing) {
+            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+                    "%s:   Defer showing divider bar due to keyguard showing.", TAG);
+            return;
+        }
+
         mDividerVisible = visible;
         sendSplitVisibilityChanged();
+
+        if (mIsDividerRemoteAnimating) {
+            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+                    "%s:   Skip animating divider bar due to it's remote animating.", TAG);
+            return;
+        }
+
         if (t != null) {
             applyDividerVisibility(t);
         } else {
@@ -1066,15 +1093,56 @@
 
     private void applyDividerVisibility(SurfaceControl.Transaction t) {
         final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
-        if (mIsDividerRemoteAnimating || dividerLeash == null) return;
+        if (dividerLeash == null) {
+            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+                    "%s:   Skip animating divider bar due to divider leash not ready.", TAG);
+            return;
+        }
+        if (mIsDividerRemoteAnimating) {
+            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+                    "%s:   Skip animating divider bar due to it's remote animating.", TAG);
+            return;
+        }
+
+        if (mDividerFadeInAnimator != null && mDividerFadeInAnimator.isRunning()) {
+            mDividerFadeInAnimator.cancel();
+        }
 
         if (mDividerVisible) {
-            t.show(dividerLeash);
-            t.setAlpha(dividerLeash, 1);
-            t.setLayer(dividerLeash, Integer.MAX_VALUE);
-            t.setPosition(dividerLeash,
-                    mSplitLayout.getRefDividerBounds().left,
-                    mSplitLayout.getRefDividerBounds().top);
+            final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
+            mDividerFadeInAnimator = ValueAnimator.ofFloat(0f, 1f);
+            mDividerFadeInAnimator.addUpdateListener(animation -> {
+                if (dividerLeash == null) {
+                    mDividerFadeInAnimator.cancel();
+                    return;
+                }
+                transaction.setAlpha(dividerLeash, (float) animation.getAnimatedValue());
+                transaction.apply();
+            });
+            mDividerFadeInAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    if (dividerLeash == null) {
+                        mDividerFadeInAnimator.cancel();
+                        return;
+                    }
+                    transaction.show(dividerLeash);
+                    transaction.setAlpha(dividerLeash, 0);
+                    transaction.setLayer(dividerLeash, Integer.MAX_VALUE);
+                    transaction.setPosition(dividerLeash,
+                                    mSplitLayout.getRefDividerBounds().left,
+                                    mSplitLayout.getRefDividerBounds().top);
+                    transaction.apply();
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mTransactionPool.release(transaction);
+                    mDividerFadeInAnimator = null;
+                }
+            });
+
+            mDividerFadeInAnimator.start();
         } else {
             t.hide(dividerLeash);
         }
@@ -1096,10 +1164,8 @@
             mSplitLayout.init();
             prepareEnterSplitScreen(wct);
             mSyncQueue.queue(wct);
-            mSyncQueue.runInSync(t -> {
-                updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
-                setDividerVisibility(true, t);
-            });
+            mSyncQueue.runInSync(t ->
+                    updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */));
         }
         if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
             mShouldUpdateRecents = true;
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
index cf4ea46..41cd31a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
@@ -37,7 +37,7 @@
         val displayBounds = WindowUtils.displayBounds
         val secondaryAppBounds = Region.from(0,
                 dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset,
-                displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarHeight)
+                displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarFrameHeight)
         return secondaryAppBounds
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index a510d69..e2da1a4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -171,7 +171,7 @@
             val bottomAppBounds = Region.from(0,
                 dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
                 displayBounds.right,
-                displayBounds.bottom - WindowUtils.navigationBarHeight)
+                displayBounds.bottom - WindowUtils.navigationBarFrameHeight)
             visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent())
                 .coversExactly(topAppBounds)
             visibleRegion(Components.ImeActivity.COMPONENT.toFlickerComponent())
@@ -192,7 +192,7 @@
             val bottomAppBounds = Region.from(0,
                 dividerBounds.bottom - WindowUtils.dockedStackDividerInset,
                 displayBounds.right,
-                displayBounds.bottom - WindowUtils.navigationBarHeight)
+                displayBounds.bottom - WindowUtils.navigationBarFrameHeight)
 
             visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent())
                 .coversExactly(topAppBounds)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 6cf8829..42b1014 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -209,12 +209,11 @@
         createNavigationInfo(animationTarget, null, null,
                 BackNavigationInfo.TYPE_RETURN_TO_HOME, null);
 
-        // Check that back start is dispatched.
         doMotionEvent(MotionEvent.ACTION_DOWN, 0);
-        verify(mIOnBackInvokedCallback).onBackStarted();
 
-        // Check that back progress is dispatched.
+        // Check that back start and progress is dispatched when first move.
         doMotionEvent(MotionEvent.ACTION_MOVE, 100);
+        verify(mIOnBackInvokedCallback).onBackStarted();
         ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class);
         verify(mIOnBackInvokedCallback).onBackProgressed(backEventCaptor.capture());
         assertEquals(animationTarget, backEventCaptor.getValue().getDepartingAnimationTarget());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index a55f737..ffaab65 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -139,6 +139,7 @@
     }
 
     @Test
+    @UiThreadTest
     public void testLaunchToSide() {
         ActivityManager.RunningTaskInfo newTask = new TestRunningTaskInfoBuilder()
                 .setParentTaskId(mSideStage.mRootTaskInfo.taskId).build();
@@ -173,6 +174,7 @@
     }
 
     @Test
+    @UiThreadTest
     public void testLaunchPair() {
         TransitionInfo info = createEnterPairInfo();
 
@@ -195,6 +197,7 @@
     }
 
     @Test
+    @UiThreadTest
     public void testMonitorInSplit() {
         enterSplit();
 
@@ -251,6 +254,7 @@
     }
 
     @Test
+    @UiThreadTest
     public void testEnterRecents() {
         enterSplit();
 
@@ -288,6 +292,7 @@
     }
 
     @Test
+    @UiThreadTest
     public void testDismissFromBeingOccluded() {
         enterSplit();
 
@@ -325,6 +330,7 @@
     }
 
     @Test
+    @UiThreadTest
     public void testDismissFromMultiWindowSupport() {
         enterSplit();
 
@@ -346,6 +352,7 @@
     }
 
     @Test
+    @UiThreadTest
     public void testDismissSnap() {
         enterSplit();
 
@@ -370,6 +377,7 @@
     }
 
     @Test
+    @UiThreadTest
     public void testDismissFromAppFinish() {
         enterSplit();
 
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index ef0270b..e5673a6 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -1728,7 +1728,9 @@
         }
         int res = nativeSetMaxNumberOfFrontends(frontendType, maxNumber);
         if (res == RESULT_SUCCESS) {
-            // TODO: b/211778848 Update Tuner Resource Manager.
+            if (!mTunerResourceManager.setMaxNumberOfFrontends(frontendType, maxNumber)) {
+                res = RESULT_INVALID_ARGUMENT;
+            }
         }
         return res;
     }
@@ -1749,7 +1751,13 @@
                     TunerVersionChecker.TUNER_VERSION_2_0, "Set maximum Frontends")) {
             return -1;
         }
-        return nativeGetMaxNumberOfFrontends(frontendType);
+        int maxNumFromHAL = nativeGetMaxNumberOfFrontends(frontendType);
+        int maxNumFromTRM = mTunerResourceManager.getMaxNumberOfFrontends(frontendType);
+        if (maxNumFromHAL != maxNumFromTRM) {
+            Log.w(TAG, "max num of usable frontend is out-of-sync b/w " + maxNumFromHAL
+                    + " != " + maxNumFromTRM);
+        }
+        return maxNumFromHAL;
     }
 
     /** @hide */
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
index 5ada89e..15175a7 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -401,6 +401,43 @@
     }
 
     /**
+     * Sets the maximum usable frontends number of a given frontend type. It is used to enable or
+     * disable frontends when cable connection status is changed by user.
+     *
+     * @param frontendType the frontendType which the maximum usable number will be set for.
+     * @param maxNum the new maximum usable number.
+     *
+     * @return true if  successful and false otherwise.
+     */
+    public boolean setMaxNumberOfFrontends(int frontendType, int maxNum) {
+        boolean result = false;
+        try {
+            result = mService.setMaxNumberOfFrontends(frontendType, maxNum);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return result;
+    }
+
+    /**
+     * Get the maximum usable frontends number of a given frontend type.
+     *
+     * @param frontendType the frontendType which the maximum usable number will be queried for.
+     *
+     * @return the maximum usable number of the queried frontend type. Returns -1 when the
+     *         frontendType is invalid
+     */
+    public int getMaxNumberOfFrontends(int frontendType) {
+        int result = -1;
+        try {
+            result = mService.getMaxNumberOfFrontends(frontendType);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return result;
+    }
+
+    /**
      * Requests from the client to share frontend with an existing client.
      *
      * <p><strong>Note:</strong> {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
index d16fc6c..144b98c 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
@@ -166,6 +166,27 @@
     boolean requestFrontend(in TunerFrontendRequest request, out int[] frontendHandle);
 
     /*
+     * Sets the maximum usable frontends number of a given frontend type. It is used to enable or
+     * disable frontends when cable connection status is changed by user.
+     *
+     * @param frontendType the frontendType which the maximum usable number will be set for.
+     * @param maxNumber the new maximum usable number.
+     *
+     * @return true if  successful and false otherwise.
+     */
+    boolean setMaxNumberOfFrontends(in int frontendType, in int maxNum);
+
+    /*
+     * Get the maximum usable frontends number of a given frontend type.
+     *
+     * @param frontendType the frontendType which the maximum usable number will be queried for.
+     *
+     * @return the maximum usable number of the queried frontend type. Returns -1 when the
+     *         frontendType is invalid
+     */
+    int getMaxNumberOfFrontends(in int frontendType);
+
+    /*
      * Requests to share frontend with an existing client.
      *
      * <p><strong>Note:</strong> {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called
diff --git a/packages/CompanionDeviceManager/AndroidManifest.xml b/packages/CompanionDeviceManager/AndroidManifest.xml
index 8b5d214..16cd2e5 100644
--- a/packages/CompanionDeviceManager/AndroidManifest.xml
+++ b/packages/CompanionDeviceManager/AndroidManifest.xml
@@ -46,6 +46,7 @@
             android:launchMode="singleInstance"
             android:excludeFromRecents="true"
             android:permission="android.permission.BIND_COMPANION_DEVICE_MANAGER_SERVICE"
+            android:configChanges="orientation|screenSize"
             android:theme="@style/ChooserActivity"/>
 
         <service
diff --git a/packages/CompanionDeviceManager/res/drawable-night/ic_apps.xml b/packages/CompanionDeviceManager/res/drawable-night/ic_apps.xml
new file mode 100644
index 0000000..8d7fa26
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable-night/ic_apps.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="@android:color/system_accent1_200">
+    <path
+        android:pathData="M6.2529,18.5H16.2529V17.5H18.2529V21.5C18.2529,22.6 17.3529,23.5 16.2529,23.5H6.2529C5.1529,23.5 4.2529,22.6 4.2529,21.5V3.5C4.2529,2.4 5.1529,1.51 6.2529,1.51L16.2529,1.5C17.3529,1.5 18.2529,2.4 18.2529,3.5V7.5H16.2529V6.5H6.2529V18.5ZM16.2529,3.5H6.2529V4.5H16.2529V3.5ZM6.2529,21.5V20.5H16.2529V21.5H6.2529ZM12.6553,9.4049C12.6553,8.8526 13.103,8.4049 13.6553,8.4049H20.5254C21.0776,8.4049 21.5254,8.8526 21.5254,9.4049V14.6055C21.5254,15.1578 21.0776,15.6055 20.5254,15.6055H14.355L12.6553,17.0871V9.4049Z"
+        android:fillColor="#3C4043"
+        android:fillType="evenOdd"/>
+</vector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable-night/ic_info.xml b/packages/CompanionDeviceManager/res/drawable-night/ic_info.xml
new file mode 100644
index 0000000..5689e34
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable-night/ic_info.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="@android:color/system_accent1_200">
+    <path android:fillColor="@android:color/white"
+          android:pathData="M11,17H13V11H11ZM12,9Q12.425,9 12.713,8.712Q13,8.425 13,8Q13,7.575 12.713,7.287Q12.425,7 12,7Q11.575,7 11.288,7.287Q11,7.575 11,8Q11,8.425 11.288,8.712Q11.575,9 12,9ZM12,22Q9.925,22 8.1,21.212Q6.275,20.425 4.925,19.075Q3.575,17.725 2.788,15.9Q2,14.075 2,12Q2,9.925 2.788,8.1Q3.575,6.275 4.925,4.925Q6.275,3.575 8.1,2.787Q9.925,2 12,2Q14.075,2 15.9,2.787Q17.725,3.575 19.075,4.925Q20.425,6.275 21.212,8.1Q22,9.925 22,12Q22,14.075 21.212,15.9Q20.425,17.725 19.075,19.075Q17.725,20.425 15.9,21.212Q14.075,22 12,22ZM12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12Q12,12 12,12ZM12,20Q15.325,20 17.663,17.663Q20,15.325 20,12Q20,8.675 17.663,6.337Q15.325,4 12,4Q8.675,4 6.338,6.337Q4,8.675 4,12Q4,15.325 6.338,17.663Q8.675,20 12,20Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable-night/ic_notifications.xml b/packages/CompanionDeviceManager/res/drawable-night/ic_notifications.xml
new file mode 100644
index 0000000..06bfad5
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable-night/ic_notifications.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="@android:color/system_accent1_200">
+    <path android:fillColor="@android:color/white"
+          android:pathData="M4,19V17H6V10Q6,7.925 7.25,6.312Q8.5,4.7 10.5,4.2V3.5Q10.5,2.875 10.938,2.438Q11.375,2 12,2Q12.625,2 13.062,2.438Q13.5,2.875 13.5,3.5V4.2Q15.5,4.7 16.75,6.312Q18,7.925 18,10V17H20V19ZM12,11.5Q12,11.5 12,11.5Q12,11.5 12,11.5Q12,11.5 12,11.5Q12,11.5 12,11.5ZM12,22Q11.175,22 10.588,21.413Q10,20.825 10,20H14Q14,20.825 13.413,21.413Q12.825,22 12,22ZM8,17H16V10Q16,8.35 14.825,7.175Q13.65,6 12,6Q10.35,6 9.175,7.175Q8,8.35 8,10Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable-night/ic_storage.xml b/packages/CompanionDeviceManager/res/drawable-night/ic_storage.xml
new file mode 100644
index 0000000..f8aef33
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable-night/ic_storage.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="@android:color/system_accent1_200">
+    <path android:fillColor="@android:color/white"
+          android:pathData="M6,17H18L14.25,12L11.25,16L9,13ZM5,21Q4.175,21 3.587,20.413Q3,19.825 3,19V5Q3,4.175 3.587,3.587Q4.175,3 5,3H19Q19.825,3 20.413,3.587Q21,4.175 21,5V19Q21,19.825 20.413,20.413Q19.825,21 19,21ZM5,19H19Q19,19 19,19Q19,19 19,19V5Q19,5 19,5Q19,5 19,5H5Q5,5 5,5Q5,5 5,5V19Q5,19 5,19Q5,19 5,19ZM5,5Q5,5 5,5Q5,5 5,5V19Q5,19 5,19Q5,19 5,19Q5,19 5,19Q5,19 5,19V5Q5,5 5,5Q5,5 5,5Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/helper_back_button.xml b/packages/CompanionDeviceManager/res/drawable/helper_back_button.xml
index 8e92051f..6ce1f12 100644
--- a/packages/CompanionDeviceManager/res/drawable/helper_back_button.xml
+++ b/packages/CompanionDeviceManager/res/drawable/helper_back_button.xml
@@ -18,6 +18,6 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
     <solid android:color="@android:color/system_accent1_100"/>
-    <corners android:topLeftRadius="16dp" android:topRightRadius="16dp"
-             android:bottomLeftRadius="16dp" android:bottomRightRadius="16dp"/>
+    <corners android:topLeftRadius="20dp" android:topRightRadius="20dp"
+             android:bottomLeftRadius="20dp" android:bottomRightRadius="20dp"/>
 </shape>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_apps.xml b/packages/CompanionDeviceManager/res/drawable/ic_apps.xml
index d1ec863..7295e78 100644
--- a/packages/CompanionDeviceManager/res/drawable/ic_apps.xml
+++ b/packages/CompanionDeviceManager/res/drawable/ic_apps.xml
@@ -19,7 +19,8 @@
         android:width="24dp"
         android:height="24dp"
         android:viewportWidth="24"
-        android:viewportHeight="24">
+        android:viewportHeight="24"
+        android:tint="@android:color/system_accent1_600">
     <path
         android:pathData="M6.2529,18.5H16.2529V17.5H18.2529V21.5C18.2529,22.6 17.3529,23.5 16.2529,23.5H6.2529C5.1529,23.5 4.2529,22.6 4.2529,21.5V3.5C4.2529,2.4 5.1529,1.51 6.2529,1.51L16.2529,1.5C17.3529,1.5 18.2529,2.4 18.2529,3.5V7.5H16.2529V6.5H6.2529V18.5ZM16.2529,3.5H6.2529V4.5H16.2529V3.5ZM6.2529,21.5V20.5H16.2529V21.5H6.2529ZM12.6553,9.4049C12.6553,8.8526 13.103,8.4049 13.6553,8.4049H20.5254C21.0776,8.4049 21.5254,8.8526 21.5254,9.4049V14.6055C21.5254,15.1578 21.0776,15.6055 20.5254,15.6055H14.355L12.6553,17.0871V9.4049Z"
         android:fillColor="#3C4043"
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml b/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml
index e5825bc..7b1ef85 100644
--- a/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml
+++ b/packages/CompanionDeviceManager/res/drawable/ic_notifications.xml
@@ -20,7 +20,7 @@
         android:height="24dp"
         android:viewportWidth="24"
         android:viewportHeight="24"
-        android:tint="?attr/colorControlNormal">
+        android:tint="@android:color/system_accent1_600">
     <path android:fillColor="@android:color/white"
           android:pathData="M4,19V17H6V10Q6,7.925 7.25,6.312Q8.5,4.7 10.5,4.2V3.5Q10.5,2.875 10.938,2.438Q11.375,2 12,2Q12.625,2 13.062,2.438Q13.5,2.875 13.5,3.5V4.2Q15.5,4.7 16.75,6.312Q18,7.925 18,10V17H20V19ZM12,11.5Q12,11.5 12,11.5Q12,11.5 12,11.5Q12,11.5 12,11.5Q12,11.5 12,11.5ZM12,22Q11.175,22 10.588,21.413Q10,20.825 10,20H14Q14,20.825 13.413,21.413Q12.825,22 12,22ZM8,17H16V10Q16,8.35 14.825,7.175Q13.65,6 12,6Q10.35,6 9.175,7.175Q8,8.35 8,10Z"/>
 </vector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/ic_storage.xml b/packages/CompanionDeviceManager/res/drawable/ic_storage.xml
index 406a3b5..3e033d3 100644
--- a/packages/CompanionDeviceManager/res/drawable/ic_storage.xml
+++ b/packages/CompanionDeviceManager/res/drawable/ic_storage.xml
@@ -20,7 +20,7 @@
         android:height="24dp"
         android:viewportWidth="24"
         android:viewportHeight="24"
-        android:tint="?attr/colorControlNormal">
+        android:tint="@android:color/system_accent1_600">
     <path android:fillColor="@android:color/white"
           android:pathData="M6,17H18L14.25,12L11.25,16L9,13ZM5,21Q4.175,21 3.587,20.413Q3,19.825 3,19V5Q3,4.175 3.587,3.587Q4.175,3 5,3H19Q19.825,3 20.413,3.587Q21,4.175 21,5V19Q21,19.825 20.413,20.413Q19.825,21 19,21ZM5,19H19Q19,19 19,19Q19,19 19,19V5Q19,5 19,5Q19,5 19,5H5Q5,5 5,5Q5,5 5,5V19Q5,19 5,19Q5,19 5,19ZM5,5Q5,5 5,5Q5,5 5,5V19Q5,19 5,19Q5,19 5,19Q5,19 5,19Q5,19 5,19V5Q5,5 5,5Q5,5 5,5Z"/>
 </vector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
index c0e6c09..520ade8 100644
--- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
@@ -12,135 +12,142 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/activity_confirmation"
+<ScrollView
+        xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:minWidth="340dp">
+        android:layout_height="match_parent"
+        style="@style/ScrollViewStyle">
 
-    <LinearLayout android:id="@+id/association_confirmation"
-                  style="@style/ContainerLayout">
-
-        <!-- A header for selfManaged devices only. -->
-        <include layout="@layout/vendor_header" />
-
-        <!-- Do NOT change the ID of the root LinearLayout above: it's referenced in CTS tests. -->
-
-        <ImageView
-            android:id="@+id/profile_icon"
+    <LinearLayout
+            android:id="@+id/activity_confirmation"
             android:layout_width="match_parent"
-            android:layout_height="32dp"
-            android:gravity="center"
-            android:layout_marginTop="18dp"
-            android:tint="@android:color/system_accent1_600"/>
+            android:layout_height="wrap_content"
+            android:baselineAligned="false">
 
-        <LinearLayout style="@style/Description">
-            <TextView
-                android:id="@+id/title"
-                style="@style/DescriptionTitle" />
+        <LinearLayout android:id="@+id/association_confirmation"
+                      style="@style/ContainerLayout">
 
-            <TextView
-                android:id="@+id/summary"
-                style="@style/DescriptionSummary" />
+            <!-- A header for selfManaged devices only. -->
+            <include layout="@layout/vendor_header" />
+
+            <!-- Do NOT change the ID of the root LinearLayout above:
+            it's referenced in CTS tests. -->
+
+            <ImageView
+                android:id="@+id/profile_icon"
+                android:layout_width="match_parent"
+                android:layout_height="32dp"
+                android:gravity="center"
+                android:layout_marginTop="18dp"
+                android:tint="@android:color/system_accent1_600"/>
+
+            <LinearLayout style="@style/Description">
+                <TextView
+                    android:id="@+id/title"
+                    style="@style/DescriptionTitle" />
+
+                <TextView
+                    android:id="@+id/summary"
+                    style="@style/DescriptionSummary" />
+
+            </LinearLayout>
+
+            <RelativeLayout
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1">
+
+                <LinearLayout
+                    android:id="@+id/multiple_device_list"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="12dp"
+                    android:layout_marginBottom="12dp"
+                    android:orientation="vertical"
+                    android:visibility="gone">
+
+                    <View
+                        android:id="@+id/border_top"
+                        style="@style/DeviceListBorder" />
+
+                    <androidx.recyclerview.widget.RecyclerView
+                        android:id="@+id/device_list"
+                        android:layout_width="match_parent"
+                        android:scrollbars="vertical"
+                        android:layout_marginBottom="12dp"
+                        android:layout_height="200dp" />
+
+                    <View
+                        android:id="@+id/border_bottom"
+                        style="@style/DeviceListBorder" />
+
+                </LinearLayout>
+
+                <androidx.recyclerview.widget.RecyclerView
+                    android:id="@+id/permission_list"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+
+                <ProgressBar
+                    android:id="@+id/spinner_multiple_device"
+                    android:visibility="gone"
+                    style="@style/Spinner"  />
+
+            </RelativeLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center"
+                android:orientation="vertical"
+                android:layout_marginTop="16dp">
+
+                <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. -->
+
+                <Button
+                    android:id="@+id/btn_positive"
+                    style="@style/PositiveButton"
+                    android:text="@string/consent_yes" />
+
+                <Button
+                    android:id="@+id/btn_negative"
+                    android:layout_marginBottom="12dp"
+                    style="@style/NegativeButton"
+                    android:text="@string/consent_no" />
+
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="bottom|end"
+                android:orientation="vertical"
+                android:layout_marginEnd="16dp"
+                android:layout_marginBottom="16dp">
+
+                <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. -->
+
+                <Button
+                    android:id="@+id/btn_negative_multiple_devices"
+                    style="@style/NegativeButtonMultipleDevices"
+                    android:textColor="?android:textColorPrimary"
+                    android:visibility="gone"
+                    android:text="@string/consent_no" />
+            </LinearLayout>
 
         </LinearLayout>
 
         <RelativeLayout
             android:layout_width="match_parent"
-            android:layout_height="0dp"
+            android:layout_height="match_parent"
             android:layout_weight="1">
 
-            <LinearLayout
-                android:id="@+id/multiple_device_list"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="12dp"
-                android:layout_marginBottom="12dp"
-                android:orientation="vertical"
-                android:visibility="gone">
-
-                <View
-                    android:id="@+id/border_top"
-                    style="@style/DeviceListBorder" />
-
-                <androidx.recyclerview.widget.RecyclerView
-                    android:id="@+id/device_list"
-                    android:layout_width="match_parent"
-                    android:scrollbars="vertical"
-                    android:layout_marginBottom="12dp"
-                    android:layout_height="200dp" />
-
-                <View
-                    android:id="@+id/border_bottom"
-                    style="@style/DeviceListBorder" />
-
-            </LinearLayout>
-
-            <androidx.recyclerview.widget.RecyclerView
-                android:id="@+id/permission_list"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content" />
-
             <ProgressBar
-                android:id="@+id/spinner_multiple_device"
+                android:id="@+id/spinner_single_device"
                 android:visibility="gone"
-                style="@style/Spinner"  />
-
-        </RelativeLayout>
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:gravity="center"
-            android:orientation="vertical"
-            android:layout_marginTop="16dp">
-
-            <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. -->
-
-            <Button
-                android:id="@+id/btn_positive"
-                style="@style/PositiveButton"
-                android:text="@string/consent_yes" />
-
-            <Button
-                android:id="@+id/btn_negative"
-                android:layout_marginBottom="12dp"
-                style="@style/NegativeButton"
-                android:text="@string/consent_no" />
-
-        </LinearLayout>
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:gravity="bottom|right"
-            android:orientation="vertical"
-            android:layout_marginRight="16dp"
-            android:layout_marginBottom="16dp">
-
-            <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. -->
-
-            <Button
-                android:id="@+id/btn_negative_multiple_devices"
-                style="@style/NegativeButtonMultipleDevices"
-                android:textColor="?android:textColorPrimary"
-                android:visibility="gone"
-                android:text="@string/consent_no" />
-        </LinearLayout>
+                style="@style/Spinner" />
+        </RelativeLayout>>
 
     </LinearLayout>
 
-    <RelativeLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_weight="1">
-
-        <ProgressBar
-            android:id="@+id/spinner_single_device"
-            android:visibility="gone"
-            style="@style/Spinner" />
-    </RelativeLayout>>
-
-</LinearLayout>
\ No newline at end of file
+</ScrollView>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml b/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml
index a9ace44..d0d46f7 100644
--- a/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/helper_confirmation.xml
@@ -15,54 +15,67 @@
   ~ limitations under the License.
   -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:id="@+id/helper_confirmation"
-              android:theme="@style/ChooserActivity"
-              android:padding="12dp"
-              style="@style/ContainerLayout">
-
-    <ImageView
-        android:id="@+id/app_icon"
-        android:layout_width="match_parent"
-        android:layout_height="32dp"
-        android:gravity="center"
-        android:layout_marginTop="12dp"
-        android:layout_marginBottom="12dp"/>
-
-    <TextView
-        android:id="@+id/helper_title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center"
-        android:paddingHorizontal="12dp"
-        android:textColor="?android:attr/textColorPrimary"
-        android:textSize="22sp" />
-
-    <TextView
-        android:id="@+id/helper_summary"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginLeft="24dp"
-        android:layout_marginRight="24dp"
-        android:layout_marginTop="12dp"
-        android:layout_marginBottom="24dp"
-        android:gravity="center"
-        android:textColor="?android:attr/textColorSecondary"
-        android:textSize="14sp" />
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    style="@style/ScrollViewStyle">
 
     <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        android:layout_marginRight="12dp"
-        android:layout_marginBottom="12dp"
-        android:gravity="end">
+        android:layout_height="wrap_content">
 
-        <Button
-            android:id="@+id/btn_back"
-            style="@style/VendorHelperBackButton"
-            android:text="@string/consent_back" />
+        <LinearLayout
+            android:id="@+id/helper_confirmation"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:theme="@style/ChooserActivity"
+            android:padding="12dp"
+            style="@style/ContainerLayout">
+
+            <ImageView
+                android:id="@+id/app_icon"
+                android:layout_width="match_parent"
+                android:layout_height="48dp"
+                android:gravity="center"
+                android:layout_marginTop="12dp"
+                android:layout_marginBottom="12dp"/>
+
+            <TextView
+                android:id="@+id/helper_title"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center"
+                android:textColor="?android:attr/textColorPrimary"
+                android:textSize="22sp" />
+
+            <TextView
+                android:id="@+id/helper_summary"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="24dp"
+                android:layout_marginEnd="24dp"
+                android:layout_marginTop="12dp"
+                android:layout_marginBottom="32dp"
+                android:gravity="center"
+                android:textColor="?android:attr/textColorSecondary"
+                android:textSize="14sp" />
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:layout_marginEnd="12dp"
+                android:gravity="end">
+
+                <Button
+                    android:id="@+id/btn_back"
+                    style="@style/VendorHelperBackButton"
+                    android:text="@string/consent_back" />
+
+            </LinearLayout>
+
+        </LinearLayout>
 
     </LinearLayout>
 
-</LinearLayout>
\ No newline at end of file
+</ScrollView>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/layout/list_item_device.xml b/packages/CompanionDeviceManager/res/layout/list_item_device.xml
index eeb988f..0a5afe4 100644
--- a/packages/CompanionDeviceManager/res/layout/list_item_device.xml
+++ b/packages/CompanionDeviceManager/res/layout/list_item_device.xml
@@ -28,14 +28,15 @@
         android:id="@android:id/icon"
         android:layout_width="24dp"
         android:layout_height="24dp"
-        android:layout_marginLeft="24dp"
-        android:layout_marginRight="12dp"
+        android:layout_marginStart="24dp"
         android:tint="@android:color/system_accent1_600"/>
 
     <TextView
         android:id="@android:id/text1"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:paddingStart="24dp"
+        android:paddingEnd="24dp"
         android:singleLine="true"
         android:textAppearance="?android:attr/textAppearanceListItemSmall"/>
 
diff --git a/packages/CompanionDeviceManager/res/layout/list_item_permission.xml b/packages/CompanionDeviceManager/res/layout/list_item_permission.xml
index 3dce38d..54916a2 100644
--- a/packages/CompanionDeviceManager/res/layout/list_item_permission.xml
+++ b/packages/CompanionDeviceManager/res/layout/list_item_permission.xml
@@ -20,8 +20,8 @@
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:orientation="horizontal"
-              android:paddingLeft="32dp"
-              android:paddingRight="32dp"
+              android:paddingStart="32dp"
+              android:paddingEnd="32dp"
               android:paddingBottom="14dp">
 
     <ImageView
@@ -30,7 +30,6 @@
         android:layout_height="24dp"
         android:layout_marginTop="8dp"
         android:layout_marginEnd="12dp"
-        android:tint="@android:color/system_accent1_600"
         android:contentDescription="Permission Icon"/>
 
     <LinearLayout
@@ -51,7 +50,6 @@
             android:id="@+id/permission_summary"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:paddingRight="24dp"
             android:textSize="14sp"
             android:textColor="?android:attr/textColorSecondary"/>
 
diff --git a/packages/CompanionDeviceManager/res/layout/vendor_header.xml b/packages/CompanionDeviceManager/res/layout/vendor_header.xml
index c35f59e..14e7431 100644
--- a/packages/CompanionDeviceManager/res/layout/vendor_header.xml
+++ b/packages/CompanionDeviceManager/res/layout/vendor_header.xml
@@ -24,28 +24,32 @@
     android:layout_gravity="center"
     android:paddingTop="24dp"
     android:paddingBottom="4dp"
-    android:paddingLeft="24dp"
-    android:paddingRight="24dp"
+    android:paddingStart="24dp"
+    android:paddingEnd="24dp"
     android:visibility="gone" >
 
     <ImageView
         android:id="@+id/vendor_header_image"
-        android:layout_width="31dp"
-        android:layout_height="32dp" />
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:contentDescription="@string/vendor_icon_description" />
 
     <TextView
         android:id="@+id/vendor_header_name"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginLeft="20dp"
-        android:layout_marginTop="5dp"
-        android:layout_toRightOf="@+id/header_image" />
+        android:layout_marginStart="12dp"
+        android:layout_marginTop="12dp"
+        android:textSize="16sp"
+        android:layout_toEndOf="@+id/vendor_header_image"
+        android:textColor="?android:attr/textColorSecondary"/>
 
     <ImageButton
         android:id="@+id/vendor_header_button"
         android:background="@drawable/ic_info"
-        android:layout_width="31dp"
-        android:layout_height="32dp"
-        android:layout_alignParentRight="true" />
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:contentDescription="@string/vendor_header_button_description"
+        android:layout_alignParentEnd="true" />
 
 </RelativeLayout>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 586a022..3d6bf15 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -116,4 +116,10 @@
         apps installed on your watch during setup will use the same permissions as your phone.\n\n
         These permissions may include access to your watch\u2019s microphone and location.</string>
 
+    <!--Description for vendor icon [CHAR LIMIT=30]-->
+    <string name="vendor_icon_description">App Icon</string>
+
+    <!--Description for information icon [CHAR LIMIT=30]-->
+    <string name="vendor_header_button_description">More Information Button</string>
+
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml
index c38323f..428f2dc 100644
--- a/packages/CompanionDeviceManager/res/values/styles.xml
+++ b/packages/CompanionDeviceManager/res/values/styles.xml
@@ -59,9 +59,8 @@
 
     <style name="VendorHelperBackButton"
            parent="@android:style/Widget.Material.Button.Borderless.Colored">
-        <item name="android:layout_width">60dp</item>
-        <item name="android:layout_height">36dp</item>
-        <item name="android:layout_marginTop">20dp</item>
+        <item name="android:layout_width">70dp</item>
+        <item name="android:layout_height">48dp</item>
         <item name="android:textAllCaps">false</item>
         <item name="android:textColor">@android:color/system_neutral1_900</item>
         <item name="android:background">@drawable/helper_back_button</item>
@@ -71,8 +70,6 @@
            parent="@android:style/Widget.Material.Button.Borderless.Colored">
         <item name="android:layout_width">300dp</item>
         <item name="android:layout_height">56dp</item>
-        <item name="android:layout_marginLeft">24dp</item>
-        <item name="android:layout_marginRight">24dp</item>
         <item name="android:layout_marginBottom">2dp</item>
         <item name="android:textAllCaps">false</item>
         <item name="android:textSize">14sp</item>
@@ -84,8 +81,6 @@
            parent="@android:style/Widget.Material.Button.Borderless.Colored">
         <item name="android:layout_width">300dp</item>
         <item name="android:layout_height">56dp</item>
-        <item name="android:layout_marginLeft">24dp</item>
-        <item name="android:layout_marginRight">24dp</item>
         <item name="android:layout_marginTop">2dp</item>
         <item name="android:textAllCaps">false</item>
         <item name="android:textSize">14sp</item>
@@ -115,4 +110,10 @@
         <item name="android:indeterminate">true</item>
         <item name="android:layout_centerInParent">true</item>
     </style>
+
+    <style name="ScrollViewStyle">
+        <item name="android:scrollbars">none</item>
+        <item name="android:fillViewport">true</item>
+        <item name="android:clipChildren">false</item>
+    </style>
 </resources>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/values/themes.xml b/packages/CompanionDeviceManager/res/values/themes.xml
index e3fc67c..1ea3968 100644
--- a/packages/CompanionDeviceManager/res/values/themes.xml
+++ b/packages/CompanionDeviceManager/res/values/themes.xml
@@ -18,8 +18,8 @@
 
     <style name="ChooserActivity"
            parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar">
-        <item name="*android:windowFixedHeightMajor">100%</item>
-        <item name="*android:windowFixedHeightMinor">100%</item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:windowNoTitle">true</item>
         <item name="android:windowBackground">@android:color/transparent</item>
     </style>
 
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 37cbf30..9e9ec04 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -51,6 +51,7 @@
 import android.companion.IAssociationRequestCallback;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.res.Configuration;
 import android.graphics.drawable.Drawable;
 import android.net.MacAddress;
 import android.os.Bundle;
@@ -411,17 +412,19 @@
         final Drawable vendorIcon;
         final CharSequence vendorName;
         final Spanned title;
+        int nightModeFlags = getResources().getConfiguration().uiMode
+                & Configuration.UI_MODE_NIGHT_MASK;
 
         mPermissionTypes = new ArrayList<>();
 
         try {
             vendorIcon = getVendorHeaderIcon(this, packageName, userId);
             vendorName = getVendorHeaderName(this, packageName, userId);
-
             mVendorHeaderImage.setImageDrawable(vendorIcon);
             if (hasVendorIcon(this, packageName, userId)) {
-                mVendorHeaderImage.setColorFilter(getResources().getColor(
-                                android.R.color.system_accent1_600, /* Theme= */null));
+                int color = nightModeFlags == Configuration.UI_MODE_NIGHT_YES
+                        ? android.R.color.system_accent1_200 : android.R.color.system_accent1_600;
+                mVendorHeaderImage.setColorFilter(getResources().getColor(color, /* Theme= */null));
             }
         } catch (PackageManager.NameNotFoundException e) {
             Log.e(TAG, "Package u" + userId + "/" + packageName + " not found.");
@@ -484,6 +487,14 @@
         if (deviceFilterPairs.isEmpty()) return;
 
         mSelectedDevice = requireNonNull(deviceFilterPairs.get(0));
+        // No need to show user consent dialog if it is a singleDevice
+        // and isSkipPrompt(true) AssociationRequest.
+        // See AssociationRequestsProcessor#mayAssociateWithoutPrompt.
+        if (mRequest.isSkipPrompt()) {
+            mSingleDeviceSpinner.setVisibility(View.GONE);
+            onUserSelectedDevice(mSelectedDevice);
+            return;
+        }
 
         final String deviceName = mSelectedDevice.getDisplayName();
         final Spanned title = getHtmlFromResources(
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml
index 25f0771..72b569f 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml
@@ -25,7 +25,7 @@
         android:fitsSystemWindows="true"
         android:outlineAmbientShadowColor="@android:color/transparent"
         android:outlineSpotShadowColor="@android:color/transparent"
-        android:background="?android:attr/colorPrimary"
+        android:background="@android:color/transparent"
         android:theme="@style/Theme.CollapsingToolbar.Settings">
 
         <com.google.android.material.appbar.CollapsingToolbarLayout
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
index 72383fe..dbb4b50 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
@@ -20,6 +20,7 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.os.Build;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -29,6 +30,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.coordinatorlayout.widget.CoordinatorLayout;
 
@@ -41,6 +43,7 @@
  * This widget is wrapping the collapsing toolbar and can be directly used by the
  * {@link AppCompatActivity}.
  */
+@RequiresApi(Build.VERSION_CODES.S)
 public class CollapsingCoordinatorLayout extends CoordinatorLayout {
     private static final String TAG = "CollapsingCoordinatorLayout";
     private static final float TOOLBAR_LINE_SPACING_MULTIPLIER = 1.1f;
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index ac30636..6766cdd 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -40,6 +40,8 @@
     static final int ORDER_FOOTER = Integer.MAX_VALUE - 1;
     @VisibleForTesting
     View.OnClickListener mLearnMoreListener;
+    @VisibleForTesting
+    int mIconVisibility = View.VISIBLE;
     private CharSequence mContentDescription;
     private CharSequence mLearnMoreText;
     private CharSequence mLearnMoreContentDescription;
@@ -84,6 +86,9 @@
         } else {
             learnMore.setVisibility(View.GONE);
         }
+
+        View icon = holder.itemView.findViewById(R.id.icon_frame);
+        icon.setVisibility(mIconVisibility);
     }
 
     @Override
@@ -165,6 +170,17 @@
         }
     }
 
+    /**
+     * Set visibility of footer icon.
+     */
+    public void setIconVisibility(int iconVisibility) {
+        if (mIconVisibility == iconVisibility) {
+            return;
+        }
+        mIconVisibility = iconVisibility;
+        notifyChanged();
+    }
+
     private void init() {
         setLayoutResource(R.layout.preference_footer);
         if (getIcon() == null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 284da73..2f30baa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -234,5 +234,10 @@
         if (!(mPreference instanceof RestrictedTopLevelPreference)) {
             mPreference.setEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
         }
+
+        if (mPreference instanceof PrimarySwitchPreference) {
+            ((PrimarySwitchPreference) mPreference)
+                    .setSwitchEnabled(!(mDisabledByAdmin || mDisabledByAppOps));
+        }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index fdb0607..6b9daa3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -863,6 +863,30 @@
             }
         }
 
+        /**
+         *  Activate session to enable a class that implements Callbacks to receive the callback.
+         */
+        public void activateSession() {
+            synchronized (mEntriesMap) {
+                if (!mResumed) {
+                    mResumed = true;
+                    mSessionsChanged = true;
+                }
+            }
+        }
+
+        /**
+         *  Deactivate session to disable a class that implements Callbacks to get the callback.
+         */
+        public void deactivateSession() {
+            synchronized (mEntriesMap) {
+                if (mResumed) {
+                    mResumed = false;
+                    mSessionsChanged = true;
+                }
+            }
+        }
+
         public ArrayList<AppEntry> getAllApps() {
             synchronized (mEntriesMap) {
                 return new ArrayList<>(mAppEntries);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 4ee2122..6919cf2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1376,7 +1376,7 @@
     /**
      * Store the member devices that are in the same coordinated set.
      */
-    public void setMemberDevice(CachedBluetoothDevice memberDevice) {
+    public void addMemberDevice(CachedBluetoothDevice memberDevice) {
         mMemberDevices.add(memberDevice);
     }
 
@@ -1393,24 +1393,24 @@
      * device and member devices.
      *
      * @param prevMainDevice the previous Main device, it will be added into the member device set.
-     * @param newMainDevie the new Main device, it will be removed from the member device set.
+     * @param newMainDevice the new Main device, it will be removed from the member device set.
      */
     public void switchMemberDeviceContent(CachedBluetoothDevice prevMainDevice,
-            CachedBluetoothDevice newMainDevie) {
+            CachedBluetoothDevice newMainDevice) {
         // Backup from main device
         final BluetoothDevice tmpDevice = mDevice;
         final short tmpRssi = mRssi;
         final boolean tmpJustDiscovered = mJustDiscovered;
         // Set main device from sub device
-        mDevice = newMainDevie.mDevice;
-        mRssi = newMainDevie.mRssi;
-        mJustDiscovered = newMainDevie.mJustDiscovered;
-        setMemberDevice(prevMainDevice);
-        mMemberDevices.remove(newMainDevie);
+        mDevice = newMainDevice.mDevice;
+        mRssi = newMainDevice.mRssi;
+        mJustDiscovered = newMainDevice.mJustDiscovered;
+        addMemberDevice(prevMainDevice);
+        mMemberDevices.remove(newMainDevice);
         // Set sub device from backup
-        newMainDevie.mDevice = tmpDevice;
-        newMainDevie.mRssi = tmpRssi;
-        newMainDevie.mJustDiscovered = tmpJustDiscovered;
+        newMainDevice.mDevice = tmpDevice;
+        newMainDevice.mRssi = tmpRssi;
+        newMainDevice.mJustDiscovered = tmpJustDiscovered;
         fetchActiveDevices();
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index cc56a21..89e10c4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -85,7 +85,7 @@
             // Once there is other devices with the same groupId, to add new device as member
             // devices.
             if (CsipDevice != null) {
-                CsipDevice.setMemberDevice(newDevice);
+                CsipDevice.addMemberDevice(newDevice);
                 newDevice.setName(CsipDevice.getName());
                 return true;
             }
@@ -148,7 +148,7 @@
             log("onGroupIdChanged: removed from UI device =" + cachedDevice
                     + ", with groupId=" + groupId + " firstMatchedIndex=" + firstMatchedIndex);
 
-            mainDevice.setMemberDevice(cachedDevice);
+            mainDevice.addMemberDevice(cachedDevice);
             mCachedDevices.remove(i);
             mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice);
             break;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 298ee90..bef1d9c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -40,7 +40,6 @@
 import org.robolectric.RuntimeEnvironment;
 
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.Map;
 
 @RunWith(RobolectricTestRunner.class)
@@ -503,8 +502,8 @@
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
         CachedBluetoothDevice cachedDevice3 = mCachedDeviceManager.addDevice(mDevice3);
-        cachedDevice1.setMemberDevice(cachedDevice2);
-        cachedDevice1.setMemberDevice(cachedDevice3);
+        cachedDevice1.addMemberDevice(cachedDevice2);
+        cachedDevice1.addMemberDevice(cachedDevice3);
 
         assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice2);
         assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice3);
@@ -524,7 +523,7 @@
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
         cachedDevice1.setGroupId(1);
         cachedDevice2.setGroupId(1);
-        cachedDevice1.setMemberDevice(cachedDevice2);
+        cachedDevice1.addMemberDevice(cachedDevice2);
 
         // Call onDeviceUnpaired for the one in mCachedDevices.
         mCachedDeviceManager.onDeviceUnpaired(cachedDevice1);
@@ -541,7 +540,7 @@
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
         cachedDevice1.setGroupId(1);
         cachedDevice2.setGroupId(1);
-        cachedDevice1.setMemberDevice(cachedDevice2);
+        cachedDevice1.addMemberDevice(cachedDevice2);
 
         // Call onDeviceUnpaired for the one in mCachedDevices.
         mCachedDeviceManager.onDeviceUnpaired(cachedDevice2);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
index 61a28aa..9abb27e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
@@ -20,6 +20,7 @@
 
 import android.content.Context;
 import android.view.LayoutInflater;
+import android.view.View;
 import android.widget.TextView;
 
 import androidx.preference.PreferenceViewHolder;
@@ -61,7 +62,7 @@
         mFooterPreference.onBindViewHolder(holder);
 
         assertThat(((TextView) holder.findViewById(
-                        R.id.settingslib_learn_more)).getText().toString())
+                R.id.settingslib_learn_more)).getText().toString())
                 .isEqualTo("Custom learn more");
     }
 
@@ -86,4 +87,11 @@
 
         assertThat(mFooterPreference.mLearnMoreListener).isNotNull();
     }
+
+    @Test
+    public void setIconVisibility_shouldReturnSameVisibilityType() {
+        mFooterPreference.setIconVisibility(View.GONE);
+
+        assertThat(mFooterPreference.mIconVisibility).isEqualTo(View.GONE);
+    }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 3029781..5eaf553 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -120,6 +120,9 @@
         Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
         Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
         Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
+        Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS,
+        Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
+        Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
         Settings.Secure.VR_DISPLAY_MODE,
         Settings.Secure.NOTIFICATION_BADGING,
         Settings.Secure.NOTIFICATION_DISMISS_RTL,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index a4da497..9ee7b65 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -18,6 +18,7 @@
 
 import static android.provider.settings.validators.SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_COMPONENT_LIST_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_PACKAGE_LIST_VALIDATOR;
@@ -176,6 +177,10 @@
         VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_WAKE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS, ANY_STRING_VALIDATOR);
+        VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, ANY_STRING_VALIDATOR);
+        VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+                ANY_STRING_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index aadfcea..a6edb0f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -5509,16 +5509,7 @@
                     currentVersion = 209;
                 }
                 if (currentVersion == 209) {
-                    // Version 209: Enable enforcement of
-                    // android.Manifest.permission#POST_NOTIFICATIONS in order for applications
-                    // to post notifications.
-                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
-                    secureSettings.insertSettingLocked(
-                            Secure.NOTIFICATION_PERMISSION_ENABLED,
-                            /* enabled= */ "1",
-                            /* tag= */ null,
-                            /* makeDefault= */ false,
-                            SettingsState.SYSTEM_PACKAGE_NAME);
+                    // removed now that feature is enabled for everyone
                     currentVersion = 210;
                 }
 
diff --git a/packages/SystemUI/res/drawable/media_output_icon_volume.xml b/packages/SystemUI/res/drawable/media_output_icon_volume.xml
new file mode 100644
index 0000000..fce4e00
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_output_icon_volume.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@color/media_dialog_item_main_content"
+      android:pathData="M14,20.725V18.675Q16.25,18.025 17.625,16.175Q19,14.325 19,11.975Q19,9.625 17.625,7.775Q16.25,5.925 14,5.275V3.225Q17.1,3.925 19.05,6.362Q21,8.8 21,11.975Q21,15.15 19.05,17.587Q17.1,20.025 14,20.725ZM3,15V9H7L12,4V20L7,15ZM14,16V7.95Q15.125,8.475 15.812,9.575Q16.5,10.675 16.5,12Q16.5,13.325 15.812,14.4Q15.125,15.475 14,16ZM10,8.85 L7.85,11H5V13H7.85L10,15.15ZM7.5,12Z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/alert_dialog_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_systemui.xml
index 528f603..fd06238 100644
--- a/packages/SystemUI/res/layout/alert_dialog_systemui.xml
+++ b/packages/SystemUI/res/layout/alert_dialog_systemui.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-
 <!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+  ~ Copyright (C) 2022 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
@@ -15,88 +14,83 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<androidx.core.widget.NestedScrollView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content">
 
-    <com.android.internal.widget.AlertDialogLayout
-        android:id="@*android:id/parentPanel"
+<com.android.internal.widget.AlertDialogLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@*android:id/parentPanel"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_horizontal|top"
+    android:orientation="vertical"
+    android:paddingTop="@dimen/dialog_top_padding"
+>
+
+    <include layout="@layout/alert_dialog_title_systemui" />
+
+    <FrameLayout
+        android:id="@*android:id/contentPanel"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:gravity="center_horizontal|top"
-        android:orientation="vertical"
-        android:paddingTop="@dimen/dialog_top_padding"
-        >
+        android:minHeight="48dp"
+        android:paddingStart="@dimen/dialog_side_padding"
+        android:paddingEnd="@dimen/dialog_side_padding"
+    >
 
-        <include layout="@layout/alert_dialog_title_systemui" />
-
-        <FrameLayout
-            android:id="@*android:id/contentPanel"
+        <ScrollView
+            android:id="@*android:id/scrollView"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:minHeight="48dp"
-            android:paddingStart="@dimen/dialog_side_padding"
-            android:paddingEnd="@dimen/dialog_side_padding"
-            >
+            android:clipToPadding="false">
 
-            <ScrollView
-                android:id="@*android:id/scrollView"
+            <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:clipToPadding="false">
+                android:orientation="vertical">
 
-                <LinearLayout
+                <Space
+                    android:id="@*android:id/textSpacerNoTitle"
+                    android:visibility="gone"
+                    android:layout_width="match_parent"
+                    android:layout_height="0dp" />
+
+                <TextView
+                    android:id="@*android:id/message"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:orientation="vertical">
+                    style="@style/TextAppearance.Dialog.Body.Message" />
 
-                    <Space
-                        android:id="@*android:id/textSpacerNoTitle"
-                        android:visibility="gone"
-                        android:layout_width="match_parent"
-                        android:layout_height="0dp" />
+                <Space
+                    android:id="@*android:id/textSpacerNoButtons"
+                    android:visibility="gone"
+                    android:layout_width="match_parent"
+                    android:layout_height="6dp" />
+            </LinearLayout>
+        </ScrollView>
+    </FrameLayout>
 
-                    <TextView
-                        android:id="@*android:id/message"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        style="@style/TextAppearance.Dialog.Body.Message" />
-
-                    <Space
-                        android:id="@*android:id/textSpacerNoButtons"
-                        android:visibility="gone"
-                        android:layout_width="match_parent"
-                        android:layout_height="6dp" />
-                </LinearLayout>
-            </ScrollView>
-        </FrameLayout>
+    <FrameLayout
+        android:id="@*android:id/customPanel"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="48dp"
+        android:paddingStart="@dimen/dialog_side_padding"
+        android:paddingEnd="@dimen/dialog_side_padding"
+    >
 
         <FrameLayout
-            android:id="@*android:id/customPanel"
+            android:id="@*android:id/custom"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+    </FrameLayout>
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingStart="@dimen/dialog_side_padding"
+        android:paddingEnd="@dimen/dialog_side_padding">
+        <include
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:minHeight="48dp"
-            android:paddingStart="@dimen/dialog_side_padding"
-            android:paddingEnd="@dimen/dialog_side_padding"
-            >
-
-            <FrameLayout
-                android:id="@*android:id/custom"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content" />
-        </FrameLayout>
-
-        <FrameLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:paddingStart="@dimen/dialog_side_padding"
-            android:paddingEnd="@dimen/dialog_side_padding">
-            <include
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                layout="@layout/alert_dialog_button_bar_systemui" />
-        </FrameLayout>
-    </com.android.internal.widget.AlertDialogLayout>
-
-</androidx.core.widget.NestedScrollView>
\ No newline at end of file
+            layout="@layout/alert_dialog_button_bar_systemui" />
+    </FrameLayout>
+</com.android.internal.widget.AlertDialogLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index 10bb6cb..085a581 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -67,7 +67,7 @@
         android:layout_width="0dp"
         android:layout_height="0dp"
         android:layout_marginStart="@dimen/overlay_offset_x"
-        android:layout_marginBottom="@dimen/overlay_offset_y"
+        android:layout_marginBottom="8dp"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintBottom_toBottomOf="@id/actions_container_background"
         android:elevation="7dp"
diff --git a/packages/SystemUI/res/layout/screenshot.xml b/packages/SystemUI/res/layout/screenshot.xml
index 890dbe5..c29e11b 100644
--- a/packages/SystemUI/res/layout/screenshot.xml
+++ b/packages/SystemUI/res/layout/screenshot.xml
@@ -29,18 +29,11 @@
         android:clickable="true"
         android:importantForAccessibility="no"/>
     <ImageView
-        android:id="@+id/screenshot_actions_background"
-        android:layout_height="@dimen/overlay_bg_protection_height"
-        android:layout_width="match_parent"
-        android:layout_gravity="bottom"
-        android:alpha="0.0"
-        android:src="@drawable/overlay_actions_background_protection"/>
-    <ImageView
         android:id="@+id/screenshot_flash"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:visibility="gone"
-        android:elevation="@dimen/overlay_preview_elevation"
+        android:elevation="7dp"
         android:src="@android:color/white"/>
     <com.android.systemui.screenshot.ScreenshotSelectorView
         android:id="@+id/screenshot_selector"
diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml
index c60609b..9c02749 100644
--- a/packages/SystemUI/res/layout/screenshot_static.xml
+++ b/packages/SystemUI/res/layout/screenshot_static.xml
@@ -24,7 +24,7 @@
         android:visibility="gone"
         android:layout_height="0dp"
         android:layout_width="0dp"
-        android:elevation="1dp"
+        android:elevation="4dp"
         android:background="@drawable/action_chip_container_background"
         android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
         app:layout_constraintBottom_toBottomOf="@+id/actions_container"
@@ -36,9 +36,10 @@
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal"
+        android:layout_marginBottom="4dp"
         android:paddingEnd="@dimen/overlay_action_container_padding_right"
         android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
-        android:elevation="1dp"
+        android:elevation="4dp"
         android:scrollbars="none"
         app:layout_constraintHorizontal_bias="0"
         app:layout_constraintWidth_percent="1.0"
@@ -64,8 +65,8 @@
         android:layout_width="0dp"
         android:layout_height="0dp"
         android:layout_marginStart="@dimen/overlay_offset_x"
-        android:layout_marginBottom="@dimen/overlay_offset_y"
-        android:elevation="@dimen/overlay_preview_elevation"
+        android:layout_marginBottom="12dp"
+        android:elevation="7dp"
         android:alpha="0"
         android:background="@drawable/overlay_border"
         app:layout_constraintStart_toStartOf="parent"
@@ -93,7 +94,7 @@
         android:layout_margin="@dimen/overlay_border_width"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
-        android:elevation="@dimen/overlay_preview_elevation"
+        android:elevation="7dp"
         android:contentDescription="@string/screenshot_edit_description"
         android:scaleType="fitEnd"
         android:background="@drawable/overlay_preview_background"
@@ -108,7 +109,7 @@
         android:id="@+id/screenshot_dismiss_button"
         android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
         android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
-        android:elevation="@dimen/overlay_dismiss_button_elevation"
+        android:elevation="10dp"
         android:visibility="gone"
         app:layout_constraintStart_toEndOf="@id/screenshot_preview"
         app:layout_constraintEnd_toEndOf="@id/screenshot_preview"
@@ -130,5 +131,5 @@
         android:visibility="gone"
         app:layout_constraintStart_toStartOf="@id/screenshot_preview"
         app:layout_constraintTop_toTopOf="@id/screenshot_preview"
-        android:elevation="@dimen/overlay_preview_elevation"/>
+        android:elevation="7dp"/>
 </com.android.systemui.screenshot.DraggableConstraintLayout>
diff --git a/packages/SystemUI/res/layout/scrollable_alert_dialog_systemui.xml b/packages/SystemUI/res/layout/scrollable_alert_dialog_systemui.xml
new file mode 100644
index 0000000..71bb938
--- /dev/null
+++ b/packages/SystemUI/res/layout/scrollable_alert_dialog_systemui.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+<androidx.core.widget.NestedScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <include layout="@layout/alert_dialog_systemui" />
+
+</androidx.core.widget.NestedScrollView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml
index 1d6f279..e6af6f4 100644
--- a/packages/SystemUI/res/values-h800dp/dimens.xml
+++ b/packages/SystemUI/res/values-h800dp/dimens.xml
@@ -16,7 +16,7 @@
 
 <resources>
     <!-- Minimum margin between clock and top of screen or ambient indication -->
-    <dimen name="keyguard_clock_top_margin">38dp</dimen>
+    <dimen name="keyguard_clock_top_margin">26dp</dimen>
 
     <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) -->
     <dimen name="large_clock_text_size">200dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a014efb..0bc3594 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -282,15 +282,12 @@
     <!-- Spacing between chip icon and chip text -->
     <dimen name="overlay_action_chip_spacing">8dp</dimen>
     <dimen name="overlay_action_chip_text_size">14sp</dimen>
-    <dimen name="overlay_offset_y">8dp</dimen>
     <dimen name="overlay_offset_x">16dp</dimen>
-    <dimen name="overlay_preview_elevation">4dp</dimen>
     <dimen name="overlay_action_container_margin_horizontal">8dp</dimen>
     <dimen name="overlay_bg_protection_height">242dp</dimen>
     <dimen name="overlay_action_container_corner_radius">18dp</dimen>
     <dimen name="overlay_action_container_padding_vertical">4dp</dimen>
     <dimen name="overlay_action_container_padding_right">8dp</dimen>
-    <dimen name="overlay_dismiss_button_elevation">7dp</dimen>
     <dimen name="overlay_dismiss_button_tappable_size">48dp</dimen>
     <dimen name="overlay_dismiss_button_margin">8dp</dimen>
     <dimen name="overlay_border_width">4dp</dimen>
@@ -1134,7 +1131,7 @@
 
     <!-- Output switcher panel related dimensions -->
     <dimen name="media_output_dialog_list_margin">12dp</dimen>
-    <dimen name="media_output_dialog_list_max_height">364dp</dimen>
+    <dimen name="media_output_dialog_list_max_height">355dp</dimen>
     <dimen name="media_output_dialog_header_album_icon_size">72dp</dimen>
     <dimen name="media_output_dialog_header_back_icon_size">32dp</dimen>
     <dimen name="media_output_dialog_header_icon_padding">16dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f9e73ec..0c25f54 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -380,7 +380,7 @@
         <item name="android:buttonBarNegativeButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
         <item name="android:buttonBarNeutralButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
         <item name="android:colorBackground">?androidprv:attr/colorSurface</item>
-        <item name="android:alertDialogStyle">@style/AlertDialogStyle</item>
+        <item name="android:alertDialogStyle">@style/ScrollableAlertDialogStyle</item>
         <item name="android:buttonBarStyle">@style/ButtonBarStyle</item>
         <item name="android:buttonBarButtonStyle">@style/Widget.Dialog.Button.Large</item>
     </style>
@@ -389,6 +389,10 @@
         <item name="android:layout">@layout/alert_dialog_systemui</item>
     </style>
 
+    <style name="ScrollableAlertDialogStyle" parent="@androidprv:style/AlertDialog.DeviceDefault">
+        <item name="android:layout">@layout/scrollable_alert_dialog_systemui</item>
+    </style>
+
     <style name="ButtonBarStyle" parent="@androidprv:style/DeviceDefault.ButtonBar.AlertDialog">
         <item name="android:paddingTop">@dimen/dialog_button_bar_top_padding</item>
         <item name="android:paddingBottom">@dimen/dialog_bottom_padding</item>
@@ -994,6 +998,7 @@
 
     <style name="Theme.SystemUI.Dialog.Cast">
         <item name="android:textAppearanceMedium">@style/TextAppearance.CastItem</item>
+        <item name="android:alertDialogStyle">@style/AlertDialogStyle</item>
     </style>
     <!-- ************************************************************************************* -->
 
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index f195d20..38fa354 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -16,14 +16,20 @@
 
 package com.android.keyguard
 
+import android.annotation.IntDef
 import android.content.ContentResolver
 import android.database.ContentObserver
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT
 import android.net.Uri
 import android.os.Handler
 import android.os.UserHandle
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT
+import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
+import android.util.Log
 import com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
@@ -44,6 +50,20 @@
     dumpManager: DumpManager
 ) : Dumpable {
 
+    companion object {
+        const val TAG = "ActiveUnlockConfig"
+
+        const val BIOMETRIC_TYPE_NONE = 0
+        const val BIOMETRIC_TYPE_ANY_FACE = 1
+        const val BIOMETRIC_TYPE_ANY_FINGERPRINT = 2
+        const val BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT = 3
+    }
+
+    @Retention(AnnotationRetention.SOURCE)
+    @IntDef(BIOMETRIC_TYPE_NONE, BIOMETRIC_TYPE_ANY_FACE, BIOMETRIC_TYPE_ANY_FINGERPRINT,
+            BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT)
+    annotation class BiometricType
+
     /**
      * Indicates the origin for an active unlock request.
      */
@@ -51,35 +71,50 @@
         WAKE, UNLOCK_INTENT, BIOMETRIC_FAIL, ASSISTANT
     }
 
+    var keyguardUpdateMonitor: KeyguardUpdateMonitor? = null
     private var requestActiveUnlockOnWakeup = false
     private var requestActiveUnlockOnUnlockIntent = false
     private var requestActiveUnlockOnBioFail = false
 
+    private var faceErrorsToTriggerBiometricFailOn = mutableSetOf(FACE_ERROR_TIMEOUT)
+    private var faceAcquireInfoToTriggerBiometricFailOn = mutableSetOf<Int>()
+    private var onUnlockIntentWhenBiometricEnrolled = mutableSetOf<Int>(BIOMETRIC_TYPE_NONE)
+
     private val settingsObserver = object : ContentObserver(handler) {
-        private val wakeUri: Uri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE)
-        private val unlockIntentUri: Uri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT)
-        private val bioFailUri: Uri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)
+        private val wakeUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE)
+        private val unlockIntentUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT)
+        private val bioFailUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)
+        private val faceErrorsUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS)
+        private val faceAcquireInfoUri =
+                secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO)
+        private val unlockIntentWhenBiometricEnrolledUri =
+                secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
 
         fun register() {
-            contentResolver.registerContentObserver(
-                    wakeUri,
-                    false,
-                    this,
-                    UserHandle.USER_ALL)
-            contentResolver.registerContentObserver(
-                    unlockIntentUri,
-                    false,
-                    this,
-                    UserHandle.USER_ALL)
-            contentResolver.registerContentObserver(
-                    bioFailUri,
-                    false,
-                    this,
-                    UserHandle.USER_ALL)
+            registerUri(
+                    listOf(
+                            wakeUri,
+                            unlockIntentUri,
+                            bioFailUri,
+                            faceErrorsUri,
+                            faceAcquireInfoUri,
+                            unlockIntentWhenBiometricEnrolledUri
+                    )
+            )
 
             onChange(true, ArrayList(), 0, getCurrentUser())
         }
 
+        private fun registerUri(uris: Collection<Uri>) {
+            for (uri in uris) {
+                contentResolver.registerContentObserver(
+                        uri,
+                        false,
+                        this,
+                        UserHandle.USER_ALL)
+            }
+        }
+
         override fun onChange(
             selfChange: Boolean,
             uris: Collection<Uri>,
@@ -104,6 +139,55 @@
                 requestActiveUnlockOnBioFail = secureSettings.getIntForUser(
                         ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 0, getCurrentUser()) == 1
             }
+
+            if (selfChange || uris.contains(faceErrorsUri)) {
+                processStringArray(
+                        secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ERRORS,
+                                getCurrentUser()),
+                        faceErrorsToTriggerBiometricFailOn,
+                        setOf(FACE_ERROR_TIMEOUT))
+            }
+
+            if (selfChange || uris.contains(faceAcquireInfoUri)) {
+                processStringArray(
+                        secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
+                                getCurrentUser()),
+                        faceAcquireInfoToTriggerBiometricFailOn,
+                        setOf<Int>())
+            }
+
+            if (selfChange || uris.contains(unlockIntentWhenBiometricEnrolledUri)) {
+                processStringArray(
+                        secureSettings.getStringForUser(
+                                ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+                                getCurrentUser()),
+                        onUnlockIntentWhenBiometricEnrolled,
+                        setOf(BIOMETRIC_TYPE_NONE))
+            }
+        }
+
+        /**
+         * Convert a pipe-separated set of integers into a set of ints.
+         * @param stringSetting expected input are integers delineated by a pipe. For example,
+         * it may look something like this: "1|5|3".
+         * @param out updates the "out" Set will the integers between the pipes.
+         * @param default If stringSetting is null, "out" will be populated with values in "default"
+         */
+        private fun processStringArray(
+            stringSetting: String?,
+            out: MutableSet<Int>,
+            default: Set<Int>
+        ) {
+            out.clear()
+            stringSetting?.let {
+                for (code: String in stringSetting.split("|")) {
+                    try {
+                        out.add(code.toInt())
+                    } catch (e: NumberFormatException) {
+                        Log.e(TAG, "Passed an invalid setting=$code")
+                    }
+                }
+            } ?: out.addAll(default)
         }
     }
 
@@ -113,6 +197,30 @@
     }
 
     /**
+     * If any active unlock triggers are enabled.
+     */
+    fun isActiveUnlockEnabled(): Boolean {
+        return requestActiveUnlockOnWakeup || requestActiveUnlockOnUnlockIntent ||
+                requestActiveUnlockOnBioFail
+    }
+
+    /**
+     * Whether the face error code from {@link BiometricFaceConstants} should trigger
+     * active unlock on biometric failure.
+     */
+    fun shouldRequestActiveUnlockOnFaceError(errorCode: Int): Boolean {
+        return faceErrorsToTriggerBiometricFailOn.contains(errorCode)
+    }
+
+    /**
+     * Whether the face acquireInfo from {@link BiometricFaceConstants} should trigger
+     * active unlock on biometric failure.
+     */
+    fun shouldRequestActiveUnlockOnFaceAcquireInfo(acquiredInfo: Int): Boolean {
+        return faceAcquireInfoToTriggerBiometricFailOn.contains(acquiredInfo)
+    }
+
+    /**
      * Whether to trigger active unlock based on where the request is coming from and
      * the current settings.
      */
@@ -121,7 +229,8 @@
             ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE -> requestActiveUnlockOnWakeup
 
             ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT ->
-                requestActiveUnlockOnUnlockIntent || requestActiveUnlockOnWakeup
+                requestActiveUnlockOnUnlockIntent || requestActiveUnlockOnWakeup ||
+                        (shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment())
 
             ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL ->
                 requestActiveUnlockOnBioFail || requestActiveUnlockOnUnlockIntent ||
@@ -131,17 +240,55 @@
         }
     }
 
-    /**
-     * If any active unlock triggers are enabled.
-     */
-    fun isActiveUnlockEnabled(): Boolean {
-        return requestActiveUnlockOnWakeup || requestActiveUnlockOnUnlockIntent ||
-                requestActiveUnlockOnBioFail
+    private fun shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment(): Boolean {
+        if (!requestActiveUnlockOnBioFail) {
+            return false
+        }
+
+        keyguardUpdateMonitor?.let {
+            val anyFaceEnrolled = it.isFaceEnrolled
+            val anyFingerprintEnrolled =
+                    it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser())
+            val udfpsEnrolled = it.isUdfpsEnrolled
+
+            if (!anyFaceEnrolled && !anyFingerprintEnrolled) {
+                return onUnlockIntentWhenBiometricEnrolled.contains(BIOMETRIC_TYPE_NONE)
+            }
+
+            if (!anyFaceEnrolled && anyFingerprintEnrolled) {
+                return onUnlockIntentWhenBiometricEnrolled.contains(
+                        BIOMETRIC_TYPE_ANY_FINGERPRINT) ||
+                        (udfpsEnrolled && onUnlockIntentWhenBiometricEnrolled.contains(
+                                BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT))
+            }
+
+            if (!anyFingerprintEnrolled && anyFaceEnrolled) {
+                return onUnlockIntentWhenBiometricEnrolled.contains(BIOMETRIC_TYPE_ANY_FACE)
+            }
+        }
+
+        return false
     }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("Settings:")
         pw.println("   requestActiveUnlockOnWakeup=$requestActiveUnlockOnWakeup")
         pw.println("   requestActiveUnlockOnUnlockIntent=$requestActiveUnlockOnUnlockIntent")
         pw.println("   requestActiveUnlockOnBioFail=$requestActiveUnlockOnBioFail")
+        pw.println("   requestActiveUnlockOnUnlockIntentWhenBiometricEnrolled=" +
+                "$onUnlockIntentWhenBiometricEnrolled")
+        pw.println("   requestActiveUnlockOnFaceError=$faceErrorsToTriggerBiometricFailOn")
+        pw.println("   requestActiveUnlockOnFaceAcquireInfo=" +
+                "$faceAcquireInfoToTriggerBiometricFailOn")
+
+        pw.println("Current state:")
+        keyguardUpdateMonitor?.let {
+            pw.println("   shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment=" +
+                    "${shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()}")
+            pw.println("   faceEnrolled=${it.isFaceEnrolled}")
+            pw.println("   fpEnrolled=${
+                    it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser())}")
+            pw.println("   udfpsEnrolled=${it.isUdfpsEnrolled}")
+        } ?: pw.println("   keyguardUpdateMonitor is uninitialized")
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index bbe9a362..121ac29 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -56,7 +56,6 @@
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.hardware.SensorPrivacyManager;
-import android.hardware.biometrics.BiometricFaceConstants;
 import android.hardware.biometrics.BiometricFingerprintConstants;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricSourceType;
@@ -1615,7 +1614,7 @@
                         mKeyguardBypassController.setUserHasDeviceEntryIntent(false);
                     }
 
-                    if (errMsgId == BiometricFaceConstants.FACE_ERROR_TIMEOUT) {
+                    if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceError(errMsgId)) {
                         requestActiveUnlock(
                                 ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL,
                                 "faceError-" + errMsgId);
@@ -1625,6 +1624,13 @@
                 @Override
                 public void onAuthenticationAcquired(int acquireInfo) {
                     handleFaceAcquired(acquireInfo);
+
+                    if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+                            acquireInfo)) {
+                        requestActiveUnlock(
+                                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL,
+                                "faceAcquireInfo-" + acquireInfo);
+                    }
                 }
     };
 
@@ -1639,6 +1645,7 @@
     private boolean mFingerprintLockedOut;
     private boolean mFingerprintLockedOutPermanent;
     private boolean mFaceLockedOutPermanent;
+    private HashMap<Integer, Boolean> mIsUnlockWithFingerprintPossible = new HashMap<>();
     private TelephonyManager mTelephonyManager;
 
     /**
@@ -1889,6 +1896,7 @@
         dumpManager.registerDumpable(getClass().getName(), this);
         mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
         mActiveUnlockConfig = activeUnlockConfiguration;
+        mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
 
         mHandler = new Handler(mainLooper) {
             @Override
@@ -2329,7 +2337,7 @@
         }
 
         if (shouldTriggerActiveUnlock()) {
-            if (DEBUG) {
+            if (DEBUG_ACTIVE_UNLOCK) {
                 Log.d("ActiveUnlock", "initiate active unlock triggerReason=" + reason);
             }
             mTrustManager.reportUserMayRequestUnlock(KeyguardUpdateMonitor.getCurrentUser());
@@ -2359,7 +2367,7 @@
         }
 
         if (allowRequest && shouldTriggerActiveUnlock()) {
-            if (DEBUG) {
+            if (DEBUG_ACTIVE_UNLOCK) {
                 Log.d("ActiveUnlock", "reportUserRequestedUnlock"
                         + " origin=" + requestOrigin.name()
                         + " reason=" + reason
@@ -2777,8 +2785,17 @@
     }
 
     private boolean isUnlockWithFingerprintPossible(int userId) {
-        return mFpm != null && mFpm.isHardwareDetected() && !isFingerprintDisabled(userId)
-                && mFpm.hasEnrolledTemplates(userId);
+        mIsUnlockWithFingerprintPossible.put(userId, mFpm != null && mFpm.isHardwareDetected()
+                && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId));
+        return mIsUnlockWithFingerprintPossible.get(userId);
+    }
+
+    /**
+     * Cached value for whether fingerprint is enrolled and possible to use for authentication.
+     * Note: checking fingerprint enrollment directly with the AuthController requires an IPC.
+     */
+    public boolean getCachedIsUnlockWithFingerprintPossible(int userId) {
+        return mIsUnlockWithFingerprintPossible.get(userId);
     }
 
     private boolean isUnlockWithFacePossible(int userId) {
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index e191365..fdde402 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -55,7 +55,7 @@
 
     @NonNull private final RectF mSensorRect;
     @NonNull private PointF mLockIconCenter = new PointF(0f, 0f);
-    private int mRadius;
+    private float mRadius;
     private int mLockIconPadding;
 
     private ImageView mLockIcon;
@@ -126,7 +126,7 @@
      * Set the location of the lock icon.
      */
     @VisibleForTesting
-    public void setCenterLocation(@NonNull PointF center, int radius, int drawablePadding) {
+    public void setCenterLocation(@NonNull PointF center, float radius, int drawablePadding) {
         mLockIconCenter = center;
         mRadius = radius;
         mLockIconPadding = drawablePadding;
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 2b217f0..d79b145 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -188,6 +188,7 @@
     protected void onViewAttached() {
         updateIsUdfpsEnrolled();
         updateConfiguration();
+        updateLockIconLocation();
         updateKeyguardShowing();
         mUserUnlockedWithBiometric = false;
 
@@ -340,20 +341,17 @@
         mHeightPixels = bounds.bottom;
         mBottomPaddingPx = getResources().getDimensionPixelSize(R.dimen.lock_icon_margin_bottom);
 
-        final int defaultPaddingPx =
-                getResources().getDimensionPixelSize(R.dimen.lock_icon_padding);
-        mScaledPaddingPx = (int) (defaultPaddingPx * mAuthController.getScaleFactor());
-
         mUnlockedLabel = mView.getContext().getResources().getString(
                 R.string.accessibility_unlock_button);
         mLockedLabel = mView.getContext()
                 .getResources().getString(R.string.accessibility_lock_icon);
-
-        updateLockIconLocation();
     }
 
     private void updateLockIconLocation() {
         if (mUdfpsSupported) {
+            final int defaultPaddingPx =
+                    getResources().getDimensionPixelSize(R.dimen.lock_icon_padding);
+            mScaledPaddingPx = (int) (defaultPaddingPx * mAuthController.getScaleFactor());
             mView.setCenterLocation(mAuthController.getUdfpsLocation(),
                     mAuthController.getUdfpsRadius(), mScaledPaddingPx);
         } else {
@@ -362,8 +360,6 @@
                         mHeightPixels - mBottomPaddingPx - sLockIconRadiusPx),
                         sLockIconRadiusPx, mScaledPaddingPx);
         }
-
-        mView.getHitRect(mSensorTouchLocation);
     }
 
     @Override
@@ -386,6 +382,7 @@
         pw.println("  mCanDismissLockScreen: " + mCanDismissLockScreen);
         pw.println("  mStatusBarState: " + StatusBarState.toString(mStatusBarState));
         pw.println("  mInterpolatedDarkAmount: " + mInterpolatedDarkAmount);
+        pw.println("  mSensorTouchLocation: " + mSensorTouchLocation);
 
         if (mView != null) {
             mView.dump(pw, args);
@@ -672,6 +669,7 @@
     }
 
     private boolean inLockIconArea(MotionEvent event) {
+        mView.getHitRect(mSensorTouchLocation);
         return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
                 && mView.getVisibility() == View.VISIBLE;
     }
@@ -692,6 +690,7 @@
         mExecutor.execute(() -> {
             updateIsUdfpsEnrolled();
             updateConfiguration();
+            updateLockIconLocation();
         });
     }
 
@@ -705,6 +704,11 @@
         public void onEnrollmentsChanged() {
             updateUdfpsConfig();
         }
+
+        @Override
+        public void onUdfpsLocationChanged() {
+            updateLockIconLocation();
+        }
     };
 
     private final View.OnClickListener mA11yClickListener = v -> onLongPress();
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index cbce854..dd31218 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -47,6 +47,7 @@
 import android.hardware.graphics.common.DisplayDecorationSupport;
 import android.os.Handler;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings.Secure;
 import android.util.DisplayUtils;
@@ -1067,15 +1068,22 @@
     }
 
     private void updateLayoutParams() {
-        if (mOverlays == null) {
-            return;
+        //ToDo: We should skip unnecessary call to update view layout.
+        Trace.beginSection("ScreenDecorations#updateLayoutParams");
+        if (mScreenDecorHwcWindow != null) {
+            mWindowManager.updateViewLayout(mScreenDecorHwcWindow, getHwcWindowLayoutParams());
         }
-        for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
-            if (mOverlays[i] == null) {
-                continue;
+
+        if (mOverlays != null) {
+            for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
+                if (mOverlays[i] == null) {
+                    continue;
+                }
+                mWindowManager.updateViewLayout(
+                        mOverlays[i].getRootView(), getWindowLayoutParams(i));
             }
-            mWindowManager.updateViewLayout(mOverlays[i].getRootView(), getWindowLayoutParams(i));
         }
+        Trace.endSection();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 9324893..75339aa 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -79,6 +79,7 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 import javax.inject.Inject;
@@ -446,11 +447,11 @@
     /**
      * @return the radius of UDFPS on the screen in pixels
      */
-    public int getUdfpsRadius() {
+    public float getUdfpsRadius() {
         if (mUdfpsController == null || mUdfpsBounds == null) {
             return -1;
         }
-        return mUdfpsBounds.height() / 2;
+        return mUdfpsBounds.height() / 2f;
     }
 
     /**
@@ -634,11 +635,17 @@
                     displayInfo.getNaturalHeight());
 
             final FingerprintSensorPropertiesInternal udfpsProp = mUdfpsProps.get(0);
+            final Rect previousUdfpsBounds = mUdfpsBounds;
             mUdfpsBounds = udfpsProp.getLocation().getRect();
             mUdfpsBounds.scale(scaleFactor);
             mUdfpsController.updateOverlayParams(udfpsProp.sensorId,
                     new UdfpsOverlayParams(mUdfpsBounds, displayInfo.getNaturalWidth(),
                             displayInfo.getNaturalHeight(), scaleFactor, displayInfo.rotation));
+            if (!Objects.equals(previousUdfpsBounds, mUdfpsBounds)) {
+                for (Callback cb : mCallbacks) {
+                    cb.onUdfpsLocationChanged();
+                }
+            }
         }
     }
 
@@ -1054,5 +1061,10 @@
          * Called when the biometric prompt is no longer showing.
          */
         default void onBiometricPromptDismissed() {}
+
+        /**
+         * The location in pixels can change due to resolution changes.
+         */
+        default void onUdfpsLocationChanged() {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index d9aa1ba..86e5016 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -335,15 +335,17 @@
                 updateSensorLocation()
             }
 
-            override fun onEnrollmentsChanged() {
+            override fun onUdfpsLocationChanged() {
+                updateUdfpsDependentParams()
+                updateSensorLocation()
             }
         }
 
     private fun updateUdfpsDependentParams() {
         authController.udfpsProps?.let {
             if (it.size > 0) {
-                udfpsRadius = it[0].location.sensorRadius.toFloat()
                 udfpsController = udfpsControllerProvider.get()
+                udfpsRadius = authController.udfpsRadius
 
                 if (mView.isAttachedToWindow) {
                     udfpsController?.addCallback(udfpsControllerCallback)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index 9139699..f28fedb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -32,6 +32,7 @@
 import android.view.ViewGroup;
 import android.widget.ImageView;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
 
@@ -44,6 +45,8 @@
 import com.airbnb.lottie.model.KeyPath;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * View corresponding with udfps_keyguard_view.xml
@@ -52,7 +55,6 @@
     private UdfpsDrawable mFingerprintDrawable; // placeholder
     private LottieAnimationView mAodFp;
     private LottieAnimationView mLockScreenFp;
-    private int mStatusBarState;
 
     // used when highlighting fp icon:
     private int mTextColorPrimary;
@@ -70,7 +72,7 @@
     private float mBurnInOffsetY;
     private float mBurnInProgress;
     private float mInterpolatedDarkAmount;
-    private boolean mAnimatingBetweenAodAndLockscreen; // As opposed to Unlocked => AOD
+    private int mAnimationType = ANIMATION_NONE;
     private boolean mFullyInflated;
 
     public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) {
@@ -117,8 +119,10 @@
             return;
         }
 
-        final float darkAmountForAnimation = mAnimatingBetweenAodAndLockscreen
-                ? mInterpolatedDarkAmount : 1f /* animating from unlocked to AOD */;
+        // if we're animating from screen off, we can immediately place the icon in the
+        // AoD-burn in location, else we need to translate the icon from LS => AoD.
+        final float darkAmountForAnimation = mAnimationType == ANIMATION_UNLOCKED_SCREEN_OFF
+                ? 1f : mInterpolatedDarkAmount;
         mBurnInOffsetX = MathUtils.lerp(0f,
             getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */)
                 - mMaxBurnInOffsetX, darkAmountForAnimation);
@@ -127,12 +131,12 @@
                 - mMaxBurnInOffsetY, darkAmountForAnimation);
         mBurnInProgress = MathUtils.lerp(0f, getBurnInProgressOffset(), darkAmountForAnimation);
 
-        if (mAnimatingBetweenAodAndLockscreen && !mPauseAuth) {
+        if (mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN && !mPauseAuth) {
             mLockScreenFp.setTranslationX(mBurnInOffsetX);
             mLockScreenFp.setTranslationY(mBurnInOffsetY);
             mBgProtection.setAlpha(1f - mInterpolatedDarkAmount);
             mLockScreenFp.setAlpha(1f - mInterpolatedDarkAmount);
-        } else if (mInterpolatedDarkAmount == 0f) {
+        } else if (darkAmountForAnimation == 0f) {
             mLockScreenFp.setTranslationX(0);
             mLockScreenFp.setTranslationY(0);
             mBgProtection.setAlpha(mAlpha / 255f);
@@ -148,9 +152,15 @@
         mAodFp.setProgress(mBurnInProgress);
         mAodFp.setAlpha(mInterpolatedDarkAmount);
 
-        // done animating between AoD & LS
-        if (mInterpolatedDarkAmount == 1f || mInterpolatedDarkAmount == 0f) {
-            mAnimatingBetweenAodAndLockscreen = false;
+        // done animating
+        final boolean doneAnimatingBetweenAodAndLS =
+                mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN
+                        && (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f);
+        final boolean doneAnimatingUnlockedScreenOff =
+                mAnimationType == ANIMATION_UNLOCKED_SCREEN_OFF
+                        && (mInterpolatedDarkAmount == 1f);
+        if (doneAnimatingBetweenAodAndLS || doneAnimatingUnlockedScreenOff) {
+            mAnimationType = ANIMATION_NONE;
         }
     }
 
@@ -158,10 +168,6 @@
         mUdfpsRequested = request;
     }
 
-    void setStatusBarState(int statusBarState) {
-        mStatusBarState = statusBarState;
-    }
-
     void updateColor() {
         if (!mFullyInflated) {
             return;
@@ -219,8 +225,16 @@
         return mAlpha;
     }
 
-    void onDozeAmountChanged(float linear, float eased, boolean animatingBetweenAodAndLockscreen) {
-        mAnimatingBetweenAodAndLockscreen = animatingBetweenAodAndLockscreen;
+    static final int ANIMATION_NONE = 0;
+    static final int ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN = 1;
+    static final int ANIMATION_UNLOCKED_SCREEN_OFF = 2;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ANIMATION_NONE, ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, ANIMATION_UNLOCKED_SCREEN_OFF})
+    private @interface AnimationType {}
+
+    void onDozeAmountChanged(float linear, float eased, @AnimationType int animationType) {
+        mAnimationType = animationType;
         mInterpolatedDarkAmount = eased;
         updateAlpha();
     }
@@ -262,7 +276,7 @@
         pw.println("    mUnpausedAlpha=" + getUnpausedAlpha());
         pw.println("    mUdfpsRequested=" + mUdfpsRequested);
         pw.println("    mInterpolatedDarkAmount=" + mInterpolatedDarkAmount);
-        pw.println("    mAnimatingBetweenAodAndLockscreen=" + mAnimatingBetweenAodAndLockscreen);
+        pw.println("    mAnimationType=" + mAnimationType);
     }
 
     private final AsyncLayoutInflater.OnInflateFinishedListener mLayoutInflaterFinishListener =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 8b0f36f..ec4cf2f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -121,7 +121,7 @@
                         mView.onDozeAmountChanged(
                                 animation.getAnimatedFraction(),
                                 (float) animation.getAnimatedValue(),
-                                /* animatingBetweenAodAndLockScreen */ false);
+                                UdfpsKeyguardView.ANIMATION_UNLOCKED_SCREEN_OFF);
                     }
                 });
     }
@@ -394,7 +394,7 @@
                 mUnlockedScreenOffDozeAnimator.start();
             } else {
                 mView.onDozeAmountChanged(linear, eased,
-                    /* animatingBetweenAodAndLockScreen */ true);
+                        UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN);
             }
 
             mLastDozeAmount = linear;
@@ -404,7 +404,6 @@
         @Override
         public void onStateChanged(int statusBarState) {
             mStatusBarState = statusBarState;
-            mView.setStatusBarState(statusBarState);
             updateAlpha();
             updatePauseAuth();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
rename to packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 5d154c3..4e48a52 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -38,7 +38,6 @@
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.EnhancedEstimates;
-import com.android.systemui.power.EnhancedEstimatesImpl;
 import com.android.systemui.power.dagger.PowerModule;
 import com.android.systemui.qs.dagger.QSModule;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
@@ -80,8 +79,19 @@
 import dagger.Provides;
 
 /**
- * A dagger module for injecting default implementations of components of System UI that may be
- * overridden by the System UI implementation.
+ * A dagger module for injecting default implementations of components of System UI.
+ *
+ * Variants of SystemUI should make a copy of this, include it in their component, and customize it
+ * as needed.
+ *
+ * This module might alternatively be named `AospSystemUIModule`, `PhoneSystemUIModule`,
+ * or `BasicSystemUIModule`.
+ *
+ * Nothing in the module should be strictly required. Each piece should either be swappable with
+ * a different implementation or entirely removable.
+ *
+ * This is different from {@link SystemUIModule} which should be used for pieces of required
+ * SystemUI code that variants of SystemUI _must_ include to function correctly.
  */
 @Module(includes = {
         MediaModule.class,
@@ -90,7 +100,7 @@
         StartCentralSurfacesModule.class,
         VolumeModule.class
 })
-public abstract class SystemUIDefaultModule {
+public abstract class ReferenceSystemUIModule {
 
     @SysUISingleton
     @Provides
@@ -101,9 +111,6 @@
     }
 
     @Binds
-    abstract EnhancedEstimates bindEnhancedEstimates(EnhancedEstimatesImpl enhancedEstimates);
-
-    @Binds
     abstract NotificationLockscreenUserManager bindNotificationLockscreenUserManager(
             NotificationLockscreenUserManagerImpl notificationLockscreenUserManager);
 
@@ -148,6 +155,7 @@
         return spC;
     }
 
+    /** */
     @Binds
     @SysUISingleton
     public abstract QSFactory bindQSFactory(QSFactoryImpl qsFactoryImpl);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 701972a..5d34a69 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -73,7 +73,7 @@
         SystemUIBinder.class,
         SystemUIModule.class,
         SystemUICoreStartableModule.class,
-        SystemUIDefaultModule.class})
+        ReferenceSystemUIModule.class})
 public interface SysUIComponent {
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index bbeb66c..535eff8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -100,8 +100,14 @@
 import dagger.Provides;
 
 /**
- * A dagger module for injecting components of System UI that are not overridden by the System UI
- * implementation.
+ * A dagger module for injecting components of System UI that are required by System UI.
+ *
+ * If your feature can be excluded, subclassed, or re-implemented by a variant of SystemUI, put
+ * your Dagger Module in {@link ReferenceSystemUIModule} and/or any variant modules that
+ * rely on the feature.
+ *
+ * Adding an entry in this file means that _all_ variants of SystemUI will receive that code. They
+ * may not appreciate that.
  */
 @Module(includes = {
             AppOpsModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 74044e2..1cc5df5 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -39,7 +39,6 @@
 import com.android.systemui.doze.DozeMachine.State;
 import com.android.systemui.doze.dagger.DozeScope;
 import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.Assert;
@@ -94,7 +93,6 @@
     private final AuthController mAuthController;
     private final DelayableExecutor mMainExecutor;
     private final KeyguardStateController mKeyguardStateController;
-    private final BatteryController mBatteryController;
     private final UiEventLogger mUiEventLogger;
     private final DevicePostureController mDevicePostureController;
 
@@ -186,8 +184,7 @@
             @Main DelayableExecutor mainExecutor,
             UiEventLogger uiEventLogger,
             KeyguardStateController keyguardStateController,
-            DevicePostureController devicePostureController,
-            BatteryController batteryController) {
+            DevicePostureController devicePostureController) {
         mContext = context;
         mDozeHost = dozeHost;
         mConfig = config;
@@ -208,7 +205,6 @@
         mMainExecutor = mainExecutor;
         mUiEventLogger = uiEventLogger;
         mKeyguardStateController = keyguardStateController;
-        mBatteryController = batteryController;
     }
     private final DevicePostureController.Callback mDevicePostureCallback =
             posture -> {
@@ -320,12 +316,7 @@
                     gentleWakeUp(pulseReason);
                 } else if (isPickup) {
                     if (shouldDropPickupEvent())  {
-                        mDozeLog.traceSensorEventDropped(
-                                pulseReason,
-                                "keyguardOccluded="
-                                        + mKeyguardStateController.isOccluded()
-                                        + " pluggedInWireless="
-                                        + mBatteryController.isPluggedInWireless());
+                        mDozeLog.traceSensorEventDropped(pulseReason, "keyguard occluded");
                         return;
                     }
                     gentleWakeUp(pulseReason);
@@ -356,7 +347,7 @@
     }
 
     private boolean shouldDropPickupEvent() {
-        return mKeyguardStateController.isOccluded() || mBatteryController.isPluggedInWireless();
+        return mKeyguardStateController.isOccluded();
     }
 
     private void gentleWakeUp(int reason) {
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index e16da89..25effec 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -89,7 +89,7 @@
 
     init {
         if (logcatEchoTracker.logInBackgroundThread && echoMessageQueue != null) {
-            thread(start = true, priority = Thread.NORM_PRIORITY) {
+            thread(start = true, name = "LogBuffer-$name", priority = Thread.NORM_PRIORITY) {
                 try {
                     while (true) {
                         echoToDesiredEndpoints(echoMessageQueue.take())
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 2fec499..8c2cb6d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -97,7 +97,7 @@
  * A view controller used for Media Playback.
  */
 public class MediaControlPanel {
-    private static final String TAG = "MediaControlPanel";
+    protected static final String TAG = "MediaControlPanel";
 
     private static final float DISABLED_ALPHA = 0.38f;
     private static final String EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME = "com.google"
@@ -106,7 +106,7 @@
             "com.google.android.apps.gsa.smartspace.extra.SMARTSPACE_INTENT";
     private static final String KEY_SMARTSPACE_ARTIST_NAME = "artist_name";
     private static final String KEY_SMARTSPACE_OPEN_IN_FOREGROUND = "KEY_OPEN_IN_FOREGROUND";
-    private static final String KEY_SMARTSPACE_APP_NAME = "KEY_SMARTSPACE_APP_NAME";
+    protected static final String KEY_SMARTSPACE_APP_NAME = "KEY_SMARTSPACE_APP_NAME";
 
     // Event types logged by smartspace
     private static final int SMARTSPACE_CARD_CLICK_EVENT = 760;
@@ -149,6 +149,7 @@
     private RecommendationViewHolder mRecommendationViewHolder;
     private String mKey;
     private MediaData mMediaData;
+    private SmartspaceMediaData mRecommendationData;
     private MediaViewController mMediaViewController;
     private MediaSession.Token mToken;
     private MediaController mController;
@@ -448,6 +449,7 @@
 
         bindOutputSwitcherChip(data);
         bindGutsMenuForPlayer(data);
+        bindPlayerContentDescription(data);
         bindScrubbingTime(data);
         bindActionButtons(data);
 
@@ -541,12 +543,6 @@
     }
 
     private boolean bindSongMetadata(MediaData data) {
-        // Accessibility label
-        mMediaViewHolder.getPlayer().setContentDescription(
-                mContext.getString(
-                        R.string.controls_media_playing_item_description,
-                        data.getSong(), data.getArtist(), data.getApp()));
-
         TextView titleText = mMediaViewHolder.getTitleText();
         TextView artistText = mMediaViewHolder.getArtistText();
         return mMetadataAnimationHandler.setNext(
@@ -568,6 +564,48 @@
             });
     }
 
+    // We may want to look into unifying this with bindRecommendationContentDescription if/when we
+    // do a refactor of this class.
+    private void bindPlayerContentDescription(MediaData data) {
+        if (mMediaViewHolder == null) {
+            return;
+        }
+
+        CharSequence contentDescription;
+        if (mMediaViewController.isGutsVisible()) {
+            contentDescription = mMediaViewHolder.getGutsViewHolder().getGutsText().getText();
+        } else if (data != null) {
+            contentDescription = mContext.getString(
+                    R.string.controls_media_playing_item_description,
+                    data.getSong(),
+                    data.getArtist(),
+                    data.getApp());
+        } else {
+            contentDescription = null;
+        }
+        mMediaViewHolder.getPlayer().setContentDescription(contentDescription);
+    }
+
+    private void bindRecommendationContentDescription(SmartspaceMediaData data) {
+        if (mRecommendationViewHolder == null) {
+            return;
+        }
+
+       CharSequence contentDescription;
+        if (mMediaViewController.isGutsVisible()) {
+            contentDescription =
+                    mRecommendationViewHolder.getGutsViewHolder().getGutsText().getText();
+        } else if (data != null) {
+            contentDescription = mContext.getString(
+                    R.string.controls_media_smartspace_rec_description,
+                    data.getAppName(mContext));
+        } else {
+            contentDescription = null;
+        }
+
+        mRecommendationViewHolder.getRecommendations().setContentDescription(contentDescription);
+    }
+
     private void bindArtworkAndColors(MediaData data, boolean updateBackground) {
         final int reqId = mArtworkNextBindRequestId++;
         if (updateBackground) {
@@ -636,6 +674,10 @@
                     mIsArtworkBound = isArtworkBound;
                 }
 
+                // Scrim bounds are set manually so it scales as expected
+                albumView.getForeground().setBounds(0, 0,
+                        Math.max(width, height), Math.max(width, height));
+
                 // Transition Colors to current color scheme
                 mColorSchemeTransition.updateColorScheme(colorScheme, mIsArtworkBound);
 
@@ -950,6 +992,7 @@
             return;
         }
 
+        mRecommendationData = data;
         mSmartspaceId = SmallHash.hash(data.getTargetId());
         mPackageName = data.getPackageName();
         mInstanceId = data.getInstanceId();
@@ -965,6 +1008,12 @@
             return;
         }
 
+        CharSequence appName = data.getAppName(mContext);
+        if (appName == null) {
+            Log.w(TAG, "Fail to get media recommendation's app name");
+            return;
+        }
+
         PackageManager packageManager = mContext.getPackageManager();
         // Set up media source app's logo.
         Drawable icon = packageManager.getApplicationIcon(applicationInfo);
@@ -972,28 +1021,11 @@
         headerLogoImageView.setImageDrawable(icon);
         fetchAndUpdateRecommendationColors(icon);
 
-        // Set up media source app's label text.
-        CharSequence appName = getAppName(data.getCardAction());
-        if (TextUtils.isEmpty(appName)) {
-            Intent launchIntent =
-                    packageManager.getLaunchIntentForPackage(data.getPackageName());
-            if (launchIntent != null) {
-                ActivityInfo launchActivity = launchIntent.resolveActivityInfo(packageManager, 0);
-                appName = launchActivity.loadLabel(packageManager);
-            } else {
-                Log.w(TAG, "Package " + data.getPackageName()
-                        +  " does not have a main launcher activity. Fallback to full app name");
-                appName = packageManager.getApplicationLabel(applicationInfo);
-            }
-        }
-
         // Set up media rec card's tap action if applicable.
         TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations();
         setSmartspaceRecItemOnClickListener(recommendationCard, data.getCardAction(),
                 /* interactedSubcardRank */ -1);
-        // Set up media rec card's accessibility label.
-        recommendationCard.setContentDescription(
-                mContext.getString(R.string.controls_media_smartspace_rec_description, appName));
+        bindRecommendationContentDescription(data);
 
         List<ImageView> mediaCoverItems = mRecommendationViewHolder.getMediaCoverItems();
         List<ViewGroup> mediaCoverContainers = mRecommendationViewHolder.getMediaCoverContainers();
@@ -1175,6 +1207,11 @@
             mRecommendationViewHolder.marquee(false, mMediaViewController.GUTS_ANIMATION_DURATION);
         }
         mMediaViewController.closeGuts(immediate);
+        if (mMediaViewHolder != null) {
+            bindPlayerContentDescription(mMediaData);
+        } else if (mRecommendationViewHolder != null) {
+            bindRecommendationContentDescription(mRecommendationData);
+        }
     }
 
     private void closeGuts() {
@@ -1188,6 +1225,11 @@
             mRecommendationViewHolder.marquee(true, mMediaViewController.GUTS_ANIMATION_DURATION);
         }
         mMediaViewController.openGuts();
+        if (mMediaViewHolder != null) {
+            bindPlayerContentDescription(mMediaData);
+        } else if (mRecommendationViewHolder != null) {
+            bindRecommendationContentDescription(mRecommendationData);
+        }
         mLogger.logLongPressOpen(mUid, mPackageName, mInstanceId);
     }
 
@@ -1302,17 +1344,6 @@
         });
     }
 
-    /** Returns the upstream app name if available. */
-    @Nullable
-    private String getAppName(SmartspaceAction action) {
-        if (action == null || action.getIntent() == null
-                || action.getIntent().getExtras() == null) {
-            return null;
-        }
-
-        return action.getIntent().getExtras().getString(KEY_SMARTSPACE_APP_NAME);
-    }
-
     /** Returns if the Smartspace action will open the activity in foreground. */
     private boolean shouldSmartspaceRecItemOpenInForeground(SmartspaceAction action) {
         if (action == null || action.getIntent() == null
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index f85078c..11ee657 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager
 import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManagerFactory
+import com.android.systemui.statusbar.policy.ConfigurationController
 import java.io.PrintWriter
 import java.util.concurrent.Executor
 import javax.inject.Inject
@@ -44,6 +45,7 @@
     private val localMediaManagerFactory: LocalMediaManagerFactory,
     private val mr2manager: MediaRouter2Manager,
     private val muteAwaitConnectionManagerFactory: MediaMuteAwaitConnectionManagerFactory,
+    private val configurationController: ConfigurationController,
     @Main private val fgExecutor: Executor,
     @Background private val bgExecutor: Executor,
     dumpManager: DumpManager
@@ -79,7 +81,7 @@
             oldEntry?.stop()
         }
         var entry = entries[key]
-        if (entry == null || entry?.token != data.token) {
+        if (entry == null || entry.token != data.token) {
             entry?.stop()
             if (data.device != null) {
                 // If we were already provided device info (e.g. from RCN), keep that and don't
@@ -118,10 +120,9 @@
     override fun dump(pw: PrintWriter, args: Array<String>) {
         with(pw) {
             println("MediaDeviceManager state:")
-            entries.forEach {
-                key, entry ->
+            entries.forEach { (key, entry) ->
                 println("  key=$key")
-                entry.dump(pw, args)
+                entry.dump(pw)
             }
         }
     }
@@ -165,6 +166,12 @@
         // expected to connect imminently, it should be displayed as the current device.
         private var aboutToConnectDeviceOverride: AboutToConnectDevice? = null
 
+        private val configListener = object : ConfigurationController.ConfigurationListener {
+            override fun onLocaleListChanged() {
+                updateCurrent()
+            }
+        }
+
         @AnyThread
         fun start() = bgExecutor.execute {
             localMediaManager.registerCallback(this)
@@ -174,6 +181,7 @@
             controller?.registerCallback(this)
             updateCurrent()
             started = true
+            configurationController.addCallback(configListener)
         }
 
         @AnyThread
@@ -183,9 +191,10 @@
             localMediaManager.stopScan()
             localMediaManager.unregisterCallback(this)
             muteAwaitConnectionManager?.stopListening()
+            configurationController.removeCallback(configListener)
         }
 
-        fun dump(pw: PrintWriter, args: Array<String>) {
+        fun dump(pw: PrintWriter) {
             val routingSession = controller?.let {
                 mr2manager.getRoutingSessionForMediaController(it)
             }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
index 1437c96..7eccb3b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
@@ -278,13 +278,14 @@
     /**
      * Apply squishFraction to a copy of viewState such that the cached version is untouched.
      */
-    private fun squishViewState(
+    @VisibleForTesting
+    internal fun squishViewState(
         viewState: TransitionViewState,
         squishFraction: Float
     ): TransitionViewState {
         val squishedViewState = viewState.copy()
         squishedViewState.height = (squishedViewState.height * squishFraction).toInt()
-        val albumArtViewState = viewState.widgetStates.get(R.id.album_art)
+        val albumArtViewState = squishedViewState.widgetStates.get(R.id.album_art)
         if (albumArtViewState != null) {
             albumArtViewState.height = squishedViewState.height
         }
@@ -317,6 +318,7 @@
         if (transitionLayout == null) {
             return null
         }
+
         // Not cached. Let's create a new measurement
         if (state.expansion == 0.0f || state.expansion == 1.0f) {
             result = transitionLayout!!.calculateViewState(
diff --git a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
index 50a96f6..c8f17d9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt
@@ -17,8 +17,13 @@
 package com.android.systemui.media
 
 import android.app.smartspace.SmartspaceAction
+import android.content.Context
 import android.content.Intent
+import android.content.pm.PackageManager
+import android.text.TextUtils
+import android.util.Log
 import com.android.internal.logging.InstanceId
+import com.android.systemui.media.MediaControlPanel.KEY_SMARTSPACE_APP_NAME
 
 /** State of a Smartspace media recommendations view. */
 data class SmartspaceMediaData(
@@ -67,6 +72,32 @@
      * Returns the list of [recommendations] that have valid data.
      */
     fun getValidRecommendations() = recommendations.filter { it.icon != null }
+
+    /** Returns the upstream app name if available. */
+    fun getAppName(context: Context): CharSequence? {
+        val nameFromAction = cardAction?.intent?.extras?.getString(KEY_SMARTSPACE_APP_NAME)
+        if (!TextUtils.isEmpty(nameFromAction)) {
+            return nameFromAction
+        }
+
+        val packageManager = context.packageManager
+        packageManager.getLaunchIntentForPackage(packageName)?.let {
+            val launchActivity = it.resolveActivityInfo(packageManager, 0)
+            return launchActivity.loadLabel(packageManager)
+        }
+
+        Log.w(
+            TAG,
+            "Package $packageName does not have a main launcher activity. " +
+                    "Fallback to full app name")
+        return try {
+            val applicationInfo = packageManager.getApplicationInfo(packageName,  /* flags= */ 0)
+            packageManager.getApplicationLabel(applicationInfo)
+        } catch (e: PackageManager.NameNotFoundException) {
+            null
+        }
+    }
 }
 
 const val NUM_REQUIRED_RECOMMENDATIONS = 3
+private val TAG = SmartspaceMediaData::class.simpleName!!
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index a397f32..58a35ff 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -143,6 +143,7 @@
             if (mController.isTransferring()) {
                 if (device.getState() == MediaDeviceState.STATE_CONNECTING
                         && !mController.hasAdjustVolumeUserRestriction()) {
+                    setUpDeviceIcon(device);
                     mProgressBar.getIndeterminateDrawable().setColorFilter(
                             new PorterDuffColorFilter(
                                     mController.getColorItemContent(),
@@ -151,11 +152,13 @@
                             false /* showSeekBar*/,
                             true /* showProgressBar */, false /* showStatus */);
                 } else {
+                    setUpDeviceIcon(device);
                     setSingleLineLayout(getItemTitle(device), false /* bFocused */);
                 }
             } else {
                 // Set different layout for each device
                 if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
+                    setUpDeviceIcon(device);
                     mTitleText.setAlpha(DEVICE_CONNECTED_ALPHA);
                     mTitleIcon.setAlpha(DEVICE_CONNECTED_ALPHA);
                     mStatusIcon.setImageDrawable(
@@ -167,6 +170,7 @@
                     mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
                     mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
                 } else if (device.getState() == MediaDeviceState.STATE_GROUPING) {
+                    setUpDeviceIcon(device);
                     mProgressBar.getIndeterminateDrawable().setColorFilter(
                             new PorterDuffColorFilter(
                                     mController.getColorItemContent(),
@@ -176,7 +180,12 @@
                             true /* showProgressBar */, false /* showStatus */);
                 } else if (mController.getSelectedMediaDevice().size() > 1
                         && isDeviceIncluded(mController.getSelectedMediaDevice(), device)) {
+                    boolean isDeviceDeselectable = isDeviceIncluded(
+                            mController.getDeselectableMediaDevice(), device);
                     mTitleText.setTextColor(mController.getColorItemContent());
+                    mTitleIcon.setImageDrawable(
+                            mContext.getDrawable(R.drawable.media_output_icon_volume));
+                    mTitleIcon.setColorFilter(mController.getColorItemContent());
                     setSingleLineLayout(getItemTitle(device), true /* bFocused */,
                             true /* showSeekBar */,
                             false /* showProgressBar */, false /* showStatus */);
@@ -184,13 +193,20 @@
                     mCheckBox.setOnCheckedChangeListener(null);
                     mCheckBox.setVisibility(View.VISIBLE);
                     mCheckBox.setChecked(true);
-                    mCheckBox.setOnCheckedChangeListener(
-                            (buttonView, isChecked) -> onGroupActionTriggered(false, device));
+                    mCheckBox.setOnCheckedChangeListener(isDeviceDeselectable
+                            ? (buttonView, isChecked) -> onGroupActionTriggered(false, device)
+                            : null);
+                    mCheckBox.setEnabled(isDeviceDeselectable);
+                    mCheckBox.setAlpha(
+                            isDeviceDeselectable ? DEVICE_CONNECTED_ALPHA
+                                    : DEVICE_DISCONNECTED_ALPHA
+                    );
                     setCheckBoxColor(mCheckBox, mController.getColorItemContent());
                     initSeekbar(device, isCurrentSeekbarInvisible);
                     mEndTouchArea.setVisibility(View.VISIBLE);
                     mEndTouchArea.setOnClickListener(null);
-                    mEndTouchArea.setOnClickListener((v) -> mCheckBox.performClick());
+                    mEndTouchArea.setOnClickListener(
+                            isDeviceDeselectable ? (v) -> mCheckBox.performClick() : null);
                     mEndTouchArea.setImportantForAccessibility(
                             View.IMPORTANT_FOR_ACCESSIBILITY_YES);
                     setUpContentDescriptionForView(mEndTouchArea, true, device);
@@ -198,6 +214,9 @@
                     mStatusIcon.setImageDrawable(
                             mContext.getDrawable(R.drawable.media_output_status_check));
                     mStatusIcon.setColorFilter(mController.getColorItemContent());
+                    mTitleIcon.setImageDrawable(
+                            mContext.getDrawable(R.drawable.media_output_icon_volume));
+                    mTitleIcon.setColorFilter(mController.getColorItemContent());
                     mTitleText.setTextColor(mController.getColorItemContent());
                     setSingleLineLayout(getItemTitle(device), true /* bFocused */,
                             true /* showSeekBar */,
@@ -206,6 +225,7 @@
                     setUpContentDescriptionForView(mContainerLayout, false, device);
                     mCurrentActivePosition = position;
                 } else if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
+                    setUpDeviceIcon(device);
                     mCheckBox.setOnCheckedChangeListener(null);
                     mCheckBox.setVisibility(View.VISIBLE);
                     mCheckBox.setChecked(false);
@@ -218,6 +238,7 @@
                             false /* showSeekBar */,
                             false /* showProgressBar */, false /* showStatus */);
                 } else {
+                    setUpDeviceIcon(device);
                     setSingleLineLayout(getItemTitle(device), false /* bFocused */);
                     mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 5c2cc0b..b407e76f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -166,15 +166,6 @@
 
         void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) {
             mDeviceId = device.getId();
-            ThreadUtils.postOnBackgroundThread(() -> {
-                Icon icon = mController.getDeviceIconCompat(device).toIcon(mContext);
-                ThreadUtils.postOnMainThread(() -> {
-                    if (!TextUtils.equals(mDeviceId, device.getId())) {
-                        return;
-                    }
-                    mTitleIcon.setImageIcon(icon);
-                });
-            });
         }
 
         abstract void onBind(int customizedItem, boolean topMargin, boolean bottomMargin);
@@ -414,5 +405,17 @@
             mSeekBar.setEnabled(false);
             mSeekBar.setOnTouchListener((v, event) -> true);
         }
+
+        protected void setUpDeviceIcon(MediaDevice device) {
+            ThreadUtils.postOnBackgroundThread(() -> {
+                Icon icon = mController.getDeviceIconCompat(device).toIcon(mContext);
+                ThreadUtils.postOnMainThread(() -> {
+                    if (!TextUtils.equals(mDeviceId, device.getId())) {
+                        return;
+                    }
+                    mTitleIcon.setImageIcon(icon);
+                });
+            });
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/README.md b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/README.md
index 1145891..6379960 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/README.md
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/README.md
@@ -1,11 +1,43 @@
 # Media Tap-To-Transfer
 
+## Overview
 This package (and child packages) include code for the media tap-to-transfer feature, which
 allows users to easily transfer playing media between devices.
 
-In media transfer, there are two devices: the *sender* and the *receiver*. The sender device will
-start and stop media casts to the receiver device. On both devices, System UI will display a chip
-informing the user about the media cast occurring.
+In media transfer, there are two devices: the **sender** and the **receiver**. The sender device
+will start and stop media casts to the receiver device. On both devices, System UI will display a
+chip informing the user about the media cast occurring.
 
-This package is structured so that the sender code is in the sender package, the receiver code is
-in the receiver package, and code that's shared between them is in the common package.
+**Important**: System UI is **not responsible** for performing the media transfer. System UI
+**only** displays an informational chip; external clients are responsible for performing the media
+transfer and informing System UI about the transfer status.
+
+## Information flow
+External clients notify System UI about the transfer status by calling `@SystemApi`s in
+`StatusBarManager`. For the sender device, use the `updateMediaTapToTransferSenderDisplay` API; for
+the receiver, use the `updateMediaTapToTransferReceiverDisplay` API. The APIs eventually flow into
+SystemUI's `CommandQueue`, which then notifies callbacks about the new state.
+`MediaTttChipControllerSender` implements the sender callback, and `MediaTttChipControllerReceiver`
+implements the receiver callback. These controllers will then show or hide the tap-to-transfer chip
+(depending on what information was sent in the API).
+
+## Architecture
+This package is structured so that the sender code is in the `sender` package, the receiver code is
+in the `receiver` package, and code that's shared between them is in the `common` package.
+
+* The `ChipStateSender` and `ChipStateReceiver` classes are enums that describe all the possible
+  transfer states (transfer started, transfer succeeded, etc.) and include relevant parameters for
+  each state.
+* The `ChipSenderInfo` and `ChipReceiverInfo` classes are simple data classes that contain all the
+  information needed to display a chip. They include the transfer state, information about the media
+  being transferred, etc.
+* The `MediaTttChipControllerSender` and `MediaTttChipControllerReceiver` classes are responsible
+  for showing or hiding the chip and updating the chip view based on information from the
+  `ChipInfo`. `MediaTttChipControllerCommon` has all the common logic for adding and removing the
+  view to the window and also includes any display logic that can be shared between the sender and
+  receiver. The sender and receiver controller subclasses have the display logic that's specific to
+  just the sender or just the receiver.
+
+## Testing
+If you want to test out the tap-to-transfer chip without using the `@SystemApi`s, you can use adb
+commands instead. Refer to `MediaTttCommandLineHelper` for information about adb commands.
diff --git a/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
index 8b8941a..3709a86 100644
--- a/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
+++ b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.power.dagger;
 
+import com.android.systemui.power.EnhancedEstimates;
+import com.android.systemui.power.EnhancedEstimatesImpl;
 import com.android.systemui.power.PowerNotificationWarnings;
 import com.android.systemui.power.PowerUI;
 
@@ -28,5 +30,9 @@
 public interface PowerModule {
     /** */
     @Binds
+    EnhancedEstimates bindEnhancedEstimates(EnhancedEstimatesImpl enhancedEstimates);
+
+    /** */
+    @Binds
     PowerUI.WarningsUI provideWarningsUi(PowerNotificationWarnings controllerImpl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 6af6e36..ccfcaa6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -149,7 +149,6 @@
     private ImageView mActionsContainerBackground;
     private HorizontalScrollView mActionsContainer;
     private LinearLayout mActionsView;
-    private ImageView mBackgroundProtection;
     private FrameLayout mDismissButton;
     private OverlayActionChip mShareChip;
     private OverlayActionChip mEditChip;
@@ -345,8 +344,6 @@
                 R.id.actions_container_background));
         mActionsContainer = requireNonNull(findViewById(R.id.actions_container));
         mActionsView = requireNonNull(findViewById(R.id.screenshot_actions));
-        mBackgroundProtection = requireNonNull(
-                findViewById(R.id.screenshot_actions_background));
         mDismissButton = requireNonNull(findViewById(R.id.screenshot_dismiss_button));
         mScrollablePreview = requireNonNull(findViewById(R.id.screenshot_scrollable_preview));
         mScreenshotFlash = requireNonNull(findViewById(R.id.screenshot_flash));
@@ -394,14 +391,6 @@
                 }
                 mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED, 0,
                         mPackageName);
-                animator.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationStart(Animator animation) {
-                        super.onAnimationStart(animation);
-                        mBackgroundProtection.animate()
-                                .alpha(0).setDuration(animation.getDuration()).start();
-                    }
-                });
             }
 
             @Override
@@ -704,7 +693,6 @@
 
         animator.addUpdateListener(animation -> {
             float t = animation.getAnimatedFraction();
-            mBackgroundProtection.setAlpha(t);
             float containerAlpha = t < alphaFraction ? t / alphaFraction : 1;
             mActionsContainer.setAlpha(containerAlpha);
             mActionsContainerBackground.setAlpha(containerAlpha);
@@ -910,7 +898,6 @@
         }
         mDismissButton.setVisibility(View.GONE);
         mActionsContainer.setVisibility(View.GONE);
-        mBackgroundProtection.setVisibility(View.GONE);
         // set these invisible, but not gone, so that the views are laid out correctly
         mActionsContainerBackground.setVisibility(View.INVISIBLE);
         mScreenshotPreviewBorder.setVisibility(View.INVISIBLE);
@@ -932,7 +919,6 @@
             mDismissButton.setVisibility(View.VISIBLE);
         }
         mActionsContainer.setVisibility(View.VISIBLE);
-        mBackgroundProtection.setVisibility(View.VISIBLE);
         mActionsContainerBackground.setVisibility(View.VISIBLE);
         mScreenshotPreviewBorder.setVisibility(View.VISIBLE);
         mScreenshotPreview.setVisibility(View.VISIBLE);
@@ -969,7 +955,6 @@
         mPendingSharedTransition = false;
         mActionsContainerBackground.setVisibility(View.GONE);
         mActionsContainer.setVisibility(View.GONE);
-        mBackgroundProtection.setAlpha(0f);
         mDismissButton.setVisibility(View.GONE);
         mScrollingScrim.setVisibility(View.GONE);
         mScrollablePreview.setVisibility(View.GONE);
@@ -1016,7 +1001,6 @@
             mDismissButton.setAlpha(alpha);
             mActionsContainerBackground.setAlpha(alpha);
             mActionsContainer.setAlpha(alpha);
-            mBackgroundProtection.setAlpha(alpha);
             mScreenshotPreviewBorder.setAlpha(alpha);
         });
         alphaAnim.setDuration(600);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index 6b79680..f0b221d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -160,8 +160,7 @@
         val channels = groupList
                 .flatMap { group ->
                     group.channels.asSequence().filterNot { channel ->
-                        channel.isImportanceLockedByOEM ||
-                                channel.importance == IMPORTANCE_NONE ||
+                        channel.importance == IMPORTANCE_NONE ||
                                 channel.isImportanceLockedByCriticalDeviceFunction
                     }
                 }
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 0ae3653..dff8c47 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
@@ -376,44 +376,24 @@
      * calls.
      */
     private static Boolean isSystemNotification(Context context, StatusBarNotification sbn) {
-        // TODO (b/194833441): clean up before launch
-        if (Settings.Secure.getIntForUser(context.getContentResolver(),
-                Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 0, USER_SYSTEM) == 1) {
-            INotificationManager iNm = INotificationManager.Stub.asInterface(
-                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        INotificationManager iNm = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
 
-            boolean isSystem = false;
-            try {
-                isSystem = iNm.isPermissionFixed(sbn.getPackageName(), sbn.getUserId());
-            } catch (RemoteException e) {
-                Log.e(TAG, "cannot reach NMS");
-            }
-            RoleManager rm = context.getSystemService(RoleManager.class);
-            List<String> fixedRoleHolders = new ArrayList<>();
-            fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_DIALER));
-            fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_EMERGENCY));
-            if (fixedRoleHolders.contains(sbn.getPackageName())) {
-                isSystem = true;
-            }
-
-            return isSystem;
-        } else {
-            PackageManager packageManager = CentralSurfaces.getPackageManagerForUser(
-                    context, sbn.getUser().getIdentifier());
-            Boolean isSystemNotification = null;
-
-            try {
-                PackageInfo packageInfo = packageManager.getPackageInfo(
-                        sbn.getPackageName(), PackageManager.GET_SIGNATURES);
-
-                isSystemNotification =
-                        com.android.settingslib.Utils.isSystemPackage(
-                                context.getResources(), packageManager, packageInfo);
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.e(TAG, "cacheIsSystemNotification: Could not find package info");
-            }
-            return isSystemNotification;
+        boolean isSystem = false;
+        try {
+            isSystem = iNm.isPermissionFixed(sbn.getPackageName(), sbn.getUserId());
+        } catch (RemoteException e) {
+            Log.e(TAG, "cannot reach NMS");
         }
+        RoleManager rm = context.getSystemService(RoleManager.class);
+        List<String> fixedRoleHolders = new ArrayList<>();
+        fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_DIALER));
+        fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_EMERGENCY));
+        if (fixedRoleHolders.contains(sbn.getPackageName())) {
+            isSystem = true;
+        }
+
+        return isSystem;
     }
 
     public NotificationContentView[] getLayouts() {
@@ -567,9 +547,7 @@
             mEntry.mIsSystemNotification = isSystemNotification(mContext, mEntry.getSbn());
         }
 
-        // TODO (b/194833441): remove when we've migrated to permission
-        boolean isNonblockable = mEntry.getChannel().isImportanceLockedByOEM()
-                || mEntry.getChannel().isImportanceLockedByCriticalDeviceFunction();
+        boolean isNonblockable = mEntry.getChannel().isImportanceLockedByCriticalDeviceFunction();
 
         if (!isNonblockable && mEntry != null && mEntry.mIsSystemNotification != null) {
             if (mEntry.mIsSystemNotification) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 83970dc..629aa03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -220,7 +220,7 @@
         }
     }
 
-    public float getMinStackScrollerPadding() {
+    public float getLockscreenMinStackScrollerPadding() {
         if (mBypassEnabled) {
             return mUnlockedStackScrollerPadding;
         } else if (mIsSplitShade) {
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 985df42..77b9efa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -24,6 +24,7 @@
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
 import static com.android.keyguard.KeyguardClockSwitch.LARGE;
 import static com.android.keyguard.KeyguardClockSwitch.SMALL;
+import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE;
 import static com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE;
 import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
@@ -325,6 +326,11 @@
     private boolean mShouldUseSplitNotificationShade;
     // The bottom padding reserved for elements of the keyguard measuring notifications
     private float mKeyguardNotificationBottomPadding;
+    /**
+     * The top padding from where notification should start in lockscreen.
+     * Should be static also during animations and should match the Y of the first notification.
+     */
+    private float mKeyguardNotificationTopPadding;
     // Current max allowed keyguard notifications determined by measuring the panel
     private int mMaxAllowedKeyguardNotifications;
 
@@ -936,7 +942,7 @@
                                         // the launcher icons animation starts, so use that as our
                                         // duration.
                                         .setDuration(unlockAnimationStartDelay)
-                                        .setInterpolator(EMPHASIZED_DECELERATE)
+                                        .setInterpolator(EMPHASIZED_ACCELERATE)
                                         .withEndAction(() -> {
                                             instantCollapse();
                                             mView.setAlpha(1f);
@@ -1513,7 +1519,10 @@
      */
     @VisibleForTesting
     float getSpaceForLockscreenNotifications() {
-        float topPadding = mNotificationStackScrollLayoutController.getTopPadding();
+        float staticTopPadding = mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding()
+                // getMinStackScrollerPadding is from the top of the screen,
+                // but we need it from the top of the NSSL.
+                - mNotificationStackScrollLayoutController.getTop();
 
         // Space between bottom of notifications and top of lock icon or udfps background.
         float lockIconPadding = mLockIconViewController.getTop();
@@ -1525,11 +1534,15 @@
 
         float bottomPadding = Math.max(lockIconPadding,
                 Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding));
-        mKeyguardNotificationBottomPadding = bottomPadding;
 
+        mKeyguardNotificationBottomPadding = bottomPadding;
+        mKeyguardNotificationTopPadding = staticTopPadding;
+
+        // To debug the available space, enable debug lines in this class. If you change how the
+        // available space is calculated, please also update those lines.
         float availableSpace =
                 mNotificationStackScrollLayoutController.getHeight()
-                        - topPadding
+                        - staticTopPadding
                         - bottomPadding;
         return availableSpace;
     }
@@ -3197,6 +3210,8 @@
         updateTrackingHeadsUp(null);
         mExpandingFromHeadsUp = false;
         setPanelScrimMinFraction(0.0f);
+        // Reset status bar alpha so alpha can be calculated upon updating view state.
+        setKeyguardStatusBarAlpha(-1f);
     }
 
     private void updateTrackingHeadsUp(@Nullable ExpandableNotificationRow pickedChild) {
@@ -4923,6 +4938,20 @@
             drawDebugInfo(canvas, (int) mLockIconViewController.getTop(), Color.GRAY,
                     "mLockIconViewController.getTop()");
 
+            if (mKeyguardShowing) {
+                // Notifications have the space between those two lines.
+                drawDebugInfo(canvas,
+                        mNotificationStackScrollLayoutController.getTop() +
+                                (int) mKeyguardNotificationTopPadding,
+                        Color.RED,
+                        "NSSL.getTop() + mKeyguardNotificationTopPadding");
+
+                drawDebugInfo(canvas, mNotificationStackScrollLayoutController.getBottom() -
+                                (int) mKeyguardNotificationBottomPadding,
+                        Color.RED,
+                        "NSSL.getBottom() - mKeyguardNotificationBottomPadding");
+            }
+
             mDebugPaint.setColor(Color.CYAN);
             canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(),
                     mNotificationStackScrollLayoutController.getTopPadding(), mDebugPaint);
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index b7f90a4..4685c14 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -38,7 +38,6 @@
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.power.EnhancedEstimates;
-import com.android.systemui.power.EnhancedEstimatesImpl;
 import com.android.systemui.power.dagger.PowerModule;
 import com.android.systemui.qs.dagger.QSModule;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
@@ -102,9 +101,6 @@
     }
 
     @Binds
-    abstract EnhancedEstimates bindEnhancedEstimates(EnhancedEstimatesImpl enhancedEstimates);
-
-    @Binds
     abstract NotificationLockscreenUserManager bindNotificationLockscreenUserManager(
             NotificationLockscreenUserManagerImpl notificationLockscreenUserManager);
 
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
index 3329eab..ad73491 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
@@ -71,6 +71,11 @@
     private var popupMenu: UserSwitcherPopupMenu? = null
     private lateinit var addButton: View
     private var addUserRecords = mutableListOf<UserRecord>()
+    private val userSwitchedCallback: UserTracker.Callback = object : UserTracker.Callback {
+        override fun onUserChanged(newUser: Int, userContext: Context) {
+            finish()
+        }
+    }
     // When the add users options become available, insert another option to manage users
     private val manageUserRecord = UserRecord(
         null /* info */,
@@ -215,11 +220,7 @@
         initBroadcastReceiver()
 
         parent.post { buildUserViews() }
-        userTracker.addCallback(object : UserTracker.Callback {
-            override fun onUserChanged(newUser: Int, userContext: Context) {
-                finish()
-            }
-        }, mainExecutor)
+        userTracker.addCallback(userSwitchedCallback, mainExecutor)
     }
 
     private fun showPopupMenu() {
@@ -340,6 +341,7 @@
         super.onDestroy()
 
         broadcastDispatcher.unregisterReceiver(broadcastReceiver)
+        userTracker.removeCallback(userSwitchedCallback)
     }
 
     private fun initBroadcastReceiver() {
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index acff871..ebdddbf 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -180,7 +180,7 @@
         int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size);
         GetWalletCardsRequest request =
                 new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, /* maxCards= */ 1);
-        mQuickAccessWalletClient.getWalletCards(mExecutor, request, cardsRetriever);
+        mQuickAccessWalletClient.getWalletCards(mCallbackExecutor, request, cardsRetriever);
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index 7476490..39cc34b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -18,6 +18,7 @@
 
 import android.content.ContentResolver
 import android.database.ContentObserver
+import android.hardware.biometrics.BiometricFaceConstants
 import android.net.Uri
 import android.os.Handler
 import android.os.UserHandle
@@ -44,18 +45,20 @@
     private val fakeWakeUri = Uri.Builder().appendPath("wake").build()
     private val fakeUnlockIntentUri = Uri.Builder().appendPath("unlock-intent").build()
     private val fakeBioFailUri = Uri.Builder().appendPath("bio-fail").build()
+    private val fakeFaceErrorsUri = Uri.Builder().appendPath("face-errors").build()
+    private val fakeFaceAcquiredUri = Uri.Builder().appendPath("face-acquired").build()
+    private val fakeUnlockIntentBioEnroll = Uri.Builder().appendPath("unlock-intent-bio").build()
 
     @Mock
     private lateinit var secureSettings: SecureSettings
-
     @Mock
     private lateinit var contentResolver: ContentResolver
-
     @Mock
     private lateinit var handler: Handler
-
     @Mock
     private lateinit var dumpManager: DumpManager
+    @Mock
+    private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
 
     @Captor
     private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
@@ -72,6 +75,13 @@
                 .thenReturn(fakeUnlockIntentUri)
         `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
                 .thenReturn(fakeBioFailUri)
+        `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS))
+                .thenReturn(fakeFaceErrorsUri)
+        `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
+                .thenReturn(fakeFaceAcquiredUri)
+        `when`(secureSettings.getUriFor(
+                Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED))
+                .thenReturn(fakeUnlockIntentBioEnroll)
 
         activeUnlockConfig = ActiveUnlockConfig(
                 handler,
@@ -99,12 +109,7 @@
         // WHEN unlock on wake is allowed
         `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
                 0, 0)).thenReturn(1)
-        settingsObserverCaptor.value.onChange(
-                false,
-                listOf(fakeWakeUri),
-                0,
-                0
-        )
+        updateSetting(fakeWakeUri)
 
         // THEN active unlock triggers allowed on: wake, unlock-intent, and biometric failure
         assertTrue(
@@ -134,12 +139,7 @@
         // WHEN unlock on biometric failed is allowed
         `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
                 0, 0)).thenReturn(1)
-        settingsObserverCaptor.value.onChange(
-                false,
-                listOf(fakeUnlockIntentUri),
-                0,
-                0
-        )
+        updateSetting(fakeUnlockIntentUri)
 
         // THEN active unlock triggers allowed on: biometric failure ONLY
         assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -154,19 +154,19 @@
     fun testOnBioFailSettingChanged() {
         verifyRegisterSettingObserver()
 
-        // GIVEN no active unlock settings enabled
+        // GIVEN no active unlock settings enabled and triggering unlock intent on biometric
+        // enrollment setting is disabled (empty string is disabled, null would use the default)
+        `when`(secureSettings.getStringForUser(
+                Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+                0)).thenReturn("")
+        updateSetting(fakeUnlockIntentBioEnroll)
         assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
                 ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
 
         // WHEN unlock on biometric failed is allowed
         `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
                 0, 0)).thenReturn(1)
-        settingsObserverCaptor.value.onChange(
-                false,
-                listOf(fakeBioFailUri),
-                0,
-                0
-        )
+        updateSetting(fakeBioFailUri)
 
         // THEN active unlock triggers allowed on: biometric failure ONLY
         assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -177,21 +177,146 @@
                 ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
     }
 
+    @Test
+    fun testFaceErrorSettingsChanged() {
+        verifyRegisterSettingObserver()
+
+        // GIVEN unlock on biometric fail
+        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
+                0, 0)).thenReturn(1)
+        updateSetting(fakeBioFailUri)
+
+        // WHEN face error timeout (3), allow trigger active unlock
+        `when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS,
+                0)).thenReturn("3")
+        updateSetting(fakeFaceAcquiredUri)
+
+        // THEN active unlock triggers allowed on error TIMEOUT
+        assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
+                BiometricFaceConstants.FACE_ERROR_TIMEOUT))
+
+        assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
+                BiometricFaceConstants.FACE_ERROR_CANCELED))
+    }
+
+    @Test
+    fun testFaceAcquiredSettingsChanged() {
+        verifyRegisterSettingObserver()
+
+        // GIVEN unlock on biometric fail
+        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
+                0, 0)).thenReturn(1)
+        updateSetting(fakeBioFailUri)
+
+        // WHEN face acquiredMsg DARK_GLASSESand MOUTH_COVERING are allowed to trigger
+        `when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
+                0)).thenReturn(
+                "${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" +
+                        "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}")
+        updateSetting(fakeFaceAcquiredUri)
+
+        // THEN active unlock triggers allowed on acquired messages DARK_GLASSES & MOUTH_COVERING
+        assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+                BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED))
+        assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+                BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED))
+
+        assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+                BiometricFaceConstants.FACE_ACQUIRED_GOOD))
+        assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
+                BiometricFaceConstants.FACE_ACQUIRED_NOT_DETECTED))
+    }
+
+    @Test
+    fun testTriggerOnUnlockIntentWhenBiometricEnrolledNone() {
+        verifyRegisterSettingObserver()
+
+        // GIVEN unlock on biometric fail
+        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
+                0, 0)).thenReturn(1)
+        updateSetting(fakeBioFailUri)
+
+        // GIVEN fingerprint and face are NOT enrolled
+        activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
+        `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(false)
+        `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false)
+
+        // WHEN unlock intent is allowed when NO biometrics are enrolled (0)
+        `when`(secureSettings.getStringForUser(
+                Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+                0)).thenReturn("${ActiveUnlockConfig.BIOMETRIC_TYPE_NONE}")
+        updateSetting(fakeUnlockIntentBioEnroll)
+
+        // THEN active unlock triggers allowed on unlock intent
+        assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
+    }
+
+    @Test
+    fun testTriggerOnUnlockIntentWhenBiometricEnrolledFingerprintOrFaceOnly() {
+        verifyRegisterSettingObserver()
+
+        // GIVEN unlock on biometric fail
+        `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
+                0, 0)).thenReturn(1)
+        updateSetting(fakeBioFailUri)
+
+        // GIVEN fingerprint and face are both enrolled
+        activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
+        `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true)
+        `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true)
+
+        // WHEN unlock intent is allowed when ONLY fingerprint is enrolled or NO biometircs
+        // are enrolled
+        `when`(secureSettings.getStringForUser(
+                Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
+                0)).thenReturn(
+                "${ActiveUnlockConfig.BIOMETRIC_TYPE_ANY_FACE}" +
+                        "|${ActiveUnlockConfig.BIOMETRIC_TYPE_ANY_FINGERPRINT}")
+        updateSetting(fakeUnlockIntentBioEnroll)
+
+        // THEN active unlock triggers NOT allowed on unlock intent
+        assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
+
+        // WHEN fingerprint ONLY enrolled
+        `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(false)
+        `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true)
+
+        // THEN active unlock triggers allowed on unlock intent
+        assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
+
+        // WHEN face ONLY enrolled
+        `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true)
+        `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false)
+
+        // THEN active unlock triggers allowed on unlock intent
+        assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
+                ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
+    }
+
+    private fun updateSetting(uri: Uri) {
+        settingsObserverCaptor.value.onChange(
+                false,
+                listOf(uri),
+                0,
+                0 /* flags */
+        )
+    }
+
     private fun verifyRegisterSettingObserver() {
-        verify(contentResolver).registerContentObserver(
-                eq(fakeWakeUri),
-                eq(false),
-                capture(settingsObserverCaptor),
-                eq(UserHandle.USER_ALL))
+        verifyRegisterSettingObserver(fakeWakeUri)
+        verifyRegisterSettingObserver(fakeUnlockIntentUri)
+        verifyRegisterSettingObserver(fakeBioFailUri)
+        verifyRegisterSettingObserver(fakeFaceErrorsUri)
+        verifyRegisterSettingObserver(fakeFaceAcquiredUri)
+        verifyRegisterSettingObserver(fakeUnlockIntentBioEnroll)
+    }
 
+    private fun verifyRegisterSettingObserver(uri: Uri) {
         verify(contentResolver).registerContentObserver(
-                eq(fakeUnlockIntentUri),
-                eq(false),
-                capture(settingsObserverCaptor),
-                eq(UserHandle.USER_ALL))
-
-        verify(contentResolver).registerContentObserver(
-                eq(fakeBioFailUri),
+                eq(uri),
                 eq(false),
                 capture(settingsObserverCaptor),
                 eq(UserHandle.USER_ALL))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index b87a7e7..0fdd905 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -168,7 +168,8 @@
 
         mController.onViewAttached();
         verify(mView, atLeast(1)).setPauseAuth(true);
-        verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount, true);
+        verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount,
+                UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN);
     }
 
     @Test
@@ -195,7 +196,8 @@
         final float eased = .65f;
         mStatusBarStateListener.onDozeAmountChanged(linear, eased);
 
-        verify(mView).onDozeAmountChanged(linear, eased, true);
+        verify(mView).onDozeAmountChanged(linear, eased,
+                UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index ae387e8..4eeb4ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -45,7 +45,6 @@
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.doze.DozeTriggers.DozingUpdateUiEvent;
 import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -91,8 +90,6 @@
     private KeyguardStateController mKeyguardStateController;
     @Mock
     private DevicePostureController mDevicePostureController;
-    @Mock
-    private BatteryController mBatteryController;
 
     private DozeTriggers mTriggers;
     private FakeSensorManager mSensors;
@@ -125,7 +122,7 @@
                 asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
                 mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(),
                 mAuthController, mExecutor, mUiEventLogger, mKeyguardStateController,
-                mDevicePostureController, mBatteryController);
+                mDevicePostureController);
         mTriggers.setDozeMachine(mMachine);
         waitForSensorManager();
     }
@@ -233,9 +230,7 @@
         when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
 
         // WHEN the pick up gesture is triggered and keyguard isn't occluded
-        // and device isn't on a wireless charger
         when(mKeyguardStateController.isOccluded()).thenReturn(false);
-        when(mBatteryController.isPluggedInWireless()).thenReturn(false);
         mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null);
 
         // THEN wakeup
@@ -249,22 +244,6 @@
 
         // WHEN the pick up gesture is triggered and keyguard IS occluded
         when(mKeyguardStateController.isOccluded()).thenReturn(true);
-        when(mBatteryController.isPluggedInWireless()).thenReturn(false);
-        mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null);
-
-        // THEN never wakeup
-        verify(mMachine, never()).wakeUp();
-    }
-
-    @Test
-    public void testPickupGestureWirelessCharger() {
-        // GIVEN device is in doze (screen blank, but running doze sensors)
-        when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
-
-        // WHEN the pick up gesture is triggered
-        // and device IS on a wireless charger
-        when(mKeyguardStateController.isOccluded()).thenReturn(false);
-        when(mBatteryController.isPluggedInWireless()).thenReturn(true);
         mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null);
 
         // THEN never wakeup
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index 5ff316e..c532ed5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -34,8 +34,6 @@
 import android.graphics.Rect;
 import android.graphics.drawable.AnimatedStateListDrawable;
 import android.hardware.biometrics.BiometricSourceType;
-import android.hardware.biometrics.SensorLocationInternal;
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.Pair;
@@ -77,9 +75,6 @@
 import org.mockito.MockitoSession;
 import org.mockito.quality.Strictness;
 
-import java.util.ArrayList;
-import java.util.List;
-
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -182,7 +177,7 @@
     @Test
     public void testUpdateFingerprintLocationOnInit() {
         // GIVEN fp sensor location is available pre-attached
-        Pair<Integer, PointF> udfps = setupUdfps(); // first = radius, second = udfps location
+        Pair<Float, PointF> udfps = setupUdfps(); // first = radius, second = udfps location
 
         // WHEN lock icon view controller is initialized and attached
         mLockIconViewController.init();
@@ -197,7 +192,7 @@
     @Test
     public void testUpdatePaddingBasedOnResolutionScale() {
         // GIVEN fp sensor location is available pre-attached & scaled resolution factor is 5
-        Pair<Integer, PointF> udfps = setupUdfps(); // first = radius, second = udfps location
+        Pair<Float, PointF> udfps = setupUdfps(); // first = radius, second = udfps location
         when(mAuthController.getScaleFactor()).thenReturn(5f);
 
         // WHEN lock icon view controller is initialized and attached
@@ -215,20 +210,19 @@
         // GIVEN fp sensor location is not available pre-init
         when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
         when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
-        when(mAuthController.getUdfpsProps()).thenReturn(null);
         mLockIconViewController.init();
         captureAttachListener();
         mAttachListener.onViewAttachedToWindow(mLockIconView);
 
-        // GIVEN fp sensor location is available post-atttached
+        // GIVEN fp sensor location is available post-attached
         captureAuthControllerCallback();
-        Pair<Integer, PointF> udfps = setupUdfps();
+        Pair<Float, PointF> udfps = setupUdfps();
 
         // WHEN all authenticators are registered
         mAuthControllerCallback.onAllAuthenticatorsRegistered();
         mDelayableExecutor.runAllReady();
 
-        // THEN lock icon view location is updated with the same coordinates as fpProps
+        // THEN lock icon view location is updated with the same coordinates as auth controller vals
         verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
                 eq(PADDING));
     }
@@ -402,21 +396,10 @@
         verify(mLockIconView).setTranslationX(0);
 
     }
-    private Pair<Integer, PointF> setupUdfps() {
+    private Pair<Float, PointF> setupUdfps() {
         when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
         final PointF udfpsLocation = new PointF(50, 75);
-        final int radius = 33;
-        final FingerprintSensorPropertiesInternal fpProps =
-                new FingerprintSensorPropertiesInternal(
-                        /* sensorId */ 0,
-                        /* strength */ 0,
-                        /* max enrollments per user */ 5,
-                        /* component info */ new ArrayList<>(),
-                        /* sensorType */ 3,
-                        /* halControlsIllumination */ true,
-                        /* resetLockoutRequiresHwToken */ false,
-                        List.of(new SensorLocationInternal("" /* displayId */,
-                                (int) udfpsLocation.x, (int) udfpsLocation.y, radius)));
+        final float radius = 33f;
         when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocation);
         when(mAuthController.getUdfpsRadius()).thenReturn(radius);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index f9fb865..18aae0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -21,7 +21,6 @@
 import android.app.PendingIntent
 import android.app.smartspace.SmartspaceAction
 import android.content.Context
-import org.mockito.Mockito.`when` as whenever
 import android.content.Intent
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
@@ -59,6 +58,7 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastSender
+import com.android.systemui.media.MediaControlPanel.KEY_SMARTSPACE_APP_NAME
 import com.android.systemui.media.dialog.MediaOutputDialogFactory
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
@@ -67,8 +67,8 @@
 import com.android.systemui.util.animation.TransitionLayout
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.KotlinArgumentCaptor
-import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.mockito.withArgCaptor
@@ -92,6 +92,7 @@
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
 
 private const val KEY = "TEST_KEY"
 private const val PACKAGE = "PKG"
@@ -102,6 +103,7 @@
 private const val SESSION_ARTIST = "SESSION_ARTIST"
 private const val SESSION_TITLE = "SESSION_TITLE"
 private const val DISABLED_DEVICE_NAME = "DISABLED_DEVICE_NAME"
+private const val REC_APP_NAME = "REC APP NAME"
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -262,6 +264,7 @@
 
         // Set valid recommendation data
         val extras = Bundle()
+        extras.putString(KEY_SMARTSPACE_APP_NAME, REC_APP_NAME)
         val intent = Intent().apply {
             putExtras(extras)
             setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -339,6 +342,7 @@
         whenever(viewHolder.player).thenReturn(view)
         whenever(viewHolder.appIcon).thenReturn(appIcon)
         whenever(viewHolder.albumView).thenReturn(albumView)
+        whenever(albumView.foreground).thenReturn(mock(Drawable::class.java))
         whenever(viewHolder.titleText).thenReturn(titleText)
         whenever(viewHolder.artistText).thenReturn(artistText)
         whenever(seamlessBackground.getDrawable(0)).thenReturn(mock(GradientDrawable::class.java))
@@ -1121,6 +1125,91 @@
         verify(mediaCarouselController).removePlayer(eq(mediaKey), eq(false), eq(false))
     }
 
+    @Test
+    fun player_gutsOpen_contentDescriptionIsForGuts() {
+        whenever(mediaViewController.isGutsVisible).thenReturn(true)
+        player.attachPlayer(viewHolder)
+
+        val gutsTextString = "gutsText"
+        whenever(gutsText.text).thenReturn(gutsTextString)
+        player.bindPlayer(mediaData, KEY)
+
+        val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java)
+        verify(viewHolder.player).contentDescription = descriptionCaptor.capture()
+        val description = descriptionCaptor.value.toString()
+
+        assertThat(description).isEqualTo(gutsTextString)
+    }
+
+    @Test
+    fun player_gutsClosed_contentDescriptionIsForPlayer() {
+        whenever(mediaViewController.isGutsVisible).thenReturn(false)
+        player.attachPlayer(viewHolder)
+
+        val app = "appName"
+        player.bindPlayer(mediaData.copy(app = app), KEY)
+
+        val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java)
+        verify(viewHolder.player).contentDescription = descriptionCaptor.capture()
+        val description = descriptionCaptor.value.toString()
+
+        assertThat(description).contains(mediaData.song!!)
+        assertThat(description).contains(mediaData.artist!!)
+        assertThat(description).contains(app)
+    }
+
+    @Test
+    fun player_gutsChangesFromOpenToClosed_contentDescriptionUpdated() {
+        // Start out open
+        whenever(mediaViewController.isGutsVisible).thenReturn(true)
+        whenever(gutsText.text).thenReturn("gutsText")
+        player.attachPlayer(viewHolder)
+        val app = "appName"
+        player.bindPlayer(mediaData.copy(app = app), KEY)
+
+        // Update to closed by long pressing
+        val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+        verify(viewHolder.player).onLongClickListener = captor.capture()
+        reset(viewHolder.player)
+
+        whenever(mediaViewController.isGutsVisible).thenReturn(false)
+        captor.value.onLongClick(viewHolder.player)
+
+        // Then content description is now the player content description
+        val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java)
+        verify(viewHolder.player).contentDescription = descriptionCaptor.capture()
+        val description = descriptionCaptor.value.toString()
+
+        assertThat(description).contains(mediaData.song!!)
+        assertThat(description).contains(mediaData.artist!!)
+        assertThat(description).contains(app)
+    }
+
+    @Test
+    fun player_gutsChangesFromClosedToOpen_contentDescriptionUpdated() {
+        // Start out closed
+        whenever(mediaViewController.isGutsVisible).thenReturn(false)
+        val gutsTextString = "gutsText"
+        whenever(gutsText.text).thenReturn(gutsTextString)
+        player.attachPlayer(viewHolder)
+        player.bindPlayer(mediaData.copy(app = "appName"), KEY)
+
+        // Update to open by long pressing
+        val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+        verify(viewHolder.player).onLongClickListener = captor.capture()
+        reset(viewHolder.player)
+
+        whenever(mediaViewController.isGutsVisible).thenReturn(true)
+        captor.value.onLongClick(viewHolder.player)
+
+        // Then content description is now the guts content description
+        val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java)
+        verify(viewHolder.player).contentDescription = descriptionCaptor.capture()
+        val description = descriptionCaptor.value.toString()
+
+        assertThat(description).isEqualTo(gutsTextString)
+    }
+
     /* ***** END guts tests for the player ***** */
 
     /* ***** Guts tests for the recommendations ***** */
@@ -1189,6 +1278,85 @@
         verify(mediaDataManager).dismissSmartspaceRecommendation(eq(mediaKey), anyLong())
     }
 
+    @Test
+    fun recommendation_gutsOpen_contentDescriptionIsForGuts() {
+        whenever(mediaViewController.isGutsVisible).thenReturn(true)
+        player.attachRecommendation(recommendationViewHolder)
+
+        val gutsTextString = "gutsText"
+        whenever(gutsText.text).thenReturn(gutsTextString)
+        player.bindRecommendation(smartspaceData)
+
+        val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java)
+        verify(viewHolder.player).contentDescription = descriptionCaptor.capture()
+        val description = descriptionCaptor.value.toString()
+
+        assertThat(description).isEqualTo(gutsTextString)
+    }
+
+    @Test
+    fun recommendation_gutsClosed_contentDescriptionIsForPlayer() {
+        whenever(mediaViewController.isGutsVisible).thenReturn(false)
+        player.attachRecommendation(recommendationViewHolder)
+
+        player.bindRecommendation(smartspaceData)
+
+        val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java)
+        verify(viewHolder.player).contentDescription = descriptionCaptor.capture()
+        val description = descriptionCaptor.value.toString()
+
+        assertThat(description).contains(REC_APP_NAME)
+    }
+
+    @Test
+    fun recommendation_gutsChangesFromOpenToClosed_contentDescriptionUpdated() {
+        // Start out open
+        whenever(mediaViewController.isGutsVisible).thenReturn(true)
+        whenever(gutsText.text).thenReturn("gutsText")
+        player.attachRecommendation(recommendationViewHolder)
+        player.bindRecommendation(smartspaceData)
+
+        // Update to closed by long pressing
+        val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+        verify(viewHolder.player).onLongClickListener = captor.capture()
+        reset(viewHolder.player)
+
+        whenever(mediaViewController.isGutsVisible).thenReturn(false)
+        captor.value.onLongClick(viewHolder.player)
+
+        // Then content description is now the player content description
+        val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java)
+        verify(viewHolder.player).contentDescription = descriptionCaptor.capture()
+        val description = descriptionCaptor.value.toString()
+
+        assertThat(description).contains(REC_APP_NAME)
+    }
+
+    @Test
+    fun recommendation_gutsChangesFromClosedToOpen_contentDescriptionUpdated() {
+        // Start out closed
+        whenever(mediaViewController.isGutsVisible).thenReturn(false)
+        val gutsTextString = "gutsText"
+        whenever(gutsText.text).thenReturn(gutsTextString)
+        player.attachRecommendation(recommendationViewHolder)
+        player.bindRecommendation(smartspaceData)
+
+        // Update to open by long pressing
+        val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java)
+        verify(viewHolder.player).onLongClickListener = captor.capture()
+        reset(viewHolder.player)
+
+        whenever(mediaViewController.isGutsVisible).thenReturn(true)
+        captor.value.onLongClick(viewHolder.player)
+
+        // Then content description is now the guts content description
+        val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java)
+        verify(viewHolder.player).contentDescription = descriptionCaptor.capture()
+        val description = descriptionCaptor.value.toString()
+
+        assertThat(description).isEqualTo(gutsTextString)
+    }
+
     /* ***** END guts tests for the recommendations ***** */
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index 10eeb11..18ee791 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -25,19 +25,17 @@
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
-
 import com.android.settingslib.media.LocalMediaManager
 import com.android.settingslib.media.MediaDevice
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager
 import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManagerFactory
+import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
-
 import com.google.common.truth.Truth.assertThat
-
 import org.junit.After
 import org.junit.Before
 import org.junit.Rule
@@ -50,8 +48,8 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
 
 private const val KEY = "TEST_KEY"
 private const val KEY_OLD = "TEST_KEY_OLD"
@@ -81,6 +79,7 @@
     @Mock private lateinit var route: RoutingSessionInfo
     @Mock private lateinit var controller: MediaController
     @Mock private lateinit var playbackInfo: PlaybackInfo
+    @Mock private lateinit var configurationController: ConfigurationController
     private lateinit var session: MediaSession
     private lateinit var mediaData: MediaData
     @JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -94,6 +93,7 @@
                 lmmFactory,
                 mr2,
                 muteAwaitFactory,
+                configurationController,
                 fakeFgExecutor,
                 fakeBgExecutor,
                 dumpster
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt
index 604e1f3..1817809 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt
@@ -4,16 +4,22 @@
 import android.testing.TestableLooper
 import android.view.View
 import androidx.test.filters.SmallTest
+import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.animation.MeasurementInput
 import com.android.systemui.util.animation.TransitionLayout
 import com.android.systemui.util.animation.TransitionViewState
+import com.android.systemui.util.animation.WidgetState
 import junit.framework.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
 
 /**
  * Tests for {@link MediaViewController}.
@@ -31,6 +37,9 @@
     private lateinit var mediaViewController: MediaViewController
     private val mediaHostStateHolder = MediaHost.MediaHostStateHolder()
     private var transitionLayout = TransitionLayout(context, /* attrs */ null, /* defStyleAttr */ 0)
+    @Mock private lateinit var mockViewState: TransitionViewState
+    @Mock private lateinit var mockCopiedState: TransitionViewState
+    @Mock private lateinit var mockWidgetState: WidgetState
 
     @Before
     fun setUp() {
@@ -63,4 +72,15 @@
         mediaHostStateHolder.squishFraction = 0.5f
         assertTrue(mediaViewController.obtainViewState(mediaHostStateHolder)!!.height == 50)
     }
+
+    @Test
+    fun testSquish_DoesNotMutateViewState() {
+        whenever(mockViewState.copy()).thenReturn(mockCopiedState)
+        whenever(mockCopiedState.widgetStates)
+            .thenReturn(mutableMapOf(R.id.album_art to mockWidgetState))
+
+        mediaViewController.squishViewState(mockViewState, 0.5f)
+        verify(mockViewState, times(1)).copy()
+        verifyNoMoreInteractions(mockViewState)
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 4ea9321..0f2e9bc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -318,15 +318,6 @@
     }
 
     @Test
-    public void testGetIsNonblockable_oemLocked() throws Exception {
-        ExpandableNotificationRow row =
-                mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
-        row.getEntry().getChannel().setImportanceLockedByOEM(true);
-
-        assertTrue(row.getIsNonblockable());
-    }
-
-    @Test
     public void testGetIsNonblockable_criticalDeviceFunction() throws Exception {
         ExpandableNotificationRow row =
                 mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 6d3a5fe..ec20271 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -336,7 +336,7 @@
         // WHEN the position algorithm is run
         positionClock();
         // THEN the padding DOESN'T adjust for keyguard status height.
-        assertThat(mClockPositionAlgorithm.getMinStackScrollerPadding())
+        assertThat(mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding())
                 .isEqualTo(mKeyguardStatusBarHeaderHeight);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 94e6b9a..11f96ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -610,13 +610,13 @@
         when(mLockIconViewController.getTop()).thenReturn(80f);
         when(mResources.getDimensionPixelSize(R.dimen.shelf_and_lock_icon_overlap)).thenReturn(5);
 
-        // Available space (100 - 10 - 15 = 75)
+        // Available space (100 - 0 - 15 = 85)
         when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(100);
-        when(mNotificationStackScrollLayoutController.getTopPadding()).thenReturn(10);
+        when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0);
         mNotificationPanelViewController.updateResources();
 
         assertThat(mNotificationPanelViewController.getSpaceForLockscreenNotifications())
-                .isEqualTo(75);
+                .isEqualTo(85);
     }
 
     @Test
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 649328d..9b29bae 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -242,6 +242,9 @@
 
         int getCurrentUserIdLocked();
 
+        Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
+                int windowId);
+
         boolean isAccessibilityButtonShown();
 
         /**
@@ -551,8 +554,6 @@
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
         Region partialInteractiveRegion = Region.obtain();
-        final MagnificationSpec spec;
-        final float[] transformMatrix;
         synchronized (mLock) {
             mUsesAccessibilityCache = true;
             if (!hasRightsToCurrentUserLocked()) {
@@ -576,11 +577,11 @@
                 partialInteractiveRegion.recycle();
                 partialInteractiveRegion = null;
             }
-            final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
-                    getTransformMatrixAndSpecLocked(resolvedWindowId);
-            transformMatrix = transformMatrixAndSpec.first;
-            spec = transformMatrixAndSpec.second;
         }
+        final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
+                getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
+        final float[] transformMatrix = transformMatrixAndSpec.first;
+        final MagnificationSpec spec = transformMatrixAndSpec.second;
         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
             return null;
         }
@@ -628,8 +629,6 @@
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
         Region partialInteractiveRegion = Region.obtain();
-        final MagnificationSpec spec;
-        final float [] transformMatrix;
         synchronized (mLock) {
             mUsesAccessibilityCache = true;
             if (!hasRightsToCurrentUserLocked()) {
@@ -653,11 +652,11 @@
                 partialInteractiveRegion.recycle();
                 partialInteractiveRegion = null;
             }
-            final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
-                    getTransformMatrixAndSpecLocked(resolvedWindowId);
-            transformMatrix = transformMatrixAndSpec.first;
-            spec = transformMatrixAndSpec.second;
         }
+        final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
+                getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
+        final float[] transformMatrix = transformMatrixAndSpec.first;
+        final MagnificationSpec spec = transformMatrixAndSpec.second;
         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
             return null;
         }
@@ -706,8 +705,6 @@
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
         Region partialInteractiveRegion = Region.obtain();
-        final MagnificationSpec spec;
-        final float[] transformMatrix;
         synchronized (mLock) {
             mUsesAccessibilityCache = true;
             if (!hasRightsToCurrentUserLocked()) {
@@ -731,11 +728,11 @@
                 partialInteractiveRegion.recycle();
                 partialInteractiveRegion = null;
             }
-            final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
-                    getTransformMatrixAndSpecLocked(resolvedWindowId);
-            transformMatrix = transformMatrixAndSpec.first;
-            spec = transformMatrixAndSpec.second;
         }
+        final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
+                getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
+        final float[] transformMatrix = transformMatrixAndSpec.first;
+        final MagnificationSpec spec = transformMatrixAndSpec.second;
         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
             return null;
         }
@@ -786,8 +783,6 @@
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
         Region partialInteractiveRegion = Region.obtain();
-        final MagnificationSpec spec;
-        final float[] transformMatrix;
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
                 return null;
@@ -811,11 +806,11 @@
                 partialInteractiveRegion.recycle();
                 partialInteractiveRegion = null;
             }
-            final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
-                    getTransformMatrixAndSpecLocked(resolvedWindowId);
-            transformMatrix = transformMatrixAndSpec.first;
-            spec = transformMatrixAndSpec.second;
         }
+        final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
+                getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
+        final float[] transformMatrix = transformMatrixAndSpec.first;
+        final MagnificationSpec spec = transformMatrixAndSpec.second;
         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
             return null;
         }
@@ -865,8 +860,6 @@
         final int resolvedWindowId;
         RemoteAccessibilityConnection connection;
         Region partialInteractiveRegion = Region.obtain();
-        final MagnificationSpec spec;
-        final float[] transformMatrix;
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
                 return null;
@@ -889,12 +882,11 @@
                 partialInteractiveRegion.recycle();
                 partialInteractiveRegion = null;
             }
-
-            final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
-                    getTransformMatrixAndSpecLocked(resolvedWindowId);
-            transformMatrix = transformMatrixAndSpec.first;
-            spec = transformMatrixAndSpec.second;
         }
+        final Pair<float[], MagnificationSpec> transformMatrixAndSpec =
+                getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
+        final float[] transformMatrix = transformMatrixAndSpec.first;
+        final MagnificationSpec spec = transformMatrixAndSpec.second;
         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
             return null;
         }
@@ -1672,21 +1664,10 @@
         mInvocationHandler.startInputLocked(connection, editorInfo, restarting);
     }
 
-
-
     @Nullable
-    Pair<float[], MagnificationSpec> getTransformMatrixAndSpecLocked(int resolvedWindowId) {
-        final WindowInfo windowInfo =
-                mA11yWindowManager.findWindowInfoByIdLocked(resolvedWindowId);
-        if (windowInfo == null) {
-            Slog.w(LOG_TAG, "getTransformMatrixAndSpec, windowInfo is null window id = "
-                    + resolvedWindowId);
-            return new Pair<>(null, null);
-        }
-
-        final MagnificationSpec spec = new MagnificationSpec();
-        spec.setTo(windowInfo.mMagnificationSpec);
-        return new Pair<>(windowInfo.mTransformMatrix, spec);
+    private Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
+            int resolvedWindowId) {
+        return mSystemSupport.getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId);
     }
 
     /**
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 4806514..99c8495 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -68,6 +68,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.database.ContentObserver;
+import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -99,6 +100,7 @@
 import android.text.TextUtils.SimpleStringSplitter;
 import android.util.ArraySet;
 import android.util.IntArray;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
@@ -458,6 +460,41 @@
     }
 
     @Override
+    public Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
+            int windowId) {
+        WindowInfo windowInfo;
+        synchronized (mLock) {
+            windowInfo = mA11yWindowManager.findWindowInfoByIdLocked(windowId);
+        }
+        if (windowInfo != null) {
+            final MagnificationSpec spec = new MagnificationSpec();
+            spec.setTo(windowInfo.mMagnificationSpec);
+            return new Pair<>(windowInfo.mTransformMatrix, spec);
+        } else {
+            // If the framework doesn't track windows, we fall back to get the pair of
+            // transformation matrix and MagnificationSpe from the WindowManagerService's
+            // WindowState.
+            IBinder token;
+            synchronized (mLock) {
+                token = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(mCurrentUserId,
+                        windowId);
+            }
+            Pair<Matrix, MagnificationSpec> pair =
+                    mWindowManagerService.getWindowTransformationMatrixAndMagnificationSpec(token);
+            final float[] outTransformationMatrix = new float[9];
+            final Matrix tmpMatrix = pair.first;
+            final MagnificationSpec spec = pair.second;
+            if (!spec.isNop()) {
+                tmpMatrix.postScale(spec.scale, spec.scale);
+                tmpMatrix.postTranslate(spec.offsetX, spec.offsetY);
+            }
+            tmpMatrix.getValues(outTransformationMatrix);
+
+            return new Pair<>(outTransformationMatrix, pair.second);
+        }
+    }
+
+    @Override
     public void onServiceInfoChangedLocked(AccessibilityUserState userState) {
         mSecurityPolicy.onBoundServicesChangedLocked(userState.mUserId,
                 userState.mBoundServices);
@@ -3750,12 +3787,12 @@
                         boundsInScreenBeforeMagnification.centerY());
 
                 // Invert magnification if needed.
-                final WindowInfo windowInfo = mA11yWindowManager.findWindowInfoByIdLocked(
-                        focus.getWindowId());
+                final Pair<float[], MagnificationSpec> pair =
+                        getWindowTransformationMatrixAndMagnificationSpec(focus.getWindowId());
                 MagnificationSpec spec = null;
-                if (windowInfo != null) {
+                if (pair != null && pair.second != null) {
                     spec = new MagnificationSpec();
-                    spec.setTo(windowInfo.mMagnificationSpec);
+                    spec.setTo(pair.second);
                 }
 
                 if (spec != null && !spec.isNop()) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 8e32a7a..6d3620f 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -3225,10 +3225,15 @@
                     currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
                     mService.logDatasetShown(id, mClientState, UI_TYPE_DIALOG);
                 }
+                // Just show fill dialog once, so disabled after shown.
+                // Note: Cannot disable before requestShowFillDialog() because the method
+                //       need to check whether fill dialog enabled.
+                setFillDialogDisabled();
                 return;
             } else {
                 setFillDialogDisabled();
             }
+
         }
 
         if (response.supportsInlineSuggestions()) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 62bb9f1..2a22cac 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -62,7 +62,6 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageItemInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.UserInfo;
@@ -81,7 +80,6 @@
 import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.text.BidiFormatter;
 import android.util.ArraySet;
 import android.util.ExceptionUtils;
 import android.util.Log;
@@ -538,20 +536,12 @@
             String callingPackage = component.getPackageName();
             checkCanCallNotificationApi(callingPackage);
             // TODO: check userId.
-            String packageTitle = BidiFormatter.getInstance().unicodeWrap(
-                    getPackageInfo(getContext(), userId, callingPackage)
-                            .applicationInfo
-                            .loadSafeLabel(getContext().getPackageManager(),
-                                    PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
-                                    PackageItemInfo.SAFE_LABEL_FLAG_TRIM
-                                            | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE)
-                            .toString());
             final long identity = Binder.clearCallingIdentity();
             try {
                 return PendingIntent.getActivityAsUser(getContext(),
                         0 /* request code */,
                         NotificationAccessConfirmationActivityContract.launcherIntent(
-                                getContext(), userId, component, packageTitle),
+                                getContext(), userId, component),
                         PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT
                                 | PendingIntent.FLAG_CANCEL_CURRENT,
                         null /* options */,
@@ -732,9 +722,12 @@
                 String[] args, ShellCallback callback, ResultReceiver resultReceiver)
                 throws RemoteException {
             enforceCallerCanManageCompanionDevice(getContext(), "onShellCommand");
-            new CompanionDeviceShellCommand(
-                    CompanionDeviceManagerService.this, mAssociationStore)
-                    .exec(this, in, out, err, args, callback, resultReceiver);
+
+            final CompanionDeviceShellCommand cmd = new CompanionDeviceShellCommand(
+                    CompanionDeviceManagerService.this,
+                    mAssociationStore,
+                    mDevicePresenceMonitor);
+            cmd.exec(this, in, out, err, args, callback, resultReceiver);
         }
 
         @Override
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index fd13085..6a19a42 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -21,6 +21,8 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
+
 import java.io.PrintWriter;
 import java.util.List;
 
@@ -29,20 +31,24 @@
 
     private final CompanionDeviceManagerService mService;
     private final AssociationStore mAssociationStore;
+    private final CompanionDevicePresenceMonitor mDevicePresenceMonitor;
 
     CompanionDeviceShellCommand(CompanionDeviceManagerService service,
-            AssociationStore associationStore) {
+            AssociationStore associationStore,
+            CompanionDevicePresenceMonitor devicePresenceMonitor) {
         mService = service;
         mAssociationStore = associationStore;
+        mDevicePresenceMonitor = devicePresenceMonitor;
     }
 
     @Override
     public int onCommand(String cmd) {
         final PrintWriter out = getOutPrintWriter();
+        final int associationId;
         try {
             switch (cmd) {
                 case "list": {
-                    final int userId = getNextArgInt();
+                    final int userId = getNextIntArgRequired();
                     final List<AssociationInfo> associationsForUser =
                             mAssociationStore.getAssociationsForUser(userId);
                     for (AssociationInfo association : associationsForUser) {
@@ -55,7 +61,7 @@
                 break;
 
                 case "associate": {
-                    int userId = getNextArgInt();
+                    int userId = getNextIntArgRequired();
                     String packageName = getNextArgRequired();
                     String address = getNextArgRequired();
                     mService.legacyCreateAssociation(userId, address, packageName, null);
@@ -63,7 +69,7 @@
                 break;
 
                 case "disassociate": {
-                    final int userId = getNextArgInt();
+                    final int userId = getNextIntArgRequired();
                     final String packageName = getNextArgRequired();
                     final String address = getNextArgRequired();
                     final AssociationInfo association =
@@ -80,6 +86,16 @@
                 }
                 break;
 
+                case "simulate-device-appeared":
+                    associationId = getNextIntArgRequired();
+                    mDevicePresenceMonitor.simulateDeviceAppeared(associationId);
+                    break;
+
+                case "simulate-device-disappeared":
+                    associationId = getNextIntArgRequired();
+                    mDevicePresenceMonitor.simulateDeviceDisappeared(associationId);
+                    break;
+
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -91,10 +107,6 @@
         }
     }
 
-    private int getNextArgInt() {
-        return Integer.parseInt(getNextArgRequired());
-    }
-
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
@@ -108,7 +120,31 @@
         pw.println("  disassociate USER_ID PACKAGE MAC_ADDRESS");
         pw.println("      Remove an existing Association.");
         pw.println("  clear-association-memory-cache");
-        pw.println("      Clear the in-memory association cache and reload all association "
-                + "information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY.");
+        pw.println("      Clear the in-memory association cache and reload all association ");
+        pw.println("      information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY.");
+        pw.println("      USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+
+        pw.println("  simulate-device-appeared ASSOCIATION_ID");
+        pw.println("      Make CDM act as if the given companion device has appeared.");
+        pw.println("      I.e. bind the associated companion application's");
+        pw.println("      CompanionDeviceService(s) and trigger onDeviceAppeared() callback.");
+        pw.println("      The CDM will consider the devices as present for 60 seconds and then");
+        pw.println("      will act as if device disappeared, unless 'simulate-device-disappeared'");
+        pw.println("      or 'simulate-device-appeared' is called again before 60 seconds run out"
+                + ".");
+        pw.println("      USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+
+        pw.println("  simulate-device-disappeared ASSOCIATION_ID");
+        pw.println("      Make CDM act as if the given companion device has disappeared.");
+        pw.println("      I.e. unbind the associated companion application's");
+        pw.println("      CompanionDeviceService(s) and trigger onDeviceDisappeared() callback.");
+        pw.println("      NOTE: This will only have effect if 'simulate-device-appeared' was");
+        pw.println("      invoked for the same device (same ASSOCIATION_ID) no longer than");
+        pw.println("      60 seconds ago.");
+        pw.println("      USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY.");
+    }
+
+    private int getNextIntArgRequired() {
+        return Integer.parseInt(getNextArgRequired());
     }
 }
diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
index 24be1b6..37e8369 100644
--- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
+++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
@@ -16,11 +16,19 @@
 
 package com.android.server.companion.presence;
 
+import static android.os.Process.ROOT_UID;
+import static android.os.Process.SHELL_UID;
+
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
+import android.annotation.TestApi;
 import android.bluetooth.BluetoothAdapter;
 import android.companion.AssociationInfo;
 import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 import android.util.Log;
 
 import com.android.server.companion.AssociationStore;
@@ -72,6 +80,11 @@
     private final @NonNull Set<Integer> mNearbyBleDevices = new HashSet<>();
     private final @NonNull Set<Integer> mReportedSelfManagedDevices = new HashSet<>();
 
+    // Tracking "simulated" presence. Used for debugging and testing only.
+    private final @NonNull Set<Integer> mSimulated = new HashSet<>();
+    private final SimulatedDevicePresenceSchedulerHelper mSchedulerHelper =
+            new SimulatedDevicePresenceSchedulerHelper();
+
     public CompanionDevicePresenceMonitor(@NonNull AssociationStore associationStore,
             @NonNull Callback callback) {
         mAssociationStore = associationStore;
@@ -106,7 +119,8 @@
     public boolean isDevicePresent(int associationId) {
         return mReportedSelfManagedDevices.contains(associationId)
                 || mConnectedBtDevices.contains(associationId)
-                || mNearbyBleDevices.contains(associationId);
+                || mNearbyBleDevices.contains(associationId)
+                || mSimulated.contains(associationId);
     }
 
     /**
@@ -155,6 +169,45 @@
         onDeviceGone(mNearbyBleDevices, associationId, /* sourceLoggingTag */ "ble");
     }
 
+    /** FOR DEBUGGING AND/OR TESTING PURPOSES ONLY. */
+    @TestApi
+    public void simulateDeviceAppeared(int associationId) {
+        // IMPORTANT: this API should only be invoked via the
+        // 'companiondevice simulate-device-appeared' Shell command, so the only uid-s allowed to
+        // make this call are SHELL and ROOT.
+        // No other caller (including SYSTEM!) should be allowed.
+        enforceCallerShellOrRoot();
+        // Make sure the association exists.
+        enforceAssociationExists(associationId);
+
+        onDevicePresent(mSimulated, associationId, /* sourceLoggingTag */ "simulated");
+
+        mSchedulerHelper.scheduleOnDeviceGoneCallForSimulatedDevicePresence(associationId);
+    }
+
+    /** FOR DEBUGGING AND/OR TESTING PURPOSES ONLY. */
+    @TestApi
+    public void simulateDeviceDisappeared(int associationId) {
+        // IMPORTANT: this API should only be invoked via the
+        // 'companiondevice simulate-device-appeared' Shell command, so the only uid-s allowed to
+        // make this call are SHELL and ROOT.
+        // No other caller (including SYSTEM!) should be allowed.
+        enforceCallerShellOrRoot();
+        // Make sure the association exists.
+        enforceAssociationExists(associationId);
+
+        mSchedulerHelper.unscheduleOnDeviceGoneCallForSimulatedDevicePresence(associationId);
+
+        onDeviceGone(mSimulated, associationId, /* sourceLoggingTag */ "simulated");
+    }
+
+    private void enforceAssociationExists(int associationId) {
+        if (mAssociationStore.getAssociationById(associationId) == null) {
+            throw new IllegalArgumentException(
+                    "Association with id " + associationId + " does not exist.");
+        }
+    }
+
     private void onDevicePresent(@NonNull Set<Integer> presentDevicesForSource,
             int newDeviceAssociationId, @NonNull String sourceLoggingTag) {
         if (DEBUG) {
@@ -212,6 +265,7 @@
         mConnectedBtDevices.remove(associationId);
         mNearbyBleDevices.remove(associationId);
         mReportedSelfManagedDevices.remove(associationId);
+        mSimulated.remove(associationId);
     }
 
     /**
@@ -232,4 +286,36 @@
         // CompanionDeviceManagerService will know that the association is removed, and will do
         // what's needed.
     }
+
+    private static void enforceCallerShellOrRoot() {
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid == SHELL_UID || callingUid == ROOT_UID) return;
+
+        throw new SecurityException("Caller is neither Shell nor Root");
+    }
+
+    private class SimulatedDevicePresenceSchedulerHelper extends Handler {
+        SimulatedDevicePresenceSchedulerHelper() {
+            super(Looper.getMainLooper());
+        }
+
+        void scheduleOnDeviceGoneCallForSimulatedDevicePresence(int associationId) {
+            // First, unschedule if it was scheduled previously.
+            if (hasMessages(/* what */ associationId)) {
+                removeMessages(/* what */ associationId);
+            }
+
+            sendEmptyMessageDelayed(/* what */ associationId, 60 * 1000 /* 60 seconds */);
+        }
+
+        void unscheduleOnDeviceGoneCallForSimulatedDevicePresence(int associationId) {
+            removeMessages(/* what */ associationId);
+        }
+
+        @Override
+        public void handleMessage(@NonNull Message msg) {
+            final int associationId = msg.what;
+            onDeviceGone(mSimulated, associationId, /* sourceLoggingTag */ "simulated");
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 5a234f5..c09bb2d 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -67,6 +67,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
@@ -360,6 +361,7 @@
                     mUidBatteryUsageInWindow.removeAt(i);
                 }
             }
+            mInjector.getPolicy().onUserRemovedLocked(userId);
         }
     }
 
@@ -368,6 +370,7 @@
         synchronized (mLock) {
             mUidBatteryUsage.delete(uid);
             mUidBatteryUsageInWindow.delete(uid);
+            mInjector.getPolicy().onUidRemovedLocked(uid);
         }
     }
 
@@ -1208,6 +1211,14 @@
                 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_window";
 
         /**
+         * The grace period after an interaction event with the app, if the background current
+         * drain goes beyond the threshold within that period, the system won't apply the
+         * restrictions.
+         */
+        static final String KEY_BG_CURRENT_DRAIN_INTERACTION_GRACE_PERIOD =
+                DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_interaction_grace_period";
+
+        /**
          * Similar to {@link #KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET}, but a higher
          * value for the legitimate cases with higher background current drain.
          */
@@ -1310,6 +1321,11 @@
         final long mDefaultBgCurrentDrainWindowMs;
 
         /**
+         * Default value to {@link #mBgCurrentDrainInteractionGracePeriodMs}.
+         */
+        final long mDefaultBgCurrentDrainInteractionGracePeriodMs;
+
+        /**
          * Default value to the {@link #INDEX_HIGH_CURRENT_DRAIN_THRESHOLD} of
          * the {@link #mBgCurrentDrainRestrictedBucketThreshold}.
          */
@@ -1394,6 +1410,11 @@
         volatile long mBgCurrentDrainWindowMs;
 
         /**
+         * @see #KEY_BG_CURRENT_DRAIN_INTERACTION_GRACE_PERIOD.
+         */
+        volatile long mBgCurrentDrainInteractionGracePeriodMs;
+
+        /**
          * @see #KEY_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION.
          */
         volatile long mBgCurrentDrainMediaPlaybackMinDuration;
@@ -1455,6 +1476,12 @@
         private final SparseArray<Pair<long[], ImmutableBatteryUsage[]>> mHighBgBatteryPackages =
                 new SparseArray<>();
 
+        /**
+         * The timestamp of the last interaction, key is the UID.
+         */
+        @GuardedBy("mLock")
+        private final SparseLongArray mLastInteractionTime = new SparseLongArray();
+
         @NonNull
         private final Object mLock;
 
@@ -1478,6 +1505,7 @@
                     isLowRamDeviceStatic() ? val[1] : val[0];
             mDefaultBgCurrentDrainWindowMs = resources.getInteger(
                     R.integer.config_bg_current_drain_window) * 1_000;
+            mDefaultBgCurrentDrainInteractionGracePeriodMs = mDefaultBgCurrentDrainWindowMs;
             val = getFloatArray(resources.obtainTypedArray(
                     R.array.config_bg_current_drain_high_threshold_to_restricted_bucket));
             mDefaultBgCurrentDrainRestrictedBucketHighThreshold =
@@ -1511,6 +1539,8 @@
             mBgCurrentDrainBgRestrictedThreshold[1] =
                     mDefaultBgCurrentDrainBgRestrictedHighThreshold;
             mBgCurrentDrainWindowMs = mDefaultBgCurrentDrainWindowMs;
+            mBgCurrentDrainInteractionGracePeriodMs =
+                    mDefaultBgCurrentDrainInteractionGracePeriodMs;
             mBgCurrentDrainMediaPlaybackMinDuration =
                     mDefaultBgCurrentDrainMediaPlaybackMinDuration;
             mBgCurrentDrainLocationMinDuration = mDefaultBgCurrentDrainLocationMinDuration;
@@ -1542,6 +1572,9 @@
                 case KEY_BG_CURRENT_DRAIN_WINDOW:
                     updateCurrentDrainWindow();
                     break;
+                case KEY_BG_CURRENT_DRAIN_INTERACTION_GRACE_PERIOD:
+                    updateCurrentDrainInteractionGracePeriod();
+                    break;
                 case KEY_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION:
                     updateCurrentDrainMediaPlaybackMinDuration();
                     break;
@@ -1626,6 +1659,13 @@
                     mDefaultBgCurrentDrainWindowMs);
         }
 
+        private void updateCurrentDrainInteractionGracePeriod() {
+            mBgCurrentDrainInteractionGracePeriodMs = DeviceConfig.getLong(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    KEY_BG_CURRENT_DRAIN_INTERACTION_GRACE_PERIOD,
+                    mDefaultBgCurrentDrainInteractionGracePeriodMs);
+        }
+
         private void updateCurrentDrainMediaPlaybackMinDuration() {
             mBgCurrentDrainMediaPlaybackMinDuration = DeviceConfig.getLong(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -1668,6 +1708,7 @@
             super.onSystemReady();
             updateCurrentDrainThreshold();
             updateCurrentDrainWindow();
+            updateCurrentDrainInteractionGracePeriod();
             updateCurrentDrainMediaPlaybackMinDuration();
             updateCurrentDrainLocationMinDuration();
             updateCurrentDrainEventDurationBasedThresholdEnabled();
@@ -1685,8 +1726,10 @@
             synchronized (mLock) {
                 final Pair<long[], ImmutableBatteryUsage[]> pair = mHighBgBatteryPackages.get(uid);
                 if (pair != null) {
+                    final long lastInteractionTime = mLastInteractionTime.get(uid, 0L);
                     final long[] ts = pair.first;
-                    final int restrictedLevel = ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] > 0
+                    final int restrictedLevel = ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET]
+                            > (lastInteractionTime + mBgCurrentDrainInteractionGracePeriodMs)
                             && mTracker.mAppRestrictionController.isAutoRestrictAbusiveAppEnabled()
                             ? RESTRICTION_LEVEL_RESTRICTED_BUCKET
                             : RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
@@ -1777,6 +1820,7 @@
                     // We're already in the background restricted level, nothing more we could do.
                     return;
                 }
+                final long lastInteractionTime = mLastInteractionTime.get(uid, 0L);
                 final long now = SystemClock.elapsedRealtime();
                 final int thresholdIndex = getCurrentDrainThresholdIndex(uid, now,
                         mBgCurrentDrainWindowMs);
@@ -1788,13 +1832,17 @@
                     long[] ts = null;
                     ImmutableBatteryUsage[] usages = null;
                     if (rbPercentage >= rbThreshold) {
-                        // New findings to us, track it and let the controller know.
-                        ts = new long[TIME_STAMP_INDEX_LAST];
-                        ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = now;
-                        usages = new ImmutableBatteryUsage[TIME_STAMP_INDEX_LAST];
-                        usages[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = usage;
-                        mHighBgBatteryPackages.put(uid, Pair.create(ts, usages));
-                        notifyController = excessive = true;
+                        if (now > lastInteractionTime + mBgCurrentDrainInteractionGracePeriodMs) {
+                            // New findings to us, track it and let the controller know.
+                            ts = new long[TIME_STAMP_INDEX_LAST];
+                            ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = now;
+                            usages = new ImmutableBatteryUsage[TIME_STAMP_INDEX_LAST];
+                            usages[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = usage;
+                            mHighBgBatteryPackages.put(uid, Pair.create(ts, usages));
+                            // It's beeen long enough since last interaction with this app.
+                            notifyController = true;
+                        }
+                        excessive = true;
                     }
                     if (decoupleThresholds && brPercentage >= brThreshold) {
                         if (ts == null) {
@@ -1812,11 +1860,15 @@
                     final long[] ts = pair.first;
                     final long lastRestrictBucketTs = ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET];
                     if (rbPercentage >= rbThreshold) {
-                        if (lastRestrictBucketTs == 0) {
-                            ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = now;
-                            pair.second[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = usage;
+                        if (now > lastInteractionTime + mBgCurrentDrainInteractionGracePeriodMs) {
+                            if (lastRestrictBucketTs == 0) {
+                                ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = now;
+                                pair.second[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = usage;
+                            }
+                            // It's been long enough since last interaction with this app.
+                            notifyController = true;
                         }
-                        notifyController = excessive = true;
+                        excessive = true;
                     } else {
                         // It's actually back to normal, but we don't untrack it until
                         // explicit user interactions, because the restriction could be the cause
@@ -1833,7 +1885,7 @@
                                 && (now > lastRestrictBucketTs + mBgCurrentDrainWindowMs));
                         if (notifyController) {
                             ts[TIME_STAMP_INDEX_BG_RESTRICTED] = now;
-                            pair.second[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = usage;
+                            pair.second[TIME_STAMP_INDEX_BG_RESTRICTED] = usage;
                         }
                         excessive = true;
                     } else {
@@ -1841,7 +1893,7 @@
                         // user consent to unrestrict it; or if it's in restricted bucket level,
                         // resetting this won't lift it from that level.
                         ts[TIME_STAMP_INDEX_BG_RESTRICTED] = 0;
-                        pair.second[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = null;
+                        pair.second[TIME_STAMP_INDEX_BG_RESTRICTED] = null;
                         // Now need to notify the controller.
                     }
                 }
@@ -1902,6 +1954,7 @@
         void onUserInteractionStarted(String packageName, int uid) {
             boolean changed = false;
             synchronized (mLock) {
+                mLastInteractionTime.put(uid, SystemClock.elapsedRealtime());
                 final int curLevel = mTracker.mAppRestrictionController.getRestrictionLevel(
                         uid, packageName);
                 if (curLevel == RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) {
@@ -1940,9 +1993,30 @@
         @VisibleForTesting
         void reset() {
             mHighBgBatteryPackages.clear();
+            mLastInteractionTime.clear();
             mTracker.reset();
         }
 
+        @GuardedBy("mLock")
+        void onUserRemovedLocked(final @UserIdInt int userId) {
+            for (int i = mHighBgBatteryPackages.size() - 1; i >= 0; i--) {
+                if (UserHandle.getUserId(mHighBgBatteryPackages.keyAt(i)) == userId) {
+                    mHighBgBatteryPackages.removeAt(i);
+                }
+            }
+            for (int i = mLastInteractionTime.size() - 1; i >= 0; i--) {
+                if (UserHandle.getUserId(mLastInteractionTime.keyAt(i)) == userId) {
+                    mLastInteractionTime.removeAt(i);
+                }
+            }
+        }
+
+        @GuardedBy("mLock")
+        void onUidRemovedLocked(final int uid) {
+            mHighBgBatteryPackages.remove(uid);
+            mLastInteractionTime.delete(uid);
+        }
+
         @Override
         void dump(PrintWriter pw, String prefix) {
             pw.print(prefix);
@@ -1976,6 +2050,10 @@
                 pw.print('=');
                 pw.println(mBgCurrentDrainWindowMs);
                 pw.print(prefix);
+                pw.print(KEY_BG_CURRENT_DRAIN_INTERACTION_GRACE_PERIOD);
+                pw.print('=');
+                pw.println(mBgCurrentDrainInteractionGracePeriodMs);
+                pw.print(prefix);
                 pw.print(KEY_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION);
                 pw.print('=');
                 pw.println(mBgCurrentDrainMediaPlaybackMinDuration);
diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java
index 18fb6a4..08a6719 100644
--- a/services/core/java/com/android/server/am/DropboxRateLimiter.java
+++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java
@@ -24,9 +24,14 @@
 
 /** Rate limiter for adding errors into dropbox. */
 public class DropboxRateLimiter {
-    private static final long RATE_LIMIT_BUFFER_EXPIRY = 15 * DateUtils.SECOND_IN_MILLIS;
-    private static final long RATE_LIMIT_BUFFER_DURATION = 10 * DateUtils.SECOND_IN_MILLIS;
-    private static final int RATE_LIMIT_ALLOWED_ENTRIES = 5;
+    // After RATE_LIMIT_ALLOWED_ENTRIES have been collected (for a single breakdown of
+    // process/eventType) further entries will be rejected until RATE_LIMIT_BUFFER_DURATION has
+    // elapsed, after which the current count for this breakdown will be reset.
+    private static final long RATE_LIMIT_BUFFER_DURATION = 10 * DateUtils.MINUTE_IN_MILLIS;
+    // The time duration after which the rate limit buffer will be cleared.
+    private static final long RATE_LIMIT_BUFFER_EXPIRY = 3 * RATE_LIMIT_BUFFER_DURATION;
+    // The number of entries to keep per breakdown of process/eventType.
+    private static final int RATE_LIMIT_ALLOWED_ENTRIES = 6;
 
     @GuardedBy("mErrorClusterRecords")
     private final ArrayMap<String, ErrorRecord> mErrorClusterRecords = new ArrayMap<>();
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index fc73a59..cceacd8 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2792,6 +2792,15 @@
         }
 
         int N = procs.size();
+        for (int i = 0; i < N; ++i) {
+            final ProcessRecord proc = procs.get(i).first;
+            try {
+                Process.setProcessFrozen(proc.getPid(), proc.uid, true);
+            } catch (Exception e) {
+                Slog.w(TAG, "Unable to freeze " + proc.getPid() + " " + proc.processName);
+            }
+        }
+
         for (int i=0; i<N; i++) {
             final Pair<ProcessRecord, Boolean> proc = procs.get(i);
             removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second,
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 3e97b91..36afb36 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -4545,8 +4545,9 @@
      * @return The restriction matching the package
      */
     private RestrictionBypass getBypassforPackage(@NonNull AndroidPackage pkg) {
-        return new RestrictionBypass(pkg.isPrivileged(), mContext.checkPermission(
-                android.Manifest.permission.EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS, -1, pkg.getUid())
+        return new RestrictionBypass(pkg.getUid() == Process.SYSTEM_UID, pkg.isPrivileged(),
+                mContext.checkPermission(android.Manifest.permission
+                        .EXEMPT_FROM_AUDIO_RECORD_RESTRICTIONS, -1, pkg.getUid())
                 == PackageManager.PERMISSION_GRANTED);
     }
 
@@ -4853,6 +4854,9 @@
                 if (opBypass != null) {
                     // If we are the system, bypass user restrictions for certain codes
                     synchronized (this) {
+                        if (opBypass.isSystemUid && appBypass != null && appBypass.isSystemUid) {
+                            return false;
+                        }
                         if (opBypass.isPrivileged && appBypass != null && appBypass.isPrivileged) {
                             return false;
                         }
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 122a950..d8aa9aa 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -486,8 +486,7 @@
 
         for (SADeviceState deviceState : mSADevices) {
             if (deviceType == deviceState.mDeviceType
-                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
-                    || !wireless) {
+                    && (!wireless || ada.getAddress().equals(deviceState.mDeviceAddress))) {
                 isInList = true;
                 if (forceEnable) {
                     deviceState.mEnabled = true;
@@ -511,8 +510,7 @@
 
         for (SADeviceState deviceState : mSADevices) {
             if (deviceType == deviceState.mDeviceType
-                    && (wireless && ada.getAddress().equals(deviceState.mDeviceAddress))
-                    || !wireless) {
+                    && (!wireless || ada.getAddress().equals(deviceState.mDeviceAddress))) {
                 deviceState.mEnabled = false;
                 break;
             }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 5ae3f33..e4e9d1d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1712,6 +1712,13 @@
             mPointerIconDisplayContext = null;
         }
 
+        synchronized (mAdditionalDisplayInputPropertiesLock) {
+            setPointerIconVisible(AdditionalDisplayInputProperties.DEFAULT_POINTER_ICON_VISIBLE,
+                    displayId);
+            setPointerAcceleration(AdditionalDisplayInputProperties.DEFAULT_POINTER_ACCELERATION,
+                    displayId);
+        }
+
         mNative.displayRemoved(displayId);
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
index db17c105..8180e66 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
@@ -64,7 +64,9 @@
                         | InputConfig.INTERCEPTS_STYLUS
                         | InputConfig.TRUSTED_OVERLAY;
 
-        // The touchable region of this input surface is not initially configured.
+        // Configure the surface to receive stylus events across the entire display.
+        mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
+
         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
         t.setInputWindowInfo(mInputSurface, mWindowHandle);
         t.setLayer(mInputSurface, HANDWRITING_SURFACE_LAYER);
@@ -81,10 +83,6 @@
         mWindowHandle.ownerUid = imeUid;
         mWindowHandle.inputConfig &= ~InputConfig.SPY;
 
-        // Update the touchable region so that the IME can intercept stylus events
-        // across the entire display.
-        mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
-
         new SurfaceControl.Transaction()
                 .setInputWindowInfo(mInputSurface, mWindowHandle)
                 .apply();
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
index a706772..f89b6ae 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
@@ -91,7 +91,7 @@
      * InputEventReceiver that batches events according to the current thread's Choreographer.
      */
     @UiThread
-    void initializeHandwritingSpy(int displayId, IBinder focusedWindowToken) {
+    void initializeHandwritingSpy(int displayId) {
         // When resetting, reuse resources if we are reinitializing on the same display.
         reset(displayId == mCurrentDisplayId);
         mCurrentDisplayId = displayId;
@@ -115,12 +115,6 @@
         mHandwritingSurface = new HandwritingEventReceiverSurface(
                 name, displayId, surface, channel);
 
-        // Configure the handwriting window to receive events over the focused window's bounds.
-        mWindowManagerInternal.replaceInputSurfaceTouchableRegionWithWindowCrop(
-                mHandwritingSurface.getSurface(),
-                mHandwritingSurface.getInputWindowHandle(),
-                focusedWindowToken);
-
         // Use a dup of the input channel so that event processing can be paused by disposing the
         // event receiver without causing a fd hangup.
         mHandwritingEventReceiver = new BatchedInputEventReceiver.SimpleBatchedInputEventReceiver(
@@ -149,7 +143,8 @@
      */
     @UiThread
     @Nullable
-    HandwritingSession startHandwritingSession(int requestId, int imePid, int imeUid) {
+    HandwritingSession startHandwritingSession(
+            int requestId, int imePid, int imeUid, IBinder focusedWindowToken) {
         if (mHandwritingSurface == null) {
             Slog.e(TAG, "Cannot start handwriting session: Handwriting was not initialized.");
             return null;
@@ -158,12 +153,20 @@
             Slog.e(TAG, "Cannot start handwriting session: Invalid request id: " + requestId);
             return null;
         }
-        if (!mRecordingGesture) {
+        if (!mRecordingGesture || mHandwritingBuffer.isEmpty()) {
             Slog.e(TAG, "Cannot start handwriting session: No stylus gesture is being recorded.");
             return null;
         }
         Objects.requireNonNull(mHandwritingEventReceiver,
                 "Handwriting session was already transferred to IME.");
+        final MotionEvent downEvent = mHandwritingBuffer.get(0);
+        assert (downEvent.getActionMasked() == MotionEvent.ACTION_DOWN);
+        if (!mWindowManagerInternal.isPointInsideWindow(
+                focusedWindowToken, mCurrentDisplayId, downEvent.getRawX(), downEvent.getRawY())) {
+            Slog.e(TAG, "Cannot start handwriting session: "
+                    + "Stylus gesture did not start inside the focused window.");
+            return null;
+        }
         if (DEBUG) Slog.d(TAG, "Starting handwriting session in display: " + mCurrentDisplayId);
 
         mInputManagerInternal.pilferPointers(mHandwritingSurface.getInputChannel().getToken());
@@ -226,13 +229,17 @@
         }
 
         if (!(ev instanceof MotionEvent)) {
-            Slog.e("Stylus", "Received non-motion event in stylus monitor.");
+            Slog.wtf(TAG, "Received non-motion event in stylus monitor.");
             return false;
         }
         final MotionEvent event = (MotionEvent) ev;
         if (!isStylusEvent(event)) {
             return false;
         }
+        if (event.getDisplayId() != mCurrentDisplayId) {
+            Slog.wtf(TAG, "Received stylus event associated with the incorrect display.");
+            return false;
+        }
 
         onStylusEvent(event);
         return true;
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index e62c5c1..1703310 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -30,6 +30,7 @@
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
 
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.InputMethodNavButtonFlags;
@@ -148,10 +149,11 @@
 
     @AnyThread
     void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo attribute,
-            boolean restarting, @InputMethodNavButtonFlags int navButtonFlags) {
+            boolean restarting, @InputMethodNavButtonFlags int navButtonFlags,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         try {
             mTarget.startInput(startInputToken, inputContext, attribute, restarting,
-                    navButtonFlags);
+                    navButtonFlags, imeDispatcher);
         } catch (RemoteException e) {
             logRemoteException(e);
         }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6af00b3..ea2b157 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -150,6 +150,7 @@
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
@@ -616,6 +617,12 @@
     IInputContext mCurInputContext;
 
     /**
+     * The {@link ImeOnBackInvokedDispatcher} last provided by the current client to
+     * receive {@link android.window.OnBackInvokedCallback}s forwarded from IME.
+     */
+    ImeOnBackInvokedDispatcher mCurImeDispatcher;
+
+    /**
      * The {@link IRemoteAccessibilityInputConnection} last provided by the current client.
      */
     @Nullable IRemoteAccessibilityInputConnection mCurRemoteAccessibilityInputConnection;
@@ -2623,7 +2630,7 @@
         final SessionState session = mCurClient.curSession;
         setEnabledSessionLocked(session);
         session.method.startInput(startInputToken, mCurInputContext, mCurAttribute, restarting,
-                navButtonFlags);
+                navButtonFlags, mCurImeDispatcher);
         if (mShowRequested) {
             if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
             showCurrentInputLocked(mCurFocusedWindow, getAppShowFlagsLocked(), null,
@@ -2733,7 +2740,8 @@
             @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
             @NonNull EditorInfo attribute, @StartInputFlags int startInputFlags,
             @StartInputReason int startInputReason,
-            int unverifiedTargetSdkVersion) {
+            int unverifiedTargetSdkVersion,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         // If no method is currently selected, do nothing.
         final String selectedMethodId = getSelectedMethodIdLocked();
         if (selectedMethodId == null) {
@@ -2777,6 +2785,7 @@
         mCurClient = cs;
         mCurInputContext = inputContext;
         mCurRemoteAccessibilityInputConnection = remoteAccessibilityInputConnection;
+        mCurImeDispatcher = imeDispatcher;
         mCurVirtualDisplayToScreenMatrix =
                 getVirtualDisplayToScreenMatrixLocked(cs.selfReportedDisplayId,
                         mDisplayIdToShowIme);
@@ -3780,10 +3789,12 @@
             @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
             int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
             IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
-            int unverifiedTargetSdkVersion) {
+            int unverifiedTargetSdkVersion,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         return startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken,
                 startInputFlags, softInputMode, windowFlags, attribute, inputContext,
-                remoteAccessibilityInputConnection, unverifiedTargetSdkVersion);
+                remoteAccessibilityInputConnection, unverifiedTargetSdkVersion,
+                imeDispatcher);
     }
 
     @NonNull
@@ -3792,7 +3803,8 @@
             @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
             int windowFlags, @Nullable EditorInfo attribute, @Nullable IInputContext inputContext,
             @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
-            int unverifiedTargetSdkVersion) {
+            int unverifiedTargetSdkVersion,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         if (windowToken == null) {
             Slog.e(TAG, "windowToken cannot be null.");
             return InputBindResult.NULL;
@@ -3829,7 +3841,7 @@
                     result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
                             client, windowToken, startInputFlags, softInputMode, windowFlags,
                             attribute, inputContext, remoteAccessibilityInputConnection,
-                            unverifiedTargetSdkVersion, userId);
+                            unverifiedTargetSdkVersion, userId, imeDispatcher);
                 } finally {
                     Binder.restoreCallingIdentity(ident);
                 }
@@ -3857,7 +3869,8 @@
             @SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute,
             IInputContext inputContext,
             @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
-            int unverifiedTargetSdkVersion, @UserIdInt int userId) {
+            int unverifiedTargetSdkVersion, @UserIdInt int userId,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
         if (DEBUG) {
             Slog.v(TAG, "startInputOrWindowGainedFocusInternalLocked: reason="
                     + InputMethodDebug.startInputReasonToString(startInputReason)
@@ -3868,7 +3881,8 @@
                     + InputMethodDebug.startInputFlagsToString(startInputFlags)
                     + " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
                     + " windowFlags=#" + Integer.toHexString(windowFlags)
-                    + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion);
+                    + " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion
+                    + " imeDispatcher=" + imeDispatcher);
         }
 
         final ClientState cs = mClients.get(client.asBinder());
@@ -3952,7 +3966,7 @@
             if (attribute != null) {
                 return startInputUncheckedLocked(cs, inputContext,
                         remoteAccessibilityInputConnection, attribute, startInputFlags,
-                        startInputReason, unverifiedTargetSdkVersion);
+                        startInputReason, unverifiedTargetSdkVersion, imeDispatcher);
             }
             return new InputBindResult(
                     InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
@@ -3993,7 +4007,8 @@
         if (isTextEditor && attribute != null
                 && shouldRestoreImeVisibility(windowToken, softInputMode)) {
             res = startInputUncheckedLocked(cs, inputContext, remoteAccessibilityInputConnection,
-                    attribute, startInputFlags, startInputReason, unverifiedTargetSdkVersion);
+                    attribute, startInputFlags, startInputReason, unverifiedTargetSdkVersion,
+                    imeDispatcher);
             showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
                     SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY);
             return res;
@@ -4033,7 +4048,8 @@
                     if (attribute != null) {
                         res = startInputUncheckedLocked(cs, inputContext,
                                 remoteAccessibilityInputConnection, attribute, startInputFlags,
-                                startInputReason, unverifiedTargetSdkVersion);
+                                startInputReason, unverifiedTargetSdkVersion,
+                                imeDispatcher);
                         didStart = true;
                     }
                     showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4065,7 +4081,8 @@
                         if (attribute != null) {
                             res = startInputUncheckedLocked(cs, inputContext,
                                     remoteAccessibilityInputConnection, attribute, startInputFlags,
-                                    startInputReason, unverifiedTargetSdkVersion);
+                                    startInputReason, unverifiedTargetSdkVersion,
+                                    imeDispatcher);
                             didStart = true;
                         }
                         showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4085,7 +4102,8 @@
                         if (attribute != null) {
                             res = startInputUncheckedLocked(cs, inputContext,
                                     remoteAccessibilityInputConnection, attribute, startInputFlags,
-                                    startInputReason, unverifiedTargetSdkVersion);
+                                    startInputReason, unverifiedTargetSdkVersion,
+                                    imeDispatcher);
                             didStart = true;
                         }
                         showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4115,7 +4133,8 @@
                 }
                 res = startInputUncheckedLocked(cs, inputContext,
                         remoteAccessibilityInputConnection, attribute, startInputFlags,
-                        startInputReason, unverifiedTargetSdkVersion);
+                        startInputReason, unverifiedTargetSdkVersion,
+                        imeDispatcher);
             } else {
                 res = InputBindResult.NULL_EDITOR_INFO;
             }
@@ -5099,9 +5118,8 @@
             case MSG_RESET_HANDWRITING: {
                 synchronized (ImfLock.class) {
                     if (mBindingController.supportsStylusHandwriting()
-                            && getCurMethodLocked() != null && mCurFocusedWindow != null) {
-                        mHwController.initializeHandwritingSpy(
-                                mCurTokenDisplayId, mCurFocusedWindow);
+                            && getCurMethodLocked() != null) {
+                        mHwController.initializeHandwritingSpy(mCurTokenDisplayId);
                     } else {
                         mHwController.reset();
                     }
@@ -5111,14 +5129,15 @@
             case MSG_START_HANDWRITING:
                 synchronized (ImfLock.class) {
                     IInputMethodInvoker curMethod = getCurMethodLocked();
-                    if (curMethod == null) {
+                    if (curMethod == null || mCurFocusedWindow == null) {
                         return true;
                     }
                     final HandwritingModeController.HandwritingSession session =
                             mHwController.startHandwritingSession(
                                     msg.arg1 /*requestId*/,
                                     msg.arg2 /*pid*/,
-                                    mBindingController.getCurMethodUid());
+                                    mBindingController.getCurMethodUid(),
+                                    mCurFocusedWindow);
                     if (session == null) {
                         Slog.e(TAG,
                                 "Failed to start handwriting session for requestId: " + msg.arg1);
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 92703ec..604e8f3 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -60,8 +60,8 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.NoSuchElementException;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * This is the system implementation of a Session. Apps will interact with the
@@ -1159,6 +1159,9 @@
         public void sendCommand(String packageName, int pid, int uid, String command, Bundle args,
                 ResultReceiver cb) {
             try {
+                final String reason = TAG + ":" + command;
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onCommand(packageName, pid, uid, command, args, cb);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in sendCommand.", e);
@@ -1168,6 +1171,9 @@
         public void sendCustomAction(String packageName, int pid, int uid, String action,
                 Bundle args) {
             try {
+                final String reason = TAG + ":custom-" + action;
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onCustomAction(packageName, pid, uid, action, args);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in sendCustomAction.", e);
@@ -1176,6 +1182,9 @@
 
         public void prepare(String packageName, int pid, int uid) {
             try {
+                final String reason = TAG + ":prepare";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPrepare(packageName, pid, uid);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in prepare.", e);
@@ -1185,6 +1194,9 @@
         public void prepareFromMediaId(String packageName, int pid, int uid, String mediaId,
                 Bundle extras) {
             try {
+                final String reason = TAG + ":prepareFromMediaId";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPrepareFromMediaId(packageName, pid, uid, mediaId, extras);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in prepareFromMediaId.", e);
@@ -1194,6 +1206,9 @@
         public void prepareFromSearch(String packageName, int pid, int uid, String query,
                 Bundle extras) {
             try {
+                final String reason = TAG + ":prepareFromSearch";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPrepareFromSearch(packageName, pid, uid, query, extras);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in prepareFromSearch.", e);
@@ -1202,6 +1217,9 @@
 
         public void prepareFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) {
             try {
+                final String reason = TAG + ":prepareFromUri";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPrepareFromUri(packageName, pid, uid, uri, extras);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in prepareFromUri.", e);
@@ -1210,6 +1228,9 @@
 
         public void play(String packageName, int pid, int uid) {
             try {
+                final String reason = TAG + ":play";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPlay(packageName, pid, uid);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in play.", e);
@@ -1219,6 +1240,9 @@
         public void playFromMediaId(String packageName, int pid, int uid, String mediaId,
                 Bundle extras) {
             try {
+                final String reason = TAG + ":playFromMediaId";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPlayFromMediaId(packageName, pid, uid, mediaId, extras);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in playFromMediaId.", e);
@@ -1228,6 +1252,9 @@
         public void playFromSearch(String packageName, int pid, int uid, String query,
                 Bundle extras) {
             try {
+                final String reason = TAG + ":playFromSearch";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPlayFromSearch(packageName, pid, uid, query, extras);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in playFromSearch.", e);
@@ -1236,6 +1263,9 @@
 
         public void playFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) {
             try {
+                final String reason = TAG + ":playFromUri";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPlayFromUri(packageName, pid, uid, uri, extras);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in playFromUri.", e);
@@ -1244,6 +1274,9 @@
 
         public void skipToTrack(String packageName, int pid, int uid, long id) {
             try {
+                final String reason = TAG + ":skipToTrack";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onSkipToTrack(packageName, pid, uid, id);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in skipToTrack", e);
@@ -1252,6 +1285,9 @@
 
         public void pause(String packageName, int pid, int uid) {
             try {
+                final String reason = TAG + ":pause";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPause(packageName, pid, uid);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in pause.", e);
@@ -1260,6 +1296,9 @@
 
         public void stop(String packageName, int pid, int uid) {
             try {
+                final String reason = TAG + ":stop";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onStop(packageName, pid, uid);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in stop.", e);
@@ -1268,6 +1307,9 @@
 
         public void next(String packageName, int pid, int uid) {
             try {
+                final String reason = TAG + ":next";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onNext(packageName, pid, uid);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in next.", e);
@@ -1276,6 +1318,9 @@
 
         public void previous(String packageName, int pid, int uid) {
             try {
+                final String reason = TAG + ":previous";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onPrevious(packageName, pid, uid);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in previous.", e);
@@ -1284,6 +1329,9 @@
 
         public void fastForward(String packageName, int pid, int uid) {
             try {
+                final String reason = TAG + ":fastForward";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onFastForward(packageName, pid, uid);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in fastForward.", e);
@@ -1292,6 +1340,9 @@
 
         public void rewind(String packageName, int pid, int uid) {
             try {
+                final String reason = TAG + ":rewind";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onRewind(packageName, pid, uid);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in rewind.", e);
@@ -1300,6 +1351,9 @@
 
         public void seekTo(String packageName, int pid, int uid, long pos) {
             try {
+                final String reason = TAG + ":seekTo";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onSeekTo(packageName, pid, uid, pos);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in seekTo.", e);
@@ -1308,6 +1362,9 @@
 
         public void rate(String packageName, int pid, int uid, Rating rating) {
             try {
+                final String reason = TAG + ":rate";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onRate(packageName, pid, uid, rating);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in rate.", e);
@@ -1316,6 +1373,9 @@
 
         public void setPlaybackSpeed(String packageName, int pid, int uid, float speed) {
             try {
+                final String reason = TAG + ":setPlaybackSpeed";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onSetPlaybackSpeed(packageName, pid, uid, speed);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in setPlaybackSpeed.", e);
@@ -1325,6 +1385,9 @@
         public void adjustVolume(String packageName, int pid, int uid, boolean asSystemService,
                 int direction) {
             try {
+                final String reason = TAG + ":adjustVolume";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 if (asSystemService) {
                     mCb.onAdjustVolume(mContext.getPackageName(), Process.myPid(),
                             Process.SYSTEM_UID, direction);
@@ -1338,6 +1401,9 @@
 
         public void setVolumeTo(String packageName, int pid, int uid, int value) {
             try {
+                final String reason = TAG + ":setVolumeTo";
+                mService.tempAllowlistTargetPkgIfPossible(getUid(), getPackageName(),
+                        pid, uid, packageName, reason);
                 mCb.onSetVolumeTo(packageName, pid, uid, value);
             } catch (RemoteException e) {
                 Log.e(TAG, "Remote failure in setVolumeTo.", e);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 83c576e..9042326 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -58,6 +58,7 @@
 import static android.content.Context.BIND_FOREGROUND_SERVICE;
 import static android.content.Context.BIND_NOT_PERCEPTIBLE;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
+import static android.content.pm.PackageManager.FEATURE_TELECOM;
 import static android.content.pm.PackageManager.FEATURE_TELEVISION;
 import static android.content.pm.PackageManager.MATCH_ALL;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
@@ -655,10 +656,10 @@
 
     private int mWarnRemoteViewsSizeBytes;
     private int mStripRemoteViewsSizeBytes;
-    final boolean mEnableAppSettingMigration;
     private boolean mForceUserSetOnUpgrade;
 
     private MetricsLogger mMetricsLogger;
+    private NotificationChannelLogger mNotificationChannelLogger;
     private TriPredicate<String, Integer, String> mAllowedManagedServicePackages;
 
     private final SavePolicyFileRunnable mSavePolicyFile = new SavePolicyFileRunnable();
@@ -1998,12 +1999,6 @@
         mNotificationRecordLogger = notificationRecordLogger;
         mNotificationInstanceIdSequence = notificationInstanceIdSequence;
         Notification.processAllowlistToken = ALLOWLIST_TOKEN;
-        // TODO (b/194833441): remove when OS is ready for migration. This flag is checked once
-        // rather than having a settings observer because some of the behaviors (e.g. readXml) only
-        // happen on reboot
-        mEnableAppSettingMigration = Settings.Secure.getIntForUser(
-                getContext().getContentResolver(),
-                Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 0, USER_SYSTEM) == 1;
     }
 
     // TODO - replace these methods with new fields in the VisibleForTesting constructor
@@ -2161,6 +2156,11 @@
         mAccessibilityManager = am;
     }
 
+    @VisibleForTesting
+    void setTelecomManager(TelecomManager tm) {
+        mTelecomManager = tm;
+    }
+
     // TODO: All tests should use this init instead of the one-off setters above.
     @VisibleForTesting
     void init(WorkerHandler handler, RankingHandler rankingHandler,
@@ -2178,7 +2178,7 @@
             TelephonyManager telephonyManager, ActivityManagerInternal ami,
             MultiRateLimiter toastRateLimiter, PermissionHelper permissionHelper,
             UsageStatsManagerInternal usageStatsManagerInternal,
-            TelecomManager telecomManager) {
+            TelecomManager telecomManager, NotificationChannelLogger channelLogger) {
         mHandler = handler;
         Resources resources = getContext().getResources();
         mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
@@ -2275,12 +2275,13 @@
             }
         });
         mPermissionHelper = permissionHelper;
+        mNotificationChannelLogger = channelLogger;
         mPreferencesHelper = new PreferencesHelper(getContext(),
                 mPackageManagerClient,
                 mRankingHandler,
                 mZenModeHelper,
                 mPermissionHelper,
-                new NotificationChannelLoggerImpl(),
+                mNotificationChannelLogger,
                 mAppOps,
                 new SysUiStatsEvent.BuilderFactory());
         mRankingHelper = new RankingHelper(getContext(),
@@ -2362,9 +2363,6 @@
         mNotificationEffectsEnabledForAutomotive =
                 resources.getBoolean(R.bool.config_enableServerNotificationEffectsForAutomotive);
 
-        mPreferencesHelper.lockChannelsForOEM(getContext().getResources().getStringArray(
-                com.android.internal.R.array.config_nonBlockableNotificationPackages));
-
         mZenModeHelper.setPriorityOnlyDndExemptPackages(getContext().getResources().getStringArray(
                 com.android.internal.R.array.config_priorityOnlyDndExemptPackages));
 
@@ -2504,10 +2502,11 @@
                 LocalServices.getService(ActivityManagerInternal.class),
                 createToastRateLimiter(), new PermissionHelper(LocalServices.getService(
                         PermissionManagerServiceInternal.class), AppGlobals.getPackageManager(),
-                        AppGlobals.getPermissionManager(), mEnableAppSettingMigration,
+                        AppGlobals.getPermissionManager(),
                         mForceUserSetOnUpgrade),
                 LocalServices.getService(UsageStatsManagerInternal.class),
-                getContext().getSystemService(TelecomManager.class));
+                getContext().getSystemService(TelecomManager.class),
+                new NotificationChannelLoggerImpl());
 
         publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
                 DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
@@ -2799,7 +2798,7 @@
         }
     }
 
-    private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
+    void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel,
             boolean fromListener) {
         if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
             // cancel
@@ -2821,11 +2820,9 @@
                 mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), true);
 
         mPreferencesHelper.updateNotificationChannel(pkg, uid, channel, true);
-        if (mEnableAppSettingMigration) {
-            if (mPreferencesHelper.onlyHasDefaultChannel(pkg, uid)) {
-                mPermissionHelper.setNotificationPermission(pkg, UserHandle.getUserId(uid),
-                        channel.getImportance() != IMPORTANCE_NONE, true);
-            }
+        if (mPreferencesHelper.onlyHasDefaultChannel(pkg, uid)) {
+            mPermissionHelper.setNotificationPermission(pkg, UserHandle.getUserId(uid),
+                    channel.getImportance() != IMPORTANCE_NONE, true);
         }
         maybeNotifyChannelOwner(pkg, uid, preUpdate, channel);
 
@@ -3474,36 +3471,19 @@
         @Override
         public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
             enforceSystemOrSystemUI("setNotificationsEnabledForPackage");
-            if (mEnableAppSettingMigration) {
-                boolean wasEnabled = mPermissionHelper.hasPermission(uid);
-                if (wasEnabled == enabled) {
-                    return;
-                }
-                mPermissionHelper.setNotificationPermission(
-                        pkg, UserHandle.getUserId(uid), enabled, true);
-                sendAppBlockStateChangedBroadcast(pkg, uid, !enabled);
-            } else {
-                synchronized (mNotificationLock) {
-                    boolean wasEnabled = mPreferencesHelper.getImportance(pkg, uid)
-                            != NotificationManager.IMPORTANCE_NONE;
-
-                    if (wasEnabled == enabled) {
-                        return;
-                    }
-                }
-
-                mPreferencesHelper.setEnabled(pkg, uid, enabled);
-                // TODO (b/194833441): this is being ignored by app ops now that the permission
-                // exists, so send the broadcast manually
-                mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
-                        enabled ? MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
-
-                sendAppBlockStateChangedBroadcast(pkg, uid, !enabled);
+            boolean wasEnabled = mPermissionHelper.hasPermission(uid);
+            if (wasEnabled == enabled) {
+                return;
             }
+            mPermissionHelper.setNotificationPermission(
+                    pkg, UserHandle.getUserId(uid), enabled, true);
+            sendAppBlockStateChangedBroadcast(pkg, uid, !enabled);
+
             mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_BAN_APP_NOTES)
                     .setType(MetricsEvent.TYPE_ACTION)
                     .setPackageName(pkg)
                     .setSubtype(enabled ? 1 : 0));
+            mNotificationChannelLogger.logAppNotificationsAllowed(uid, pkg, enabled);
             // Now, cancel any outstanding notifications that are part of a just-disabled app
             if (!enabled) {
                 cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true,
@@ -3529,8 +3509,6 @@
         public void setNotificationsEnabledWithImportanceLockForPackage(
                 String pkg, int uid, boolean enabled) {
             setNotificationsEnabledForPackage(pkg, uid, enabled);
-
-            mPreferencesHelper.setAppImportanceLocked(pkg, uid);
         }
 
         /**
@@ -3646,14 +3624,10 @@
         @Override
         public int getPackageImportance(String pkg) {
             checkCallerIsSystemOrSameApp(pkg);
-            if (mEnableAppSettingMigration) {
-                if (mPermissionHelper.hasPermission(Binder.getCallingUid())) {
-                    return IMPORTANCE_DEFAULT;
-                } else {
-                    return IMPORTANCE_NONE;
-                }
+            if (mPermissionHelper.hasPermission(Binder.getCallingUid())) {
+                return IMPORTANCE_DEFAULT;
             } else {
-                return mPreferencesHelper.getImportance(pkg, Binder.getCallingUid());
+                return IMPORTANCE_NONE;
             }
         }
 
@@ -5885,8 +5859,7 @@
     NotificationRecord createAutoGroupSummary(int userId, String pkg, String triggeringKey,
             boolean needsOngoingFlag) {
         NotificationRecord summaryRecord = null;
-        boolean isPermissionFixed = mPermissionHelper.isMigrationEnabled()
-                ? mPermissionHelper.isPermissionFixed(pkg, userId) : false;
+        boolean isPermissionFixed = mPermissionHelper.isPermissionFixed(pkg, userId);
         synchronized (mNotificationLock) {
             NotificationRecord notificationRecord = mNotificationsByKey.get(triggeringKey);
             if (notificationRecord == null) {
@@ -5895,10 +5868,6 @@
                 return null;
             }
             NotificationChannel channel = notificationRecord.getChannel();
-            boolean isImportanceFixed = mPermissionHelper.isMigrationEnabled()
-                    ? isPermissionFixed
-                    : (channel.isImportanceLockedByOEM()
-                            || channel.isImportanceLockedByCriticalDeviceFunction());
             final StatusBarNotification adjustedSbn = notificationRecord.getSbn();
             userId = adjustedSbn.getUser().getIdentifier();
             int uid =  adjustedSbn.getUid();
@@ -5944,7 +5913,7 @@
                                 System.currentTimeMillis());
                 summaryRecord = new NotificationRecord(getContext(), summarySbn,
                         notificationRecord.getChannel());
-                summaryRecord.setImportanceFixed(isImportanceFixed);
+                summaryRecord.setImportanceFixed(isPermissionFixed);
                 summaryRecord.setIsAppImportanceLocked(
                         notificationRecord.getIsAppImportanceLocked());
                 summaries.put(pkg, summarySbn.getKey());
@@ -5992,10 +5961,6 @@
     @VisibleForTesting
     protected ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>>
             getAllUsersNotificationPermissions() {
-        // don't bother if migration is not enabled
-        if (!mEnableAppSettingMigration) {
-            return null;
-        }
         ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> allPermissions = new ArrayMap<>();
         final List<UserInfo> allUsers = mUm.getUsers();
         // for each of these, get the package notification permissions that are associated
@@ -6509,13 +6474,8 @@
                     + ", notificationUid=" + notificationUid
                     + ", notification=" + notification;
             Slog.e(TAG, noChannelStr);
-            boolean appNotificationsOff;
-            if (mEnableAppSettingMigration) {
-                appNotificationsOff = !mPermissionHelper.hasPermission(notificationUid);
-            } else {
-                appNotificationsOff = mPreferencesHelper.getImportance(pkg, notificationUid)
-                        == NotificationManager.IMPORTANCE_NONE;
-            }
+            boolean appNotificationsOff = !mPermissionHelper.hasPermission(notificationUid);
+
 
             if (!appNotificationsOff) {
                 doChannelWarningToast(notificationUid,
@@ -6527,14 +6487,11 @@
         }
 
         final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
-        r.setIsAppImportanceLocked(mPreferencesHelper.getIsAppImportanceLocked(pkg, callingUid));
+        r.setIsAppImportanceLocked(mPermissionHelper.isPermissionUserSet(pkg, userId));
         r.setPostSilently(postSilently);
         r.setFlagBubbleRemoved(false);
         r.setPkgAllowedAsConvo(mMsgPkgsAllowedAsConvos.contains(pkg));
-        boolean isImportanceFixed = mPermissionHelper.isMigrationEnabled()
-                ? mPermissionHelper.isPermissionFixed(pkg, userId)
-                : (channel.isImportanceLockedByOEM()
-                        || channel.isImportanceLockedByCriticalDeviceFunction());
+        boolean isImportanceFixed = mPermissionHelper.isPermissionFixed(pkg, userId);
         r.setImportanceFixed(isImportanceFixed);
 
         if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
@@ -6978,19 +6935,20 @@
     private boolean isCallNotification(String pkg, int uid) {
         final long identity = Binder.clearCallingIdentity();
         try {
-            return mTelecomManager.isInManagedCall() || mTelecomManager.isInSelfManagedCall(
-                    pkg, UserHandle.getUserHandleForUid(uid));
+            if (mPackageManagerClient.hasSystemFeature(FEATURE_TELECOM)
+                    && mTelecomManager != null) {
+                return mTelecomManager.isInManagedCall()
+                        || mTelecomManager.isInSelfManagedCall(
+                                pkg, UserHandle.getUserHandleForUid(uid));
+            }
+            return false;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
 
     private boolean areNotificationsEnabledForPackageInt(String pkg, int uid) {
-        if (mEnableAppSettingMigration) {
-            return mPermissionHelper.hasPermission(uid);
-        } else {
-            return mPreferencesHelper.getImportance(pkg, uid) != IMPORTANCE_NONE;
-        }
+        return mPermissionHelper.hasPermission(uid);
     }
 
     protected int getNotificationCount(String pkg, int userId, int excludedId,
@@ -7405,6 +7363,7 @@
         @Override
         public void run() {
             boolean appBanned = !areNotificationsEnabledForPackageInt(pkg, uid);
+            boolean isCallNotification = isCallNotification(pkg, uid);
             synchronized (mNotificationLock) {
                 try {
                     NotificationRecord r = null;
@@ -7423,8 +7382,10 @@
 
                     final StatusBarNotification n = r.getSbn();
                     final Notification notification = n.getNotification();
+                    boolean isCallNotificationAndCorrectStyle = isCallNotification
+                            && notification.isStyle(Notification.CallStyle.class);
 
-                    if (!notification.isMediaNotification()
+                    if (!(notification.isMediaNotification() || isCallNotificationAndCorrectStyle)
                             && (appBanned || isRecordBlockedLocked(r))) {
                         mUsageStats.registerBlocked(r);
                         if (DBG) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f979343..cbaf485 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -1089,7 +1089,7 @@
     }
 
     /**
-     * @see PreferencesHelper#getIsAppImportanceLocked(String, int)
+     * @see PermissionHelper#isPermissionUserSet(String, int)
      */
     public boolean getIsAppImportanceLocked() {
         return mIsAppImportanceLocked;
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index b4230c1..b2fee1e 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -55,30 +55,21 @@
     private final PermissionManagerServiceInternal mPmi;
     private final IPackageManager mPackageManager;
     private final IPermissionManager mPermManager;
-    // TODO (b/194833441): Remove when the migration is enabled
-    private final boolean mMigrationEnabled;
     private final boolean mForceUserSetOnUpgrade;
 
     public PermissionHelper(PermissionManagerServiceInternal pmi, IPackageManager packageManager,
-            IPermissionManager permManager, boolean migrationEnabled,
-            boolean forceUserSetOnUpgrade) {
+            IPermissionManager permManager, boolean forceUserSetOnUpgrade) {
         mPmi = pmi;
         mPackageManager = packageManager;
         mPermManager = permManager;
-        mMigrationEnabled = migrationEnabled;
         mForceUserSetOnUpgrade = forceUserSetOnUpgrade;
     }
 
-    public boolean isMigrationEnabled() {
-        return mMigrationEnabled;
-    }
-
     /**
      * Returns whether the given uid holds the notification permission. Must not be called
      * with a lock held.
      */
     public boolean hasPermission(int uid) {
-        assertFlag();
         final long callingId = Binder.clearCallingIdentity();
         try {
             return mPmi.checkPostNotificationsPermissionGrantedOrLegacyAccess(uid)
@@ -93,7 +84,6 @@
      * Must not be called with a lock held. Format: uid, packageName
      */
     Set<Pair<Integer, String>> getAppsRequestingPermission(int userId) {
-        assertFlag();
         Set<Pair<Integer, String>> requested = new HashSet<>();
         List<PackageInfo> pkgs = getInstalledPackages(userId);
         for (PackageInfo pi : pkgs) {
@@ -131,7 +121,6 @@
      * with a lock held. Format: uid, packageName.
      */
     Set<Pair<Integer, String>> getAppsGrantedPermission(int userId) {
-        assertFlag();
         Set<Pair<Integer, String>> granted = new HashSet<>();
         ParceledListSlice<PackageInfo> parceledList = null;
         try {
@@ -153,7 +142,6 @@
     public @NonNull
             ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>>
                     getNotificationPermissionValues(int userId) {
-        assertFlag();
         ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> notifPermissions = new ArrayMap<>();
         Set<Pair<Integer, String>> allRequestingUids = getAppsRequestingPermission(userId);
         Set<Pair<Integer, String>> allApprovedUids = getAppsGrantedPermission(userId);
@@ -180,7 +168,6 @@
      */
     public void setNotificationPermission(String packageName, @UserIdInt int userId, boolean grant,
             boolean userSet, boolean reviewRequired) {
-        assertFlag();
         final long callingId = Binder.clearCallingIdentity();
         try {
             // Do not change the permission if the package doesn't request it, do not change fixed
@@ -221,7 +208,6 @@
      * restoring a pre-T backup on a T+ device
      */
     public void setNotificationPermission(PackagePermission pkgPerm) {
-        assertFlag();
         if (pkgPerm == null || pkgPerm.packageName == null) {
             return;
         }
@@ -233,7 +219,6 @@
     }
 
     public boolean isPermissionFixed(String packageName, @UserIdInt int userId) {
-        assertFlag();
         final long callingId = Binder.clearCallingIdentity();
         try {
             try {
@@ -251,7 +236,6 @@
     }
 
     boolean isPermissionUserSet(String packageName, @UserIdInt int userId) {
-        assertFlag();
         final long callingId = Binder.clearCallingIdentity();
         try {
             try {
@@ -269,7 +253,6 @@
     }
 
     boolean isPermissionGrantedByDefaultOrRole(String packageName, @UserIdInt int userId) {
-        assertFlag();
         final long callingId = Binder.clearCallingIdentity();
         try {
             try {
@@ -288,7 +271,6 @@
 
     private boolean packageRequestsNotificationPermission(String packageName,
             @UserIdInt int userId) {
-        assertFlag();
         try {
             String[] permissions = mPackageManager.getPackageInfo(packageName, GET_PERMISSIONS,
                     userId).requestedPermissions;
@@ -299,12 +281,6 @@
         return false;
     }
 
-    private void assertFlag() {
-        if (!mMigrationEnabled) {
-            throw new IllegalStateException("Method called without checking flag value");
-        }
-    }
-
     public static class PackagePermission {
         public final String packageName;
         public final @UserIdInt int userId;
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index ef3c770..4e3fbaa 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -209,11 +209,7 @@
         mAppOps = appOpsManager;
         mStatsEventBuilderFactory = statsEventBuilderFactory;
 
-        if (mPermissionHelper.isMigrationEnabled()) {
-            XML_VERSION = 4;
-        } else {
-            XML_VERSION = 2;
-        }
+        XML_VERSION = 4;
 
         updateBadgingEnabled();
         updateBubblesEnabled();
@@ -230,8 +226,7 @@
 
         final int xmlVersion = parser.getAttributeInt(null, ATT_VERSION, -1);
         boolean upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE;
-        boolean migrateToPermission = (xmlVersion < XML_VERSION_NOTIF_PERMISSION)
-                && mPermissionHelper.isMigrationEnabled();
+        boolean migrateToPermission = (xmlVersion < XML_VERSION_NOTIF_PERMISSION);
         if (xmlVersion < XML_VERSION_REVIEW_PERMISSIONS_NOTIFICATION) {
             // make a note that we should show the notification at some point.
             // it shouldn't be possible for the user to already have seen it, as the XML version
@@ -393,8 +388,6 @@
                             hasUserConfiguredSettings(r));
                     pkgPerms.add(pkgPerm);
                 }
-            } else if (!mPermissionHelper.isMigrationEnabled()) {
-                r.importance = appImportance;
             }
         } catch (Exception e) {
             Slog.w(TAG, "Failed to restore pkg", e);
@@ -417,16 +410,8 @@
                 } else {
                     channel.populateFromXml(parser);
                 }
-                if (!mPermissionHelper.isMigrationEnabled()) {
-                    channel.setImportanceLockedByCriticalDeviceFunction(
-                            r.defaultAppLockedImportance);
-                    channel.setImportanceLockedByOEM(r.oemLockedImportance);
-                    if (!channel.isImportanceLockedByOEM()) {
-                        if (r.oemLockedChannels.contains(channel.getId())) {
-                            channel.setImportanceLockedByOEM(true);
-                        }
-                    }
-                }
+                channel.setImportanceLockedByCriticalDeviceFunction(
+                        r.defaultAppLockedImportance);
 
                 if (isShortcutOk(channel) && isDeletionOk(channel)) {
                     r.channels.put(id, channel);
@@ -604,7 +589,7 @@
             out.endTag(null, TAG_STATUS_ICONS);
         }
         ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> notifPermissions = new ArrayMap<>();
-        if (mPermissionHelper.isMigrationEnabled() && forBackup) {
+        if (forBackup) {
             notifPermissions = mPermissionHelper.getNotificationPermissionValues(userId);
         }
 
@@ -736,28 +721,6 @@
         }
     }
 
-    /**
-     * Gets importance.
-     */
-    @Override
-    public int getImportance(String packageName, int uid) {
-        synchronized (mPackagePreferences) {
-            return getOrCreatePackagePreferencesLocked(packageName, uid).importance;
-        }
-    }
-
-    /**
-     * Returns whether the importance of the corresponding notification is user-locked and shouldn't
-     * be adjusted by an assistant (via means of a blocking helper, for example). For the channel
-     * locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}.
-     */
-    public boolean getIsAppImportanceLocked(String packageName, int uid) {
-        synchronized (mPackagePreferences) {
-            int userLockedFields = getOrCreatePackagePreferencesLocked(packageName, uid).lockedAppFields;
-            return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
-        }
-    }
-
     @Override
     public boolean canShowBadge(String packageName, int uid) {
         synchronized (mPackagePreferences) {
@@ -1043,16 +1006,10 @@
                             : NotificationChannel.DEFAULT_ALLOW_BUBBLE);
                 }
                 clearLockedFieldsLocked(channel);
-                if (!mPermissionHelper.isMigrationEnabled()) {
-                    channel.setImportanceLockedByOEM(r.oemLockedImportance);
-                    if (!channel.isImportanceLockedByOEM()) {
-                        if (r.oemLockedChannels.contains(channel.getId())) {
-                            channel.setImportanceLockedByOEM(true);
-                        }
-                    }
-                    channel.setImportanceLockedByCriticalDeviceFunction(
-                            r.defaultAppLockedImportance);
-                }
+
+                channel.setImportanceLockedByCriticalDeviceFunction(
+                        r.defaultAppLockedImportance);
+
                 if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
                     channel.setLockscreenVisibility(
                             NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
@@ -1133,33 +1090,15 @@
                 updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
             }
 
-            if (mPermissionHelper.isMigrationEnabled()) {
-                if (mPermissionHelper.isPermissionFixed(r.pkg, UserHandle.getUserId(r.uid))
-                        && !(channel.isBlockable() || channel.getImportance() == IMPORTANCE_NONE)) {
-                    updatedChannel.setImportance(channel.getImportance());
-                }
-            } else {
-                // no importance updates are allowed if OEM blocked it
-                updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM());
-                if (updatedChannel.isImportanceLockedByOEM()) {
-                    updatedChannel.setImportance(channel.getImportance());
-                }
-                updatedChannel.setImportanceLockedByCriticalDeviceFunction(
-                        r.defaultAppLockedImportance);
-                if (updatedChannel.isImportanceLockedByCriticalDeviceFunction()
-                        && updatedChannel.getImportance() == IMPORTANCE_NONE) {
-                    updatedChannel.setImportance(channel.getImportance());
-                }
+            if ((mPermissionHelper.isPermissionFixed(r.pkg, UserHandle.getUserId(r.uid))
+                    || channel.isImportanceLockedByCriticalDeviceFunction())
+                    && !(channel.isBlockable() || channel.getImportance() == IMPORTANCE_NONE)) {
+                updatedChannel.setImportance(channel.getImportance());
             }
 
             r.channels.put(updatedChannel.getId(), updatedChannel);
 
             if (onlyHasDefaultChannel(pkg, uid)) {
-                if (!mPermissionHelper.isMigrationEnabled()) {
-                    // copy settings to app level so they are inherited by new channels
-                    // when the app migrates
-                    r.importance = updatedChannel.getImportance();
-                }
                 r.priority = updatedChannel.canBypassDnd()
                         ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
                 r.visibility = updatedChannel.getLockscreenVisibility();
@@ -1328,61 +1267,8 @@
         mHideSilentStatusBarIcons = hide;
     }
 
-    public void lockChannelsForOEM(String[] appOrChannelList) {
-        if (mPermissionHelper.isMigrationEnabled()) {
-            return;
-        }
-        if (appOrChannelList == null) {
-            return;
-        }
-        for (String appOrChannel : appOrChannelList) {
-            if (!TextUtils.isEmpty(appOrChannel)) {
-                String[] appSplit = appOrChannel.split(NON_BLOCKABLE_CHANNEL_DELIM);
-                if (appSplit != null && appSplit.length > 0) {
-                    String appName = appSplit[0];
-                    String channelId = appSplit.length == 2 ? appSplit[1] : null;
-
-                    synchronized (mPackagePreferences) {
-                        boolean foundApp = false;
-                        for (PackagePreferences r : mPackagePreferences.values()) {
-                            if (r.pkg.equals(appName)) {
-                                foundApp = true;
-                                if (channelId == null) {
-                                    // lock all channels for the app
-                                    r.oemLockedImportance = true;
-                                    for (NotificationChannel channel : r.channels.values()) {
-                                        channel.setImportanceLockedByOEM(true);
-                                    }
-                                } else {
-                                    NotificationChannel channel = r.channels.get(channelId);
-                                    if (channel != null) {
-                                        channel.setImportanceLockedByOEM(true);
-                                    }
-                                    // Also store the locked channels on the record, so they aren't
-                                    // temporarily lost when data is cleared on the package
-                                    r.oemLockedChannels.add(channelId);
-                                }
-                            }
-                        }
-                        if (!foundApp) {
-                            List<String> channels =
-                                    mOemLockedApps.getOrDefault(appName, new ArrayList<>());
-                            if (channelId != null) {
-                                channels.add(channelId);
-                            }
-                            mOemLockedApps.put(appName, channels);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
     public void updateDefaultApps(int userId, ArraySet<String> toRemove,
             ArraySet<Pair<String, Integer>> toAdd) {
-        if (mPermissionHelper.isMigrationEnabled()) {
-            return;
-        }
         synchronized (mPackagePreferences) {
             for (PackagePreferences p : mPackagePreferences.values()) {
                 if (userId == UserHandle.getUserId(p.uid)) {
@@ -1802,20 +1688,8 @@
         }
         for (int i = candidatePkgs.size() - 1; i >= 0; i--) {
             Pair<String, Integer> app = candidatePkgs.valueAt(i);
-            if (mPermissionHelper.isMigrationEnabled()) {
-                if (!mPermissionHelper.hasPermission(app.second)) {
-                    candidatePkgs.removeAt(i);
-                }
-            } else {
-                synchronized (mPackagePreferences) {
-                    PackagePreferences r = getPackagePreferencesLocked(app.first, app.second);
-                    if (r == null) {
-                        continue;
-                    }
-                    if (r.importance == IMPORTANCE_NONE) {
-                        candidatePkgs.removeAt(i);
-                    }
-                }
+            if (!mPermissionHelper.hasPermission(app.second)) {
+                candidatePkgs.removeAt(i);
             }
         }
         boolean haveBypassingApps = candidatePkgs.size() > 0;
@@ -1861,27 +1735,6 @@
     }
 
     /**
-     * Sets importance.
-     */
-    @Override
-    public void setImportance(String pkgName, int uid, int importance) {
-        synchronized (mPackagePreferences) {
-            getOrCreatePackagePreferencesLocked(pkgName, uid).importance = importance;
-        }
-        updateConfig();
-    }
-
-    public void setEnabled(String packageName, int uid, boolean enabled) {
-        boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE;
-        if (wasEnabled == enabled) {
-            return;
-        }
-        setImportance(packageName, uid,
-                enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE);
-        mNotificationChannelLogger.logAppNotificationsAllowed(uid, packageName, enabled);
-    }
-
-    /**
      * Sets whether any notifications from the app, represented by the given {@code pkgName} and
      * {@code uid}, have their importance locked by the user. Locked notifications don't get
      * considered for sentiment adjustments (and thus never show a blocking helper).
@@ -2055,23 +1908,15 @@
                 pw.print(" (");
                 pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
                 pw.print(')');
-                if (!mPermissionHelper.isMigrationEnabled()) {
-                    if (r.importance != DEFAULT_IMPORTANCE) {
-                        pw.print(" importance=");
-                        pw.print(NotificationListenerService.Ranking.importanceToString(
-                                r.importance));
-                    }
-                } else {
-                    Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
-                    if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
-                        pw.print(" importance=");
-                        pw.print(NotificationListenerService.Ranking.importanceToString(
-                                packagePermissions.get(key).first
-                                        ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
-                        pw.print(" userSet=");
-                        pw.print(packagePermissions.get(key).second);
-                        pkgsWithPermissionsToHandle.remove(key);
-                    }
+                Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
+                if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
+                    pw.print(" importance=");
+                    pw.print(NotificationListenerService.Ranking.importanceToString(
+                            packagePermissions.get(key).first
+                                    ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
+                    pw.print(" userSet=");
+                    pw.print(packagePermissions.get(key).second);
+                    pkgsWithPermissionsToHandle.remove(key);
                 }
                 if (r.priority != DEFAULT_PRIORITY) {
                     pw.print(" priority=");
@@ -2111,7 +1956,7 @@
             }
         }
         // Handle any remaining packages with permissions
-        if (mPermissionHelper.isMigrationEnabled() && pkgsWithPermissionsToHandle != null) {
+        if (pkgsWithPermissionsToHandle != null) {
             for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) {
                 // p.first is the uid of this package; p.second is the package name
                 if (filter.matches(p.second)) {
@@ -2151,16 +1996,12 @@
 
                 proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg);
                 proto.write(RankingHelperProto.RecordProto.UID, r.uid);
-                if (mPermissionHelper.isMigrationEnabled()) {
-                    Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
-                    if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
-                        proto.write(RankingHelperProto.RecordProto.IMPORTANCE,
-                                packagePermissions.get(key).first
-                                        ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
-                        pkgsWithPermissionsToHandle.remove(key);
-                    }
-                } else {
-                    proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance);
+                Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
+                if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
+                    proto.write(RankingHelperProto.RecordProto.IMPORTANCE,
+                            packagePermissions.get(key).first
+                                    ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
+                    pkgsWithPermissionsToHandle.remove(key);
                 }
                 proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority);
                 proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility);
@@ -2177,7 +2018,7 @@
             }
         }
 
-        if (mPermissionHelper.isMigrationEnabled() && pkgsWithPermissionsToHandle != null) {
+        if (pkgsWithPermissionsToHandle != null) {
             for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) {
                 if (filter.matches(p.second)) {
                     fToken = proto.start(fieldId);
@@ -2217,25 +2058,22 @@
                 // collect whether this package's importance info was user-set for later, if needed
                 // before the migration is enabled, this will simply default to false in all cases.
                 boolean importanceIsUserSet = false;
-                if (mPermissionHelper.isMigrationEnabled()) {
-                    // Even if this package's data is not present, we need to write something;
-                    // so default to IMPORTANCE_NONE, since if PM doesn't know about the package
-                    // for some reason, notifications are not allowed.
-                    int importance = IMPORTANCE_NONE;
-                    Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
-                    if (pkgPermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
-                        Pair<Boolean, Boolean> permissionPair = pkgPermissions.get(key);
-                        importance = permissionPair.first
-                                ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE;
-                        // cache the second value for writing later
-                        importanceIsUserSet = permissionPair.second;
+                // Even if this package's data is not present, we need to write something;
+                // so default to IMPORTANCE_NONE, since if PM doesn't know about the package
+                // for some reason, notifications are not allowed.
+                int importance = IMPORTANCE_NONE;
+                Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
+                if (pkgPermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
+                    Pair<Boolean, Boolean> permissionPair = pkgPermissions.get(key);
+                    importance = permissionPair.first
+                            ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE;
+                    // cache the second value for writing later
+                    importanceIsUserSet = permissionPair.second;
 
-                        pkgsWithPermissionsToHandle.remove(key);
-                    }
-                    event.writeInt(importance);
-                } else {
-                    event.writeInt(r.importance);
+                    pkgsWithPermissionsToHandle.remove(key);
                 }
+                event.writeInt(importance);
+
                 event.writeInt(r.visibility);
                 event.writeInt(r.lockedAppFields);
                 event.writeBoolean(importanceIsUserSet);  // optional bool user_set_importance = 5;
@@ -2244,7 +2082,7 @@
         }
 
         // handle remaining packages with PackageManager permissions but not local settings
-        if (mPermissionHelper.isMigrationEnabled() && pkgPermissions != null) {
+        if (pkgPermissions != null) {
             for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) {
                 if (pulledEvents > NOTIFICATION_PREFERENCES_PULL_LIMIT) {
                     break;
@@ -2357,22 +2195,14 @@
                     try {
                         PackagePreferences.put("userId", UserHandle.getUserId(r.uid));
                         PackagePreferences.put("packageName", r.pkg);
-                        if (mPermissionHelper.isMigrationEnabled()) {
-                            Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
-                            if (pkgPermissions != null
-                                    && pkgsWithPermissionsToHandle.contains(key)) {
-                                PackagePreferences.put("importance",
-                                        NotificationListenerService.Ranking.importanceToString(
-                                                pkgPermissions.get(key).first
-                                                        ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
-                                pkgsWithPermissionsToHandle.remove(key);
-                            }
-                        } else {
-                            if (r.importance != DEFAULT_IMPORTANCE) {
-                                PackagePreferences.put("importance",
-                                        NotificationListenerService.Ranking.importanceToString(
-                                                r.importance));
-                            }
+                        Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
+                        if (pkgPermissions != null
+                                && pkgsWithPermissionsToHandle.contains(key)) {
+                            PackagePreferences.put("importance",
+                                    NotificationListenerService.Ranking.importanceToString(
+                                            pkgPermissions.get(key).first
+                                                    ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
+                            pkgsWithPermissionsToHandle.remove(key);
                         }
                         if (r.priority != DEFAULT_PRIORITY) {
                             PackagePreferences.put("priority",
@@ -2404,7 +2234,7 @@
         }
 
         // handle packages for which there are permissions but no local settings
-        if (mPermissionHelper.isMigrationEnabled() && pkgsWithPermissionsToHandle != null) {
+        if (pkgsWithPermissionsToHandle != null) {
             for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) {
                 if (filter == null || filter.matches(p.second)) {
                     JSONObject PackagePreferences = new JSONObject();
@@ -2443,8 +2273,7 @@
     public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter,
             ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
         JSONArray bans = new JSONArray();
-        Map<Integer, String> packageBans = mPermissionHelper.isMigrationEnabled()
-                ? getPermissionBasedPackageBans(pkgPermissions) : getPackageBans();
+        Map<Integer, String> packageBans = getPermissionBasedPackageBans(pkgPermissions);
         for (Map.Entry<Integer, String> ban : packageBans.entrySet()) {
             final int userId = UserHandle.getUserId(ban.getKey());
             final String packageName = ban.getValue();
@@ -2597,7 +2426,7 @@
                         synchronized (mPackagePreferences) {
                             mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r);
                         }
-                        if (mPermissionHelper.isMigrationEnabled() && r.migrateToPm) {
+                        if (r.migrateToPm) {
                             try {
                                 PackagePermission p = new PackagePermission(
                                         r.pkg, UserHandle.getUserId(r.uid),
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 3982593..3e9d90c 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -24,8 +24,6 @@
 
 public interface RankingConfig {
 
-    void setImportance(String packageName, int uid, int importance);
-    int getImportance(String packageName, int uid);
     void setShowBadge(String packageName, int uid, boolean showBadge);
     boolean canShowBadge(String packageName, int uid);
     boolean badgingEnabled(UserHandle userHandle);
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 2824585..3911994 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -34,13 +34,14 @@
 import android.content.pm.ApkChecksum;
 import android.content.pm.Checksum;
 import android.content.pm.IOnChecksumsReadyListener;
-import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.parsing.ApkLiteParseUtils;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
+import android.os.Environment;
+import android.os.FileUtils;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -63,7 +64,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.security.VerityUtils;
-import com.android.server.LocalServices;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.io.ByteArrayOutputStream;
@@ -637,9 +637,18 @@
         return null;
     }
 
+    private static boolean containsFile(File dir, String filePath) {
+        if (dir == null) {
+            return false;
+        }
+        return FileUtils.contains(dir.getAbsolutePath(), filePath);
+    }
+
     private static ApkChecksum extractHashFromFS(String split, String filePath) {
         // verity first
-        {
+        // Skip /product folder.
+        // TODO(b/231354111): remove this hack once we are allowed to change SELinux rules.
+        if (!containsFile(Environment.getProductDirectory(), filePath)) {
             byte[] hash = VerityUtils.getFsverityRootHash(filePath);
             if (hash != null) {
                 return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, hash);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 320b06f..32f0f10 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -119,7 +119,7 @@
             IInstalld.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES;
 
     private final boolean mIsolated;
-
+    private volatile boolean mDeferSetFirstBoot;
     private volatile IInstalld mInstalld;
     private volatile Object mWarnIfHeld;
 
@@ -171,6 +171,7 @@
             mInstalld = IInstalld.Stub.asInterface(binder);
             try {
                 invalidateMounts();
+                executeDeferredActions();
             } catch (InstallerException ignored) {
             }
         } else {
@@ -180,6 +181,15 @@
     }
 
     /**
+     * Perform any deferred actions on mInstalld while the connection could not be made.
+     */
+    private void executeDeferredActions() throws InstallerException {
+        if (mDeferSetFirstBoot) {
+            setFirstBoot();
+        }
+    }
+
+    /**
      * Do several pre-flight checks before making a remote call.
      *
      * @return if the remote call should continue.
@@ -291,8 +301,15 @@
             return;
         }
         try {
-            mInstalld.setFirstBoot();
-        } catch (RemoteException e) {
+            // mInstalld might be null if the connection could not be established.
+            if (mInstalld != null) {
+                mInstalld.setFirstBoot();
+            } else {
+                // if it is null while trying to set the first boot, set a flag to try and set the
+                // first boot when the connection is eventually established
+                mDeferSetFirstBoot = true;
+            }
+        } catch (Exception e) {
             throw InstallerException.from(e);
         }
     }
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 4df54b7..fa0c6c3 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -166,18 +166,19 @@
      * An in-memory copy of shortcuts for this package that was loaded from xml, keyed on IDs.
      */
     @GuardedBy("mLock")
-    final ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
+    private final ArrayMap<String, ShortcutInfo> mShortcuts = new ArrayMap<>();
 
     /**
      * A temporary copy of shortcuts that are to be cleared once persisted into AppSearch, keyed on
      * IDs.
      */
     @GuardedBy("mLock")
-    private ArrayMap<String, ShortcutInfo> mTransientShortcuts = new ArrayMap<>(0);
+    private final ArrayMap<String, ShortcutInfo> mTransientShortcuts = new ArrayMap<>(0);
 
     /**
      * All the share targets from the package
      */
+    @GuardedBy("mLock")
     private final ArrayList<ShareTargetInfo> mShareTargets = new ArrayList<>(0);
 
     /**
@@ -231,7 +232,9 @@
     }
 
     public int getShortcutCount() {
-        return mShortcuts.size();
+        synchronized (mLock) {
+            return mShortcuts.size();
+        }
     }
 
     @Override
@@ -272,7 +275,9 @@
     @Nullable
     public ShortcutInfo findShortcutById(@Nullable final String id) {
         if (id == null) return null;
-        return mShortcuts.get(id);
+        synchronized (mLock) {
+            return mShortcuts.get(id);
+        }
     }
 
     public boolean isShortcutExistsAndInvisibleToPublisher(String id) {
@@ -347,11 +352,14 @@
      * Delete a shortcut by ID. This will *always* remove it even if it's immutable or invisible.
      */
     private ShortcutInfo forceDeleteShortcutInner(@NonNull String id) {
-        final ShortcutInfo shortcut = mShortcuts.remove(id);
-        if (shortcut != null) {
-            removeIcon(shortcut);
-            shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED
-                    | ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CACHED_ALL);
+        final ShortcutInfo shortcut;
+        synchronized (mLock) {
+            shortcut = mShortcuts.remove(id);
+            if (shortcut != null) {
+                removeIcon(shortcut);
+                shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED
+                        | ShortcutInfo.FLAG_MANIFEST | ShortcutInfo.FLAG_CACHED_ALL);
+            }
         }
         return shortcut;
     }
@@ -524,14 +532,16 @@
     public List<ShortcutInfo> deleteAllDynamicShortcuts() {
         final long now = mShortcutUser.mService.injectCurrentTimeMillis();
         boolean changed = false;
-        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
-            ShortcutInfo si = mShortcuts.valueAt(i);
-            if (si.isDynamic() && si.isVisibleToPublisher()) {
-                changed = true;
+        synchronized (mLock) {
+            for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+                ShortcutInfo si = mShortcuts.valueAt(i);
+                if (si.isDynamic() && si.isVisibleToPublisher()) {
+                    changed = true;
 
-                si.setTimestamp(now);
-                si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
-                si.setRank(0); // It may still be pinned, so clear the rank.
+                    si.setTimestamp(now);
+                    si.clearFlags(ShortcutInfo.FLAG_DYNAMIC);
+                    si.setRank(0); // It may still be pinned, so clear the rank.
+                }
             }
         }
         removeAllShortcutsAsync();
@@ -874,59 +884,63 @@
      */
     public List<ShortcutManager.ShareShortcutInfo> getMatchingShareTargets(
             @NonNull IntentFilter filter) {
-        final List<ShareTargetInfo> matchedTargets = new ArrayList<>();
-        for (int i = 0; i < mShareTargets.size(); i++) {
-            final ShareTargetInfo target = mShareTargets.get(i);
-            for (ShareTargetInfo.TargetData data : target.mTargetData) {
-                if (filter.hasDataType(data.mMimeType)) {
-                    // Matched at least with one data type
-                    matchedTargets.add(target);
-                    break;
-                }
-            }
-        }
-
-        if (matchedTargets.isEmpty()) {
-            return new ArrayList<>();
-        }
-
-        // Get the list of all dynamic shortcuts in this package.
-        final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
-        // Pass callingLauncher to ensure pinned flag marked by system ui, e.g. ShareSheet, are
-        // included in the result
-        findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
-                ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION,
-                mShortcutUser.mService.mContext.getPackageName(),
-                0, /*getPinnedByAnyLauncher=*/ false);
-
-        final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
-        for (int i = 0; i < shortcuts.size(); i++) {
-            final Set<String> categories = shortcuts.get(i).getCategories();
-            if (categories == null || categories.isEmpty()) {
-                continue;
-            }
-            for (int j = 0; j < matchedTargets.size(); j++) {
-                // Shortcut must have all of share target categories
-                boolean hasAllCategories = true;
-                final ShareTargetInfo target = matchedTargets.get(j);
-                for (int q = 0; q < target.mCategories.length; q++) {
-                    if (!categories.contains(target.mCategories[q])) {
-                        hasAllCategories = false;
+        synchronized (mLock) {
+            final List<ShareTargetInfo> matchedTargets = new ArrayList<>();
+            for (int i = 0; i < mShareTargets.size(); i++) {
+                final ShareTargetInfo target = mShareTargets.get(i);
+                for (ShareTargetInfo.TargetData data : target.mTargetData) {
+                    if (filter.hasDataType(data.mMimeType)) {
+                        // Matched at least with one data type
+                        matchedTargets.add(target);
                         break;
                     }
                 }
-                if (hasAllCategories) {
-                    result.add(new ShortcutManager.ShareShortcutInfo(shortcuts.get(i),
-                            new ComponentName(getPackageName(), target.mTargetClass)));
-                    break;
+            }
+
+            if (matchedTargets.isEmpty()) {
+                return new ArrayList<>();
+            }
+
+            // Get the list of all dynamic shortcuts in this package.
+            final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+            // Pass callingLauncher to ensure pinned flag marked by system ui, e.g. ShareSheet, are
+            // included in the result
+            findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
+                    ShortcutInfo.CLONE_REMOVE_FOR_APP_PREDICTION,
+                    mShortcutUser.mService.mContext.getPackageName(),
+                    0, /*getPinnedByAnyLauncher=*/ false);
+
+            final List<ShortcutManager.ShareShortcutInfo> result = new ArrayList<>();
+            for (int i = 0; i < shortcuts.size(); i++) {
+                final Set<String> categories = shortcuts.get(i).getCategories();
+                if (categories == null || categories.isEmpty()) {
+                    continue;
+                }
+                for (int j = 0; j < matchedTargets.size(); j++) {
+                    // Shortcut must have all of share target categories
+                    boolean hasAllCategories = true;
+                    final ShareTargetInfo target = matchedTargets.get(j);
+                    for (int q = 0; q < target.mCategories.length; q++) {
+                        if (!categories.contains(target.mCategories[q])) {
+                            hasAllCategories = false;
+                            break;
+                        }
+                    }
+                    if (hasAllCategories) {
+                        result.add(new ShortcutManager.ShareShortcutInfo(shortcuts.get(i),
+                                new ComponentName(getPackageName(), target.mTargetClass)));
+                        break;
+                    }
                 }
             }
+            return result;
         }
-        return result;
     }
 
     public boolean hasShareTargets() {
-        return !mShareTargets.isEmpty();
+        synchronized (mLock) {
+            return !mShareTargets.isEmpty();
+        }
     }
 
     /**
@@ -935,38 +949,40 @@
      * the app's Xml resource.
      */
     int getSharingShortcutCount() {
-        if (mShareTargets.isEmpty()) {
-            return 0;
-        }
-
-        // Get the list of all dynamic shortcuts in this package
-        final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
-        findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
-                ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
-
-        int sharingShortcutCount = 0;
-        for (int i = 0; i < shortcuts.size(); i++) {
-            final Set<String> categories = shortcuts.get(i).getCategories();
-            if (categories == null || categories.isEmpty()) {
-                continue;
+        synchronized (mLock) {
+            if (mShareTargets.isEmpty()) {
+                return 0;
             }
-            for (int j = 0; j < mShareTargets.size(); j++) {
-                // A SharingShortcut must have all of share target categories
-                boolean hasAllCategories = true;
-                final ShareTargetInfo target = mShareTargets.get(j);
-                for (int q = 0; q < target.mCategories.length; q++) {
-                    if (!categories.contains(target.mCategories[q])) {
-                        hasAllCategories = false;
+
+            // Get the list of all dynamic shortcuts in this package
+            final ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+            findAll(shortcuts, ShortcutInfo::isNonManifestVisible,
+                    ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+
+            int sharingShortcutCount = 0;
+            for (int i = 0; i < shortcuts.size(); i++) {
+                final Set<String> categories = shortcuts.get(i).getCategories();
+                if (categories == null || categories.isEmpty()) {
+                    continue;
+                }
+                for (int j = 0; j < mShareTargets.size(); j++) {
+                    // A SharingShortcut must have all of share target categories
+                    boolean hasAllCategories = true;
+                    final ShareTargetInfo target = mShareTargets.get(j);
+                    for (int q = 0; q < target.mCategories.length; q++) {
+                        if (!categories.contains(target.mCategories[q])) {
+                            hasAllCategories = false;
+                            break;
+                        }
+                    }
+                    if (hasAllCategories) {
+                        sharingShortcutCount++;
                         break;
                     }
                 }
-                if (hasAllCategories) {
-                    sharingShortcutCount++;
-                    break;
-                }
             }
+            return sharingShortcutCount;
         }
-        return sharingShortcutCount;
     }
 
     /**
@@ -1090,19 +1106,25 @@
 
         // Now prepare to publish manifest shortcuts.
         List<ShortcutInfo> newManifestShortcutList = null;
-        try {
-            newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService,
-                    getPackageName(), getPackageUserId(), mShareTargets);
-        } catch (IOException|XmlPullParserException e) {
-            Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e);
+        final int shareTargetSize;
+        synchronized (mLock) {
+            try {
+                shareTargetSize = mShareTargets.size();
+                newManifestShortcutList = ShortcutParser.parseShortcuts(mShortcutUser.mService,
+                        getPackageName(), getPackageUserId(), mShareTargets);
+            } catch (IOException | XmlPullParserException e) {
+                Slog.e(TAG, "Failed to load shortcuts from AndroidManifest.xml.", e);
+            }
         }
         final int manifestShortcutSize = newManifestShortcutList == null ? 0
                 : newManifestShortcutList.size();
         if (ShortcutService.DEBUG || ShortcutService.DEBUG_REBOOT) {
             Slog.d(TAG,
-                    String.format("Package %s has %d manifest shortcut(s), and %d share target(s)",
-                            getPackageName(), manifestShortcutSize, mShareTargets.size()));
+                    String.format(
+                            "Package %s has %d manifest shortcut(s), and %d share target(s)",
+                            getPackageName(), manifestShortcutSize, shareTargetSize));
         }
+
         if (isNewApp && (manifestShortcutSize == 0)) {
             // If it's a new app, and it doesn't have manifest shortcuts, then nothing to do.
 
@@ -1701,37 +1723,38 @@
     @Override
     public void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup)
             throws IOException, XmlPullParserException {
-        final int size = mShortcuts.size();
-        final int shareTargetSize = mShareTargets.size();
+        synchronized (mLock) {
+            final int size = mShortcuts.size();
+            final int shareTargetSize = mShareTargets.size();
 
-        if (hasNoShortcut() && shareTargetSize == 0 && mApiCallCount == 0) {
-            return; // nothing to write.
-        }
+            if (hasNoShortcut() && shareTargetSize == 0 && mApiCallCount == 0) {
+                return; // nothing to write.
+            }
 
-        out.startTag(null, TAG_ROOT);
+            out.startTag(null, TAG_ROOT);
 
-        ShortcutService.writeAttr(out, ATTR_NAME, getPackageName());
-        ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
-        ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
-        if (!forBackup) {
-            synchronized (mLock) {
-                ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, (mIsAppSearchSchemaUpToDate)
+            ShortcutService.writeAttr(out, ATTR_NAME, getPackageName());
+            ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
+            ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
+            if (!forBackup) {
+                ShortcutService.writeAttr(out, ATTR_SCHEMA_VERSON, mIsAppSearchSchemaUpToDate
                         ? AppSearchShortcutInfo.SCHEMA_VERSION : 0);
             }
-        }
-        getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup);
+            getPackageInfo().saveToXml(mShortcutUser.mService, out, forBackup);
 
-        for (int j = 0; j < size; j++) {
-            saveShortcut(out, mShortcuts.valueAt(j), forBackup, getPackageInfo().isBackupAllowed());
-        }
-
-        if (!forBackup) {
-            for (int j = 0; j < shareTargetSize; j++) {
-                mShareTargets.get(j).saveToXml(out);
+            for (int j = 0; j < size; j++) {
+                saveShortcut(
+                        out, mShortcuts.valueAt(j), forBackup, getPackageInfo().isBackupAllowed());
             }
-        }
 
-        out.endTag(null, TAG_ROOT);
+            if (!forBackup) {
+                for (int j = 0; j < shareTargetSize; j++) {
+                    mShareTargets.get(j).saveToXml(out);
+                }
+            }
+
+            out.endTag(null, TAG_ROOT);
+        }
     }
 
     private void saveShortcut(TypedXmlSerializer out, ShortcutInfo si, boolean forBackup,
@@ -1917,38 +1940,38 @@
         synchronized (ret.mLock) {
             ret.mIsAppSearchSchemaUpToDate = ShortcutService.parseIntAttribute(
                     parser, ATTR_SCHEMA_VERSON, 0) == AppSearchShortcutInfo.SCHEMA_VERSION;
-        }
-        ret.mApiCallCount = ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
-        ret.mLastResetTime = ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
 
+            ret.mApiCallCount = ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
+            ret.mLastResetTime = ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
 
-        final int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-            final int depth = parser.getDepth();
-            final String tag = parser.getName();
-            if (depth == outerDepth + 1) {
-                switch (tag) {
-                    case ShortcutPackageInfo.TAG_ROOT:
-                        ret.getPackageInfo().loadFromXml(parser, fromBackup);
-
-                        continue;
-                    case TAG_SHORTCUT:
-                        final ShortcutInfo si = parseShortcut(parser, packageName,
-                                shortcutUser.getUserId(), fromBackup);
-                        // Don't use addShortcut(), we don't need to save the icon.
-                        ret.mShortcuts.put(si.getId(), si);
-                        continue;
-                    case TAG_SHARE_TARGET:
-                        ret.mShareTargets.add(ShareTargetInfo.loadFromXml(parser));
-                        continue;
+            final int outerDepth = parser.getDepth();
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+                if (type != XmlPullParser.START_TAG) {
+                    continue;
                 }
+                final int depth = parser.getDepth();
+                final String tag = parser.getName();
+                if (depth == outerDepth + 1) {
+                    switch (tag) {
+                        case ShortcutPackageInfo.TAG_ROOT:
+                            ret.getPackageInfo().loadFromXml(parser, fromBackup);
+
+                            continue;
+                        case TAG_SHORTCUT:
+                            final ShortcutInfo si = parseShortcut(parser, packageName,
+                                    shortcutUser.getUserId(), fromBackup);
+                            // Don't use addShortcut(), we don't need to save the icon.
+                            ret.mShortcuts.put(si.getId(), si);
+                            continue;
+                        case TAG_SHARE_TARGET:
+                            ret.mShareTargets.add(ShareTargetInfo.loadFromXml(parser));
+                            continue;
+                    }
+                }
+                ShortcutService.warnForInvalidTag(depth, tag);
             }
-            ShortcutService.warnForInvalidTag(depth, tag);
         }
         return ret;
     }
@@ -2152,7 +2175,9 @@
 
     @VisibleForTesting
     List<ShareTargetInfo> getAllShareTargetsForTest() {
-        return new ArrayList<>(mShareTargets);
+        synchronized (mLock) {
+            return new ArrayList<>(mShareTargets);
+        }
     }
 
     @Override
@@ -2291,15 +2316,19 @@
 
     private void saveShortcut(@NonNull final Collection<ShortcutInfo> shortcuts) {
         Objects.requireNonNull(shortcuts);
-        for (ShortcutInfo si : shortcuts) {
-            mShortcuts.put(si.getId(), si);
+        synchronized (mLock) {
+            for (ShortcutInfo si : shortcuts) {
+                mShortcuts.put(si.getId(), si);
+            }
         }
     }
 
     @Nullable
     List<ShortcutInfo> findAll(@NonNull final Collection<String> ids) {
-        return ids.stream().map(mShortcuts::get)
-                .filter(Objects::nonNull).collect(Collectors.toList());
+        synchronized (mLock) {
+            return ids.stream().map(mShortcuts::get)
+                    .filter(Objects::nonNull).collect(Collectors.toList());
+        }
     }
 
     private void forEachShortcut(@NonNull final Consumer<ShortcutInfo> cb) {
@@ -2318,10 +2347,12 @@
 
     private void forEachShortcutStopWhen(
             @NonNull final Function<ShortcutInfo, Boolean> cb) {
-        for (int i = mShortcuts.size() - 1; i >= 0; i--) {
-            final ShortcutInfo si = mShortcuts.valueAt(i);
-            if (cb.apply(si)) {
-                return;
+        synchronized (mLock) {
+            for (int i = mShortcuts.size() - 1; i >= 0; i--) {
+                final ShortcutInfo si = mShortcuts.valueAt(i);
+                if (cb.apply(si)) {
+                    return;
+                }
             }
         }
     }
@@ -2461,6 +2492,7 @@
                         })));
     }
 
+    @GuardedBy("mLock")
     @Override
     void scheduleSaveToAppSearchLocked() {
         final Map<String, ShortcutInfo> copy = new ArrayMap<>(mShortcuts);
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index e1ff9ea..6ee9c66 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -392,6 +392,9 @@
         if ((flags & PARSE_FRAMEWORK_RES_SPLITS) != 0) {
             liteParseFlags = flags;
         }
+        if ((flags & PARSE_APK_IN_APEX) != 0) {
+            liteParseFlags |= PARSE_APK_IN_APEX;
+        }
         final ParseResult<PackageLite> liteResult =
                 ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, frameworkSplits,
                         liteParseFlags);
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 7ba1cad..977f79f 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -1453,16 +1453,6 @@
                 }
             }
 
-            try {
-                if (Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                        Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, UserHandle.USER_SYSTEM)
-                        == 0) {
-                    return false;
-                }
-            } catch (Settings.SettingNotFoundException e) {
-                return false;
-            }
-
             if (!pkg.getRequestedPermissions().contains(POST_NOTIFICATIONS)
                     || CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, pkgName, user)
                     || mKeyguardManager.isKeyguardLocked()) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b142141..d8e7fbe 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -27,7 +27,6 @@
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS;
 import static android.os.Build.VERSION_CODES.M;
 import static android.os.Build.VERSION_CODES.O;
 import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
@@ -130,7 +129,6 @@
 import android.media.AudioSystem;
 import android.media.IAudioService;
 import android.media.session.MediaSessionLegacyHelper;
-import android.os.BatteryManagerInternal;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.DeviceIdleManager;
@@ -396,7 +394,6 @@
     PowerManagerInternal mPowerManagerInternal;
     IStatusBarService mStatusBarService;
     StatusBarManagerInternal mStatusBarManagerInternal;
-    BatteryManagerInternal mBatteryManagerInternal;
     AudioManagerInternal mAudioManagerInternal;
     DisplayManager mDisplayManager;
     DisplayManagerInternal mDisplayManagerInternal;
@@ -791,8 +788,7 @@
         @Override
         public void onWakeUp() {
             synchronized (mLock) {
-                if (shouldEnableWakeGestureLp()
-                        && getBatteryManagerInternal().getPlugType() != BATTERY_PLUGGED_WIRELESS) {
+                if (shouldEnableWakeGestureLp()) {
                     performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
                             "Wake Up");
                     wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture,
@@ -849,15 +845,6 @@
         }
     }
 
-    BatteryManagerInternal getBatteryManagerInternal() {
-        synchronized (mServiceAcquireLock) {
-            if (mBatteryManagerInternal == null) {
-                mBatteryManagerInternal =
-                        LocalServices.getService(BatteryManagerInternal.class);
-            }
-            return mBatteryManagerInternal;
-        }
-    }
 
     // returns true if the key was handled and should not be passed to the user
     private boolean backKeyPress() {
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index ee30fa2..fb9a4d4 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -15,6 +15,8 @@
  */
 package com.android.server.tv.tunerresourcemanager;
 
+import android.media.tv.tunerresourcemanager.TunerResourceManager;
+
 import java.util.HashSet;
 import java.util.Set;
 
@@ -63,6 +65,11 @@
     private int mNiceValue;
 
     /**
+     * The handle of the primary frontend resource
+     */
+    private int mPrimaryUsingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+
+    /**
      * List of the frontend handles that are used by the current client.
      */
     private Set<Integer> mUsingFrontendHandles = new HashSet<>();
@@ -175,6 +182,22 @@
     }
 
     /**
+     * Set the primary frontend used by the client
+     *
+     * @param frontendHandle being used.
+     */
+    public void setPrimaryFrontend(int frontendHandle) {
+        mPrimaryUsingFrontendHandle = frontendHandle;
+    }
+
+    /**
+     * Get the primary frontend used by the client
+     */
+    public int getPrimaryFrontend() {
+        return mPrimaryUsingFrontendHandle;
+    }
+
+    /**
      * Update the set of client that share frontend with the current client.
      *
      * @param clientId the client to share the fe with the current client.
@@ -206,6 +229,7 @@
     public void releaseFrontend() {
         mUsingFrontendHandles.clear();
         mShareFeClientIds.clear();
+        mPrimaryUsingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
     }
 
     /**
@@ -276,6 +300,7 @@
     public void reclaimAllResources() {
         mUsingFrontendHandles.clear();
         mShareFeClientIds.clear();
+        mPrimaryUsingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
         mUsingLnbHandles.clear();
         mUsingCasSystemId = INVALID_RESOURCE_ID;
         mUsingCiCamId = INVALID_RESOURCE_ID;
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index af705d5..6162d716 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -42,6 +42,7 @@
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseIntArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -72,14 +73,28 @@
     private static final long INVALID_THREAD_ID = -1;
     private static final long TRMS_LOCK_TIMEOUT = 500;
 
+    private static final int INVALID_FE_COUNT = -1;
+
     // Map of the registered client profiles
     private Map<Integer, ClientProfile> mClientProfiles = new HashMap<>();
     private int mNextUnusedClientId = 0;
 
     // Map of the current available frontend resources
     private Map<Integer, FrontendResource> mFrontendResources = new HashMap<>();
-    // Backup Map of the current available frontend resources
+    // SparseIntArray of the max usable number for each frontend resource type
+    private SparseIntArray mFrontendMaxUsableNums = new SparseIntArray();
+    // SparseIntArray of the currently used number for each frontend resource type
+    private SparseIntArray mFrontendUsedNums = new SparseIntArray();
+    // SparseIntArray of the existing number for each frontend resource type
+    private SparseIntArray mFrontendExistingNums = new SparseIntArray();
+
+    // Backups for the frontend resource maps for enabling testing with custom resource maps
+    // such as TunerTest.testHasUnusedFrontend1()
     private Map<Integer, FrontendResource> mFrontendResourcesBackup = new HashMap<>();
+    private SparseIntArray mFrontendMaxUsableNumsBackup = new SparseIntArray();
+    private SparseIntArray mFrontendUsedNumsBackup = new SparseIntArray();
+    private SparseIntArray mFrontendExistingNumsBackup = new SparseIntArray();
+
     // Map of the current available lnb resources
     private Map<Integer, LnbResource> mLnbResources = new HashMap<>();
     // Map of the current available Cas resources
@@ -268,6 +283,29 @@
         }
 
         @Override
+        public boolean setMaxNumberOfFrontends(int frontendType, int maxUsableNum) {
+            enforceTunerAccessPermission("requestFrontend");
+            enforceTrmAccessPermission("requestFrontend");
+            if (maxUsableNum < 0) {
+                Slog.w(TAG, "setMaxNumberOfFrontends failed with maxUsableNum:" + maxUsableNum
+                        + " frontendType:" + frontendType);
+                return false;
+            }
+            synchronized (mLock) {
+                return setMaxNumberOfFrontendsInternal(frontendType, maxUsableNum);
+            }
+        }
+
+        @Override
+        public int getMaxNumberOfFrontends(int frontendType) {
+            enforceTunerAccessPermission("requestFrontend");
+            enforceTrmAccessPermission("requestFrontend");
+            synchronized (mLock) {
+                return getMaxNumberOfFrontendsInternal(frontendType);
+            }
+        }
+
+        @Override
         public void shareFrontend(int selfClientId, int targetClientId) throws RemoteException {
             enforceTunerAccessPermission("shareFrontend");
             enforceTrmAccessPermission("shareFrontend");
@@ -572,71 +610,19 @@
             }
 
             synchronized (mLock) {
-                if (mClientProfiles != null) {
-                    pw.println("ClientProfiles:");
-                    pw.increaseIndent();
-                    for (Map.Entry<Integer, ClientProfile> entry : mClientProfiles.entrySet()) {
-                        pw.println(entry.getKey() + " : " + entry.getValue());
-                    }
-                    pw.decreaseIndent();
-                }
-
-                if (mFrontendResources != null) {
-                    pw.println("FrontendResources:");
-                    pw.increaseIndent();
-                    for (Map.Entry<Integer, FrontendResource> entry
-                            : mFrontendResources.entrySet()) {
-                        pw.println(entry.getKey() + " : " + entry.getValue());
-                    }
-                    pw.decreaseIndent();
-                }
-
-                if (mFrontendResourcesBackup != null) {
-                    pw.println("FrontendResourcesBackUp:");
-                    pw.increaseIndent();
-                    for (Map.Entry<Integer, FrontendResource> entry
-                            : mFrontendResourcesBackup.entrySet()) {
-                        pw.println(entry.getKey() + " : " + entry.getValue());
-                    }
-                    pw.decreaseIndent();
-                }
-
-                if (mLnbResources != null) {
-                    pw.println("LnbResources:");
-                    pw.increaseIndent();
-                    for (Map.Entry<Integer, LnbResource> entry : mLnbResources.entrySet()) {
-                        pw.println(entry.getKey() + " : " + entry.getValue());
-                    }
-                    pw.decreaseIndent();
-                }
-
-                if (mCasResources != null) {
-                    pw.println("CasResources:");
-                    pw.increaseIndent();
-                    for (Map.Entry<Integer, CasResource> entry : mCasResources.entrySet()) {
-                        pw.println(entry.getKey() + " : " + entry.getValue());
-                    }
-                    pw.decreaseIndent();
-                }
-
-                if (mCiCamResources != null) {
-                    pw.println("CiCamResources:");
-                    pw.increaseIndent();
-                    for (Map.Entry<Integer, CiCamResource> entry : mCiCamResources.entrySet()) {
-                        pw.println(entry.getKey() + " : " + entry.getValue());
-                    }
-                    pw.decreaseIndent();
-                }
-
-                if (mListeners != null) {
-                    pw.println("Listners:");
-                    pw.increaseIndent();
-                    for (Map.Entry<Integer, ResourcesReclaimListenerRecord> entry
-                            : mListeners.entrySet()) {
-                        pw.println(entry.getKey() + " : " + entry.getValue());
-                    }
-                    pw.decreaseIndent();
-                }
+                dumpMap(mClientProfiles, "ClientProfiles:", "\n", pw);
+                dumpMap(mFrontendResources, "FrontendResources:", "\n", pw);
+                dumpSIA(mFrontendExistingNums, "FrontendExistingNums:", ", ", pw);
+                dumpSIA(mFrontendUsedNums, "FrontendUsedNums:", ", ", pw);
+                dumpSIA(mFrontendMaxUsableNums, "FrontendMaxUsableNums:", ", ", pw);
+                dumpMap(mFrontendResourcesBackup, "FrontendResourcesBackUp:", "\n", pw);
+                dumpSIA(mFrontendExistingNumsBackup, "FrontendExistingNumsBackup:", ", ", pw);
+                dumpSIA(mFrontendUsedNumsBackup, "FrontendUsedNumsBackup:", ", ", pw);
+                dumpSIA(mFrontendMaxUsableNumsBackup, "FrontendUsedNumsBackup:", ", ", pw);
+                dumpMap(mLnbResources, "LnbResource:", "\n", pw);
+                dumpMap(mCasResources, "CasResource:", "\n", pw);
+                dumpMap(mCiCamResources, "CiCamResource:", "\n", pw);
+                dumpMap(mListeners, "Listners:", "\n", pw);
             }
         }
 
@@ -786,10 +772,10 @@
     protected void storeResourceMapInternal(int resourceType) {
         switch (resourceType) {
             case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND:
-                if (mFrontendResources != null && mFrontendResources.size() > 0) {
-                    mFrontendResourcesBackup.putAll(mFrontendResources);
-                    mFrontendResources.clear();
-                }
+                replaceFeResourceMap(mFrontendResources, mFrontendResourcesBackup);
+                replaceFeCounts(mFrontendExistingNums, mFrontendExistingNumsBackup);
+                replaceFeCounts(mFrontendUsedNums, mFrontendUsedNumsBackup);
+                replaceFeCounts(mFrontendMaxUsableNums, mFrontendMaxUsableNumsBackup);
                 break;
                 // TODO: implement for other resource type when needed
             default:
@@ -800,9 +786,10 @@
     protected void clearResourceMapInternal(int resourceType) {
         switch (resourceType) {
             case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND:
-                if (mFrontendResources != null) {
-                    mFrontendResources.clear();
-                }
+                replaceFeResourceMap(null, mFrontendResources);
+                replaceFeCounts(null, mFrontendExistingNums);
+                replaceFeCounts(null, mFrontendUsedNums);
+                replaceFeCounts(null, mFrontendMaxUsableNums);
                 break;
                 // TODO: implement for other resource type when needed
             default:
@@ -813,12 +800,10 @@
     protected void restoreResourceMapInternal(int resourceType) {
         switch (resourceType) {
             case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND:
-                if (mFrontendResourcesBackup != null
-                        && mFrontendResourcesBackup.size() > 0) {
-                    mFrontendResources.clear();
-                    mFrontendResources.putAll(mFrontendResourcesBackup);
-                    mFrontendResourcesBackup.clear();
-                }
+                replaceFeResourceMap(mFrontendResourcesBackup, mFrontendResources);
+                replaceFeCounts(mFrontendExistingNumsBackup, mFrontendExistingNums);
+                replaceFeCounts(mFrontendUsedNumsBackup, mFrontendUsedNums);
+                replaceFeCounts(mFrontendMaxUsableNumsBackup, mFrontendMaxUsableNums);
                 break;
                 // TODO: implement for other resource type when needed
             default:
@@ -954,6 +939,11 @@
         for (FrontendResource fr : getFrontendResources().values()) {
             if (fr.getType() == request.frontendType) {
                 if (!fr.isInUse()) {
+                    // Unused resource cannot be acquired if the max is already reached, but
+                    // TRM still has to look for the reclaim candidate
+                    if (isFrontendMaxNumUseReached(request.frontendType)) {
+                        continue;
+                    }
                     // Grant unused frontend with no exclusive group members first.
                     if (fr.getExclusiveGroupMemberFeHandles().isEmpty()) {
                         grantingFrontendHandle = fr.getHandle();
@@ -1021,6 +1011,9 @@
         for (int inUseHandle : newOwnerProfile.getInUseFrontendHandles()) {
             getFrontendResource(inUseHandle).setOwner(newOwnerId);
         }
+        // change the primary frontend
+        newOwnerProfile.setPrimaryFrontend(currentOwnerProfile.getPrimaryFrontend());
+        currentOwnerProfile.setPrimaryFrontend(TunerResourceManager.INVALID_RESOURCE_HANDLE);
         // double check there is no other resources tied to the previous owner
         for (int inUseHandle : currentOwnerProfile.getInUseFrontendHandles()) {
             int ownerId = getFrontendResource(inUseHandle).getOwnerClientId();
@@ -1657,11 +1650,13 @@
         FrontendResource grantingFrontend = getFrontendResource(grantingHandle);
         ClientProfile ownerProfile = getClientProfile(ownerClientId);
         grantingFrontend.setOwner(ownerClientId);
+        increFrontendNum(mFrontendUsedNums, grantingFrontend.getType());
         ownerProfile.useFrontend(grantingHandle);
         for (int exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeHandles()) {
             getFrontendResource(exclusiveGroupMember).setOwner(ownerClientId);
             ownerProfile.useFrontend(exclusiveGroupMember);
         }
+        ownerProfile.setPrimaryFrontend(grantingHandle);
     }
 
     private void updateLnbClientMappingOnNewGrant(int grantingHandle, int ownerClientId) {
@@ -1755,6 +1750,109 @@
         return mFrontendResources;
     }
 
+    private boolean setMaxNumberOfFrontendsInternal(int frontendType, int maxUsableNum) {
+        int usedNum = mFrontendUsedNums.get(frontendType, INVALID_FE_COUNT);
+        if (usedNum == INVALID_FE_COUNT || usedNum <= maxUsableNum) {
+            mFrontendMaxUsableNums.put(frontendType, maxUsableNum);
+            return true;
+        } else {
+            Slog.e(TAG, "max number of frontend for frontendType: " + frontendType
+                    + " cannot be set to a value lower than the current usage count."
+                    + " (requested max num = " + maxUsableNum + ", current usage = " + usedNum);
+            return false;
+        }
+    }
+
+    private int getMaxNumberOfFrontendsInternal(int frontendType) {
+        int existingNum = mFrontendExistingNums.get(frontendType, INVALID_FE_COUNT);
+        if (existingNum == INVALID_FE_COUNT) {
+            Log.e(TAG, "existingNum is -1 for " + frontendType);
+            return -1;
+        }
+        int maxUsableNum = mFrontendMaxUsableNums.get(frontendType, INVALID_FE_COUNT);
+        if (maxUsableNum == INVALID_FE_COUNT) {
+            return existingNum;
+        } else {
+            return maxUsableNum;
+        }
+    }
+
+    private boolean isFrontendMaxNumUseReached(int frontendType) {
+        int maxUsableNum = mFrontendMaxUsableNums.get(frontendType, INVALID_FE_COUNT);
+        if (maxUsableNum == INVALID_FE_COUNT) {
+            return false;
+        }
+        int useNum = mFrontendUsedNums.get(frontendType, INVALID_FE_COUNT);
+        if (useNum == INVALID_FE_COUNT) {
+            useNum = 0;
+        }
+        return useNum >= maxUsableNum;
+    }
+
+    private void increFrontendNum(SparseIntArray targetNums, int frontendType) {
+        int num = targetNums.get(frontendType, INVALID_FE_COUNT);
+        if (num == INVALID_FE_COUNT) {
+            targetNums.put(frontendType, 1);
+        } else {
+            targetNums.put(frontendType, num + 1);
+        }
+    }
+
+    private void decreFrontendNum(SparseIntArray targetNums, int frontendType) {
+        int num = targetNums.get(frontendType, INVALID_FE_COUNT);
+        if (num != INVALID_FE_COUNT) {
+            targetNums.put(frontendType, num - 1);
+        }
+    }
+
+    private void replaceFeResourceMap(Map<Integer, FrontendResource> srcMap, Map<Integer,
+            FrontendResource> dstMap) {
+        if (dstMap != null) {
+            dstMap.clear();
+            if (srcMap != null && srcMap.size() > 0) {
+                dstMap.putAll(srcMap);
+            }
+        }
+    }
+
+    private void replaceFeCounts(SparseIntArray srcCounts, SparseIntArray dstCounts) {
+        if (dstCounts != null) {
+            dstCounts.clear();
+            if (srcCounts != null) {
+                for (int i = 0; i < srcCounts.size(); i++) {
+                    dstCounts.put(srcCounts.keyAt(i), srcCounts.valueAt(i));
+                }
+            }
+        }
+    }
+    private void dumpMap(Map<?, ?> targetMap, String headline, String delimiter,
+            IndentingPrintWriter pw) {
+        if (targetMap != null) {
+            pw.println(headline);
+            pw.increaseIndent();
+            for (Map.Entry<?, ?> entry : targetMap.entrySet()) {
+                pw.print(entry.getKey() + " : " + entry.getValue());
+                pw.print(delimiter);
+            }
+            pw.println();
+            pw.decreaseIndent();
+        }
+    }
+
+    private void dumpSIA(SparseIntArray array, String headline, String delimiter,
+            IndentingPrintWriter pw) {
+        if (array != null) {
+            pw.println(headline);
+            pw.increaseIndent();
+            for (int i = 0; i < array.size(); i++) {
+                pw.print(array.keyAt(i) + " : " + array.valueAt(i));
+                pw.print(delimiter);
+            }
+            pw.println();
+            pw.decreaseIndent();
+        }
+    }
+
     private void addFrontendResource(FrontendResource newFe) {
         // Update the exclusive group member list in all the existing Frontend resource
         for (FrontendResource fe : getFrontendResources().values()) {
@@ -1771,6 +1869,8 @@
         }
         // Update resource list and available id list
         mFrontendResources.put(newFe.getHandle(), newFe);
+        increFrontendNum(mFrontendExistingNums, newFe.getType());
+
     }
 
     private void removeFrontendResource(int removingHandle) {
@@ -1789,6 +1889,7 @@
             getFrontendResource(excGroupmemberFeHandle)
                     .removeExclusiveGroupMemberFeId(fe.getHandle());
         }
+        decreFrontendNum(mFrontendExistingNums, fe.getType());
         mFrontendResources.remove(removingHandle);
     }
 
@@ -1918,6 +2019,15 @@
             }
 
         }
+
+        int primaryFeId = profile.getPrimaryFrontend();
+        if (primaryFeId != TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+            FrontendResource primaryFe = getFrontendResource(primaryFeId);
+            if (primaryFe != null) {
+                decreFrontendNum(mFrontendUsedNums, primaryFe.getType());
+            }
+        }
+
         profile.releaseFrontend();
     }
 
diff --git a/services/core/java/com/android/server/utils/WatchedArrayList.java b/services/core/java/com/android/server/utils/WatchedArrayList.java
index 6059f96..07474a63 100644
--- a/services/core/java/com/android/server/utils/WatchedArrayList.java
+++ b/services/core/java/com/android/server/utils/WatchedArrayList.java
@@ -416,7 +416,7 @@
         dst.mStorage.ensureCapacity(end);
         for (int i = 0; i < end; i++) {
             final E val = Snapshots.maybeSnapshot(src.get(i));
-            dst.add(i, val);
+            dst.mStorage.add(val);
         }
         dst.seal();
     }
diff --git a/services/core/java/com/android/server/utils/WatchedArrayMap.java b/services/core/java/com/android/server/utils/WatchedArrayMap.java
index 7c1cde8..63441aa 100644
--- a/services/core/java/com/android/server/utils/WatchedArrayMap.java
+++ b/services/core/java/com/android/server/utils/WatchedArrayMap.java
@@ -465,7 +465,7 @@
         for (int i = 0; i < end; i++) {
             final V val = Snapshots.maybeSnapshot(src.valueAt(i));
             final K key = src.keyAt(i);
-            dst.put(key, val);
+            dst.mStorage.put(key, val);
         }
         dst.seal();
     }
diff --git a/services/core/java/com/android/server/utils/WatchedArraySet.java b/services/core/java/com/android/server/utils/WatchedArraySet.java
index ec80261..a2eaed7 100644
--- a/services/core/java/com/android/server/utils/WatchedArraySet.java
+++ b/services/core/java/com/android/server/utils/WatchedArraySet.java
@@ -427,7 +427,7 @@
         dst.mStorage.ensureCapacity(end);
         for (int i = 0; i < end; i++) {
             final E val = Snapshots.maybeSnapshot(src.valueAt(i));
-            dst.append(val);
+            dst.mStorage.append(val);
         }
         dst.seal();
     }
diff --git a/services/core/java/com/android/server/utils/WatchedLongSparseArray.java b/services/core/java/com/android/server/utils/WatchedLongSparseArray.java
index bf23de1..9da9e75 100644
--- a/services/core/java/com/android/server/utils/WatchedLongSparseArray.java
+++ b/services/core/java/com/android/server/utils/WatchedLongSparseArray.java
@@ -410,7 +410,7 @@
         for (int i = 0; i < end; i++) {
             final E val = Snapshots.maybeSnapshot(src.valueAt(i));
             final long key = src.keyAt(i);
-            dst.put(key, val);
+            dst.mStorage.put(key, val);
         }
         dst.seal();
     }
diff --git a/services/core/java/com/android/server/utils/WatchedSparseArray.java b/services/core/java/com/android/server/utils/WatchedSparseArray.java
index 9b99b91..6ce38b7 100644
--- a/services/core/java/com/android/server/utils/WatchedSparseArray.java
+++ b/services/core/java/com/android/server/utils/WatchedSparseArray.java
@@ -490,7 +490,7 @@
         for (int i = 0; i < end; i++) {
             final E val = Snapshots.maybeSnapshot(src.valueAt(i));
             final int key = src.keyAt(i);
-            dst.put(key, val);
+            dst.mStorage.put(key, val);
         }
         dst.seal();
     }
diff --git a/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java b/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java
index 772a8d0..50e2272 100644
--- a/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java
+++ b/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java
@@ -310,7 +310,7 @@
         }
         final int end = src.size();
         for (int i = 0; i < end; i++) {
-            dst.put(src.keyAt(i), src.valueAt(i));
+            dst.mStorage.put(src.keyAt(i), src.valueAt(i));
         }
         dst.seal();
     }
diff --git a/services/core/java/com/android/server/utils/WatchedSparseIntArray.java b/services/core/java/com/android/server/utils/WatchedSparseIntArray.java
index 72705bf..53d1682 100644
--- a/services/core/java/com/android/server/utils/WatchedSparseIntArray.java
+++ b/services/core/java/com/android/server/utils/WatchedSparseIntArray.java
@@ -315,7 +315,7 @@
         }
         final int end = src.size();
         for (int i = 0; i < end; i++) {
-            dst.put(src.keyAt(i), src.valueAt(i));
+            dst.mStorage.put(src.keyAt(i), src.valueAt(i));
         }
         dst.seal();
     }
diff --git a/services/core/java/com/android/server/utils/WatchedSparseSetArray.java b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java
index 05db12e..77750ed 100644
--- a/services/core/java/com/android/server/utils/WatchedSparseSetArray.java
+++ b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java
@@ -169,7 +169,7 @@
             final ArraySet set = src.get(i);
             final int setSize = set.size();
             for (int j = 0; j < setSize; j++) {
-                dst.add(src.keyAt(i), set.valueAt(j));
+                dst.mStorage.add(src.keyAt(i), set.valueAt(j));
             }
         }
         dst.seal();
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 63619e5..f1dbad61 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -446,6 +446,43 @@
         // Not relevant for the window observer.
     }
 
+    public Pair<Matrix, MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
+            IBinder token) {
+        synchronized (mService.mGlobalLock) {
+            final Matrix transformationMatrix = new Matrix();
+            final MagnificationSpec magnificationSpec = new MagnificationSpec();
+
+            final WindowState windowState = mService.mWindowMap.get(token);
+            if (windowState != null) {
+                windowState.getTransformationMatrix(new float[9], transformationMatrix);
+
+                if (hasCallbacks()) {
+                    final MagnificationSpec otherMagnificationSpec =
+                            getMagnificationSpecForWindow(windowState);
+                    if (otherMagnificationSpec != null && !otherMagnificationSpec.isNop()) {
+                        magnificationSpec.setTo(otherMagnificationSpec);
+                    }
+                }
+            }
+
+            return new Pair<>(transformationMatrix, magnificationSpec);
+        }
+    }
+
+    MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) {
+        if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+            mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForWindow",
+                    FLAGS_MAGNIFICATION_CALLBACK,
+                    "windowState={" + windowState + "}");
+        }
+        final int displayId = windowState.getDisplayId();
+        final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+        if (displayMagnifier != null) {
+            return displayMagnifier.getMagnificationSpecForWindow(windowState);
+        }
+        return null;
+    }
+
     boolean hasCallbacks() {
         if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
                 | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index cb65597..701fc94 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -68,7 +68,6 @@
 import static com.android.server.wm.NonAppWindowAnimationAdapter.shouldAttachNavBarToApp;
 import static com.android.server.wm.NonAppWindowAnimationAdapter.shouldStartNonAppWindowAnimationsForKeyguardExit;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.WallpaperAnimationAdapter.shouldStartWallpaperAnimation;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -1143,17 +1142,13 @@
             if (activity == null) {
                 continue;
             }
-            if (activity.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) {
-                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
-                        "Delaying app transition for recents animation to finish");
-                return false;
-            }
             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                     "Check opening app=%s: allDrawn=%b startingDisplayed=%b "
                             + "startingMoved=%b isRelaunching()=%b startingWindow=%s",
                     activity, activity.allDrawn, activity.startingDisplayed,
                     activity.startingMoved, activity.isRelaunching(),
                     activity.mStartingWindow);
+
             final boolean allDrawn = activity.allDrawn && !activity.isRelaunching();
             if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) {
                 return false;
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index ee7d9a9..46ce433 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -153,10 +153,10 @@
             for (WindowContainer wc : mRootMembers) {
                 wc.waitForSyncTransactionCommit(wcAwaitingCommit);
             }
-            final Runnable callback = new Runnable() {
+            class CommitCallback implements Runnable {
                 // Can run a second time if the action completes after the timeout.
                 boolean ran = false;
-                public void run() {
+                public void onCommitted() {
                     synchronized (mWm.mGlobalLock) {
                         if (ran) {
                             return;
@@ -171,8 +171,23 @@
                         wcAwaitingCommit.clear();
                     }
                 }
+
+                // Called in timeout
+                @Override
+                public void run() {
+                    // Sometimes we get a trace, sometimes we get a bugreport without
+                    // a trace. Since these kind of ANRs can trigger such an issue,
+                    // try and ensure we will have some visibility in both cases.
+                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionCommitTimeout");
+                    Slog.e(TAG, "WM sent Transaction to organized, but never received" +
+                           " commit callback. Application ANR likely to follow.");
+                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+                    onCommitted();
+
+                }
             };
-            merged.addTransactionCommittedListener((r) -> { r.run(); }, callback::run);
+            CommitCallback callback = new CommitCallback();
+            merged.addTransactionCommittedListener((r) -> { r.run(); }, callback::onCommitted);
             mWm.mH.postDelayed(callback, BLAST_TIMEOUT_DURATION);
 
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionReady");
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index b37f980..247117e 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -124,8 +124,7 @@
                     LocalServices.getService(WindowManagerInternal.class);
             IBinder focusedWindowToken = windowManagerInternal.getFocusedWindowToken();
 
-            window = wmService.windowForClientLocked(null, focusedWindowToken,
-                    false /* throwOnError */);
+            window = wmService.getFocusedWindowLocked();
 
             if (window == null) {
                 EmbeddedWindowController.EmbeddedWindow embeddedWindow =
@@ -147,12 +146,6 @@
             }
 
             if (window == null) {
-                window = wmService.getFocusedWindowLocked();
-                ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
-                        "Focused window found using wmService.getFocusedWindowLocked()");
-            }
-
-            if (window == null) {
                 // We don't have any focused window, fallback ont the top currentTask of the focused
                 // display.
                 ProtoLog.w(WM_DEBUG_BACK_PREVIEW,
@@ -194,7 +187,6 @@
             if (backType == BackNavigationInfo.TYPE_CALLBACK
                     || currentActivity == null
                     || currentTask == null
-                    || currentTask.getDisplayContent().getImeContainer().isVisible()
                     || currentActivity.isActivityTypeHome()) {
                 return infoBuilder
                         .setType(backType)
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2975a95..d0baa23 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2922,7 +2922,7 @@
     }
 
     DisplayCutout loadDisplayCutout(int displayWidth, int displayHeight) {
-        if (mDisplayPolicy == null) {
+        if (mDisplayPolicy == null || mInitialDisplayCutout == null) {
             return null;
         }
         return DisplayCutout.fromResourcesRectApproximation(
@@ -2931,7 +2931,7 @@
     }
 
     RoundedCorners loadRoundedCorners(int displayWidth, int displayHeight) {
-        if (mDisplayPolicy == null) {
+        if (mDisplayPolicy == null || mInitialRoundedCorners == null) {
             return null;
         }
         return RoundedCorners.fromResources(
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 65062e5..ea72e12 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -573,7 +573,7 @@
                             recentsAnimationController.getTargetAppDisplayArea();
                     if (targetDA != null) {
                         mRecentsAnimationInputConsumer.reparent(mInputTransaction, targetDA);
-                        mRecentsAnimationInputConsumer.show(mInputTransaction, MAX_VALUE - 1);
+                        mRecentsAnimationInputConsumer.show(mInputTransaction, MAX_VALUE - 2);
                         mAddRecentsAnimationInputConsumerHandle = false;
                     }
                 }
@@ -584,10 +584,14 @@
                     final Task rootTask = w.getTask().getRootTask();
                     mPipInputConsumer.mWindowHandle.replaceTouchableRegionWithCrop(
                             rootTask.getSurfaceControl());
+                    final DisplayArea targetDA = rootTask.getDisplayArea();
                     // We set the layer to z=MAX-1 so that it's always on top.
-                    mPipInputConsumer.reparent(mInputTransaction, rootTask);
-                    mPipInputConsumer.show(mInputTransaction, MAX_VALUE - 1);
-                    mAddPipInputConsumerHandle = false;
+                    if (targetDA != null) {
+                        mPipInputConsumer.layout(mInputTransaction, rootTask.getBounds());
+                        mPipInputConsumer.reparent(mInputTransaction, targetDA);
+                        mPipInputConsumer.show(mInputTransaction, MAX_VALUE - 1);
+                        mAddPipInputConsumerHandle = false;
+                    }
                 }
             }
 
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 4068a97..c7f8a1e 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -158,7 +158,7 @@
     void continueStartingAnimations() {
         synchronized (mLock) {
             mAnimationStartDeferred = false;
-            if (!mPendingAnimations.isEmpty()) {
+            if (!mPendingAnimations.isEmpty() && mPreProcessingAnimations.isEmpty()) {
                 mChoreographer.postFrameCallback(this::startAnimations);
             }
         }
@@ -204,7 +204,7 @@
 
                             mPreProcessingAnimations.remove(animationLeash);
                             mPendingAnimations.put(animationLeash, runningAnim);
-                            if (!mAnimationStartDeferred) {
+                            if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
                                 mChoreographer.postFrameCallback(this::startAnimations);
                             }
                         }
@@ -214,7 +214,7 @@
 
             if (!requiresEdgeExtension) {
                 mPendingAnimations.put(animationLeash, runningAnim);
-                if (!mAnimationStartDeferred) {
+                if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
                     mChoreographer.postFrameCallback(this::startAnimations);
                 }
 
@@ -330,6 +330,14 @@
 
     private void startAnimations(long frameTimeNanos) {
         synchronized (mLock) {
+            if (!mPreProcessingAnimations.isEmpty()) {
+                // We only want to start running animations once all mPreProcessingAnimations have
+                // been processed to ensure preprocessed animations start in sync.
+                // NOTE: This means we might delay running animations that require preprocessing if
+                // new animations that also require preprocessing are requested before the previous
+                // ones have finished (see b/227449117).
+                return;
+            }
             startPendingAnimationsLocked();
         }
         mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
@@ -553,4 +561,4 @@
             return mAnimationHandler;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index a480c37..2f00bc8 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1584,14 +1584,6 @@
         return true;
     }
 
-    void forAllWindowContainers(Consumer<WindowContainer> callback) {
-        callback.accept(this);
-        final int count = mChildren.size();
-        for (int i = 0; i < count; i++) {
-            mChildren.get(i).forAllWindowContainers(callback);
-        }
-    }
-
     /**
      * For all windows at or below this container call the callback.
      * @param   callback Calls the {@link ToBooleanFunction#apply} method for each window found and
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index f8bc26a..c0d7d13 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -23,17 +23,18 @@
 import android.annotation.Nullable;
 import android.content.ClipData;
 import android.content.Context;
+import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.display.DisplayManagerInternal;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.util.Pair;
 import android.view.Display;
 import android.view.IInputFilter;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IWindow;
 import android.view.InputChannel;
-import android.view.InputWindowHandle;
 import android.view.MagnificationSpec;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
@@ -459,6 +460,17 @@
     public abstract void getWindowFrame(IBinder token, Rect outBounds);
 
     /**
+     * Get the transformation matrix and MagnificationSpec given its token.
+     *
+     * @param token The token.
+     * @return The pair of the transformation matrix and magnification spec.
+     */
+    // TODO (b/231663133): Long term solution for tracking window when the
+    //                     FLAG_RETRIEVE_INTERACTIVE_WINDOWS is unset.
+    public abstract Pair<Matrix, MagnificationSpec>
+            getWindowTransformationMatrixAndMagnificationSpec(IBinder token);
+
+    /**
      * Opens the global actions dialog.
      */
     public abstract void showGlobalActions();
@@ -849,24 +861,12 @@
     public abstract SurfaceControl getHandwritingSurfaceForDisplay(int displayId);
 
     /**
-     * Replaces the touchable region of the provided input surface with the crop of the window with
-     * the provided token. This method will associate the inputSurface with a copy of
-     * the given inputWindowHandle, where the copy is configured using
-     * {@link InputWindowHandle#replaceTouchableRegionWithCrop(SurfaceControl)} with the surface
-     * of the provided windowToken.
+     * Returns {@code true} if the given point is within the window bounds of the given window.
      *
-     * This is a no-op if windowToken is not valid or the window is not found.
-     *
-     * This does not change any other properties of the inputSurface.
-     *
-     * This method exists to avoid leaking the window's SurfaceControl outside WindowManagerService.
-     *
-     * @param inputSurface The surface for which the touchable region should be set.
-     * @param inputWindowHandle The {@link InputWindowHandle} for the input surface.
-     * @param windowToken The window whose bounds should be used as the touchable region for the
-     *                    inputSurface.
+     * @param windowToken the window whose bounds should be used for the hit test.
+     * @param displayX the x coordinate of the test point in the display's coordinate space.
+     * @param displayY the y coordinate of the test point in the display's coordinate space.
      */
-    public abstract void replaceInputSurfaceTouchableRegionWithWindowCrop(
-            @NonNull SurfaceControl inputSurface, @NonNull InputWindowHandle inputWindowHandle,
-            @NonNull IBinder windowToken);
+    public abstract boolean isPointInsideWindow(
+            @NonNull IBinder windowToken, int displayId, float displayX, float displayY);
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3906a19..8f1f7ec 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -118,7 +118,6 @@
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
 import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
@@ -173,6 +172,7 @@
 import android.content.res.TypedArray;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
+import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -224,6 +224,7 @@
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.MergedConfiguration;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.TimeUtils;
@@ -3051,22 +3052,13 @@
         }
     }
 
-
     void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
         if (mRecentsAnimationController != null) {
             final RecentsAnimationController controller = mRecentsAnimationController;
             mRecentsAnimationController = null;
             controller.cleanupAnimation(reorderMode);
-            // TODO(multi-display): currently only default display support recents animation.
-            // Cancel any existing app transition animation running in the legacy transition
-            // framework.
-            final DisplayContent dc = getDefaultDisplayContentLocked();
-            dc.mAppTransition.freeze();
-            dc.forAllWindowContainers((wc) -> {
-                if (wc.isAnimating(TRANSITION, ANIMATION_TYPE_APP_TRANSITION)) {
-                    wc.cancelAnimation();
-                }
-            });
+            // TODO(mult-display): currently only default display support recents animation.
+            getDefaultDisplayContentLocked().mAppTransition.updateBooster();
         }
     }
 
@@ -7769,6 +7761,13 @@
         }
 
         @Override
+        public Pair<Matrix, MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec(
+                IBinder token) {
+            return mAccessibilityController
+                    .getWindowTransformationMatrixAndMagnificationSpec(token);
+        }
+
+        @Override
         public void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId) {
             final WindowContainer container = displayId == INVALID_DISPLAY
                     ? mRoot : mRoot.getDisplayContent(displayId);
@@ -8241,23 +8240,15 @@
         }
 
         @Override
-        public void replaceInputSurfaceTouchableRegionWithWindowCrop(
-                @NonNull SurfaceControl inputSurface,
-                @NonNull InputWindowHandle inputWindowHandle,
-                @NonNull IBinder windowToken) {
+        public boolean isPointInsideWindow(@NonNull IBinder windowToken, int displayId,
+                float displayX, float displayY) {
             synchronized (mGlobalLock) {
                 final WindowState w = mWindowMap.get(windowToken);
-                if (w == null) {
-                    return;
+                if (w == null || w.getDisplayId() != displayId) {
+                    return false;
                 }
-                // Make a copy of the InputWindowHandle to avoid leaking the window's
-                // SurfaceControl.
-                final InputWindowHandle localHandle = new InputWindowHandle(inputWindowHandle);
-                localHandle.replaceTouchableRegionWithCrop(w.getSurfaceControl());
-                final SurfaceControl.Transaction t = mTransactionFactory.get();
-                t.setInputWindowInfo(inputSurface, localHandle);
-                t.apply();
-                t.close();
+
+                return w.getBounds().contains((int) displayX, (int) displayY);
             }
         }
     }
@@ -8877,16 +8868,18 @@
                 WindowState newFocusTarget =  displayContent == null
                         ? null : displayContent.findFocusedWindow();
                 if (newFocusTarget == null) {
-                    ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus remove request for "
-                                    + "win=%s dropped since no candidate was found",
+                    t.setFocusedWindow(null, null, displayId).apply();
+                    ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s"
+                                    + " dropped focus so setting focus to null since no candidate"
+                                    + " was found",
                             embeddedWindow);
                     return;
                 }
-                t.requestFocusTransfer(newFocusTarget.mInputChannelToken, newFocusTarget.getName(),
-                        inputToken, embeddedWindow.toString(),
+                t.setFocusedWindow(newFocusTarget.mInputChannelToken, newFocusTarget.getName(),
                         displayId).apply();
+
                 EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
-                        "Transfer focus request " + newFocusTarget,
+                        "Focus request " + newFocusTarget,
                         "reason=grantEmbeddedWindowFocus(false)");
             }
             ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s",
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index d2e56fa..ed9b2f0 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -378,7 +378,7 @@
     SurfaceControl.Builder makeSurface() {
         final SurfaceControl.Builder builder = super.makeSurface();
         if (mRoundedCornerOverlay) {
-            builder.setParent(null);
+            builder.setParent(getDisplayContent().getSurfaceControl());
         }
         return builder;
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 3912991..0c69067 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -157,13 +157,14 @@
     private static final String TAG_SSID_ALLOWLIST = "ssid-allowlist";
     private static final String TAG_SSID_DENYLIST = "ssid-denylist";
     private static final String TAG_SSID = "ssid";
-    private static final String ATTR_VALUE = "value";
-    private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
-    private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
     private static final String TAG_PREFERENTIAL_NETWORK_SERVICE_CONFIGS =
             "preferential_network_service_configs";
     private static final String TAG_PREFERENTIAL_NETWORK_SERVICE_CONFIG =
             "preferential_network_service_config";
+    private static final String TAG_PROTECTED_PACKAGES = "protected_packages";
+    private static final String ATTR_VALUE = "value";
+    private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
+    private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
 
     DeviceAdminInfo info;
 
@@ -253,6 +254,9 @@
     // List of package names to keep cached.
     List<String> keepUninstalledPackages;
 
+    // List of packages for which the user cannot invoke "clear data" or "force stop".
+    List<String> protectedPackages;
+
     // Wi-Fi SSID restriction policy.
     WifiSsidPolicy mWifiSsidPolicy;
 
@@ -505,6 +509,7 @@
                 permittedNotificationListeners);
         writePackageListToXml(out, TAG_KEEP_UNINSTALLED_PACKAGES, keepUninstalledPackages);
         writePackageListToXml(out, TAG_METERED_DATA_DISABLED_PACKAGES, meteredDisabledPackages);
+        writePackageListToXml(out, TAG_PROTECTED_PACKAGES, protectedPackages);
         if (hasUserRestrictions()) {
             UserRestrictionsUtils.writeRestrictions(
                     out, userRestrictions, TAG_USER_RESTRICTIONS);
@@ -771,6 +776,8 @@
                 keepUninstalledPackages = readPackageList(parser, tag);
             } else if (TAG_METERED_DATA_DISABLED_PACKAGES.equals(tag)) {
                 meteredDisabledPackages = readPackageList(parser, tag);
+            } else if (TAG_PROTECTED_PACKAGES.equals(tag)) {
+                protectedPackages = readPackageList(parser, tag);
             } else if (TAG_USER_RESTRICTIONS.equals(tag)) {
                 userRestrictions = UserRestrictionsUtils.readRestrictions(parser);
             } else if (TAG_DEFAULT_ENABLED_USER_RESTRICTIONS.equals(tag)) {
@@ -1210,6 +1217,16 @@
             pw.println(keepUninstalledPackages);
         }
 
+        if (meteredDisabledPackages != null) {
+            pw.print("meteredDisabledPackages=");
+            pw.println(meteredDisabledPackages);
+        }
+
+        if (protectedPackages != null) {
+            pw.print("protectedPackages=");
+            pw.println(protectedPackages);
+        }
+
         pw.print("organizationColor=");
         pw.println(organizationColor);
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index 48a436f..fd97db2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -17,6 +17,7 @@
 package com.android.server.devicepolicy;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManager;
@@ -129,8 +130,10 @@
     // This is the list of component allowed to start lock task mode.
     List<String> mLockTaskPackages = new ArrayList<>();
 
-    // List of packages protected by device owner
-    List<String> mUserControlDisabledPackages = new ArrayList<>();
+    /** @deprecated moved to {@link ActiveAdmin#protectedPackages}. */
+    @Deprecated
+    @Nullable
+    List<String> mUserControlDisabledPackages;
 
     // Bitfield of feature flags to be enabled during LockTask mode.
     // We default on the power button menu, in order to be consistent with pre-P behaviour.
@@ -364,13 +367,6 @@
                 out.endTag(null, TAG_OWNER_INSTALLED_CA_CERT);
             }
 
-            for (int i = 0, size = policyData.mUserControlDisabledPackages.size(); i < size; i++) {
-                String packageName = policyData.mUserControlDisabledPackages.get(i);
-                out.startTag(null, TAG_PROTECTED_PACKAGES);
-                out.attribute(null, ATTR_NAME, packageName);
-                out.endTag(null, TAG_PROTECTED_PACKAGES);
-            }
-
             if (policyData.mAppsSuspended) {
                 out.startTag(null, TAG_APPS_SUSPENDED);
                 out.attributeBoolean(null, ATTR_VALUE, policyData.mAppsSuspended);
@@ -473,7 +469,7 @@
             policy.mAdminMap.clear();
             policy.mAffiliationIds.clear();
             policy.mOwnerInstalledCaCerts.clear();
-            policy.mUserControlDisabledPackages.clear();
+            policy.mUserControlDisabledPackages = null;
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -561,15 +557,19 @@
                     policy.mCurrentInputMethodSet = true;
                 } else if (TAG_OWNER_INSTALLED_CA_CERT.equals(tag)) {
                     policy.mOwnerInstalledCaCerts.add(parser.getAttributeValue(null, ATTR_ALIAS));
-                } else if (TAG_PROTECTED_PACKAGES.equals(tag)) {
-                    policy.mUserControlDisabledPackages.add(
-                            parser.getAttributeValue(null, ATTR_NAME));
                 } else if (TAG_APPS_SUSPENDED.equals(tag)) {
                     policy.mAppsSuspended =
                             parser.getAttributeBoolean(null, ATTR_VALUE, false);
                 } else if (TAG_BYPASS_ROLE_QUALIFICATIONS.equals(tag)) {
                     policy.mBypassDevicePolicyManagementRoleQualifications = true;
                     policy.mCurrentRoleHolder =  parser.getAttributeValue(null, ATTR_VALUE);
+                // Deprecated tags below
+                } else if (TAG_PROTECTED_PACKAGES.equals(tag)) {
+                    if (policy.mUserControlDisabledPackages == null) {
+                        policy.mUserControlDisabledPackages = new ArrayList<>();
+                    }
+                    policy.mUserControlDisabledPackages.add(
+                            parser.getAttributeValue(null, ATTR_NAME));
                 } else {
                     Slogf.w(TAG, "Unknown tag: %s", tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -674,8 +674,6 @@
         pw.increaseIndent();
         pw.print("mPasswordOwner="); pw.println(mPasswordOwner);
         pw.print("mPasswordTokenHandle="); pw.println(Long.toHexString(mPasswordTokenHandle));
-        pw.print("mUserControlDisabledPackages=");
-        pw.println(mUserControlDisabledPackages);
         pw.print("mAppsSuspended="); pw.println(mAppsSuspended);
         pw.print("mUserSetupComplete="); pw.println(mUserSetupComplete);
         pw.print("mAffiliationIds="); pw.println(mAffiliationIds);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8a7134e..ceac102 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -536,7 +536,7 @@
     // to decide whether an existing policy in the {@link #DEVICE_POLICIES_XML} needs to
     // be upgraded. See {@link PolicyVersionUpgrader} on instructions how to add an upgrade
     // step.
-    static final int DPMS_VERSION = 2;
+    static final int DPMS_VERSION = 3;
 
     static {
         SECURE_SETTINGS_ALLOWLIST = new ArraySet<>();
@@ -1908,39 +1908,12 @@
                 if (userHandle == UserHandle.USER_SYSTEM) {
                     mStateCache.setDeviceProvisioned(policy.mUserSetupComplete);
                 }
-
-                migrateDeviceOwnerProtectedPackagesToOwners(userHandle, policy);
             }
             return policy;
         }
     }
 
     /**
-     * Only used by {@link #getUserData(int)} to migrate <b>existing</b> device owner protected
-     * packages that were stored in {@link DevicePolicyData#mUserControlDisabledPackages} to
-     * {@link Owners} because the device owner protected packages are now stored on a per device
-     * owner basis instead of on a per user basis.
-     *
-     * Any calls to {@link #setUserControlDisabledPackages(ComponentName, List)} would now store
-     * the device owner protected packages in {@link Owners} instead of {@link DevicePolicyData}.
-     * @param userHandle The device owner user
-     * @param policy The policy data of the device owner user
-     */
-    private void migrateDeviceOwnerProtectedPackagesToOwners(
-            int userHandle, DevicePolicyData policy) {
-        ComponentName deviceOwnerComponent = getOwnerComponent(userHandle);
-        if (isDeviceOwner(deviceOwnerComponent, userHandle)
-                && !policy.mUserControlDisabledPackages.isEmpty()) {
-            mOwners.setDeviceOwnerProtectedPackages(
-                    deviceOwnerComponent.getPackageName(),
-                    policy.mUserControlDisabledPackages);
-
-            policy.mUserControlDisabledPackages = new ArrayList<>();
-            saveSettingsLocked(userHandle);
-        }
-    }
-
-    /**
      * Creates and loads the policy data from xml for data that is shared between
      * various profiles of a user. In contrast to {@link #getUserData(int)}
      * it allows access to data of users other than the calling user.
@@ -3182,6 +3155,7 @@
     void handleStartUser(int userId) {
         synchronized (getLockObject()) {
             pushScreenCapturePolicy(userId);
+            pushUserControlDisabledPackagesLocked(userId);
         }
         pushUserRestrictions(userId);
         // When system user is started (device boot), load cache for all users.
@@ -3204,6 +3178,20 @@
         startOwnerService(userId, "start-user");
     }
 
+    // TODO(b/218639412): Once PM stores these on a per-user basis, push even empty lists to handle
+    // DO/PO removal correctly.
+    void pushUserControlDisabledPackagesLocked(int userId) {
+        if (userId != mOwners.getDeviceOwnerUserId()) {
+            return;
+        }
+        ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+        if (deviceOwner == null || deviceOwner.protectedPackages == null) {
+            return;
+        }
+        mInjector.getPackageManagerInternal().setDeviceOwnerProtectedPackages(
+                deviceOwner.info.getPackageName(), deviceOwner.protectedPackages);
+    }
+
     @Override
     void handleUnlockUser(int userId) {
         startOwnerService(userId, "unlock-user");
@@ -8513,6 +8501,13 @@
         }
     }
 
+    private boolean isDeviceOwnerUserId(int userId) {
+        synchronized (getLockObject()) {
+            return mOwners.getDeviceOwnerComponent() != null
+                    && mOwners.getDeviceOwnerUserId() == userId;
+        }
+    }
+
     private boolean isDeviceOwnerPackage(String packageName, int userId) {
         synchronized (getLockObject()) {
             return mOwners.hasDeviceOwner()
@@ -8795,6 +8790,7 @@
         setNetworkLoggingActiveInternal(false);
         deleteTransferOwnershipBundleLocked(userId);
         toggleBackupServiceActive(UserHandle.USER_SYSTEM, true);
+        pushUserControlDisabledPackagesLocked(userId);
     }
 
     private void clearApplicationRestrictions(int userId) {
@@ -8979,7 +8975,6 @@
         policy.mLockTaskPackages.clear();
         updateLockTaskPackagesLocked(policy.mLockTaskPackages, userId);
         policy.mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
-        policy.mUserControlDisabledPackages.clear();
         saveSettingsLocked(userId);
 
         try {
@@ -12117,10 +12112,11 @@
             return;
         }
         final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(isProfileOwner(caller)
+        Preconditions.checkCallAuthorization((isProfileOwner(caller)
+                        && isManagedProfile(caller.getUserId()))
                         || isDefaultDeviceOwner(caller),
-                "Caller is not profile owner or device owner;"
-                        + " only profile owner or device owner may control the preferential"
+                "Caller is not managed profile owner or device owner;"
+                        + " only managed profile owner or device owner may control the preferential"
                         + " network service");
         synchronized (getLockObject()) {
             final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked(
@@ -12147,11 +12143,12 @@
         }
 
         final CallerIdentity caller = getCallerIdentity();
-        Preconditions.checkCallAuthorization(isProfileOwner(caller)
+        Preconditions.checkCallAuthorization((isProfileOwner(caller)
+                        && isManagedProfile(caller.getUserId()))
                         || isDefaultDeviceOwner(caller),
-                "Caller is not profile owner or device owner;"
-                        + " only profile owner or device owner may retrieve the preferential"
-                        + " network service configurations");
+                "Caller is not managed profile owner or device owner;"
+                        + " only managed profile owner or device owner may retrieve the "
+                        + "preferential network service configurations");
         synchronized (getLockObject()) {
             final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked(
                     caller.getUserId());
@@ -16976,13 +16973,22 @@
                 DevicePolicyManager.OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES);
 
         synchronized (getLockObject()) {
-            mOwners.setDeviceOwnerProtectedPackages(who.getPackageName(), packages);
-            DevicePolicyEventLogger
-                    .createEvent(DevicePolicyEnums.SET_USER_CONTROL_DISABLED_PACKAGES)
-                    .setAdmin(who)
-                    .setStrings(packages.toArray(new String[packages.size()]))
-                    .write();
+            ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+            if (!Objects.equals(deviceOwner.protectedPackages, packages)) {
+                deviceOwner.protectedPackages = packages.isEmpty() ? null : packages;
+                saveSettingsLocked(caller.getUserId());
+            }
         }
+
+        mInjector.binderWithCleanCallingIdentity(
+                () -> mInjector.getPackageManagerInternal().setDeviceOwnerProtectedPackages(
+                        who.getPackageName(), packages));
+
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_USER_CONTROL_DISABLED_PACKAGES)
+                .setAdmin(who)
+                .setStrings(packages.toArray(new String[packages.size()]))
+                .write();
     }
 
     @Override
@@ -16994,7 +17000,9 @@
                 isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller));
 
         synchronized (getLockObject()) {
-            return mOwners.getDeviceOwnerProtectedPackages(who.getPackageName());
+            ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+            return deviceOwner.protectedPackages != null
+                    ? deviceOwner.protectedPackages : Collections.emptyList();
         }
     }
 
@@ -18266,7 +18274,7 @@
 
     private void updateNetworkPreferenceForUser(int userId,
             List<PreferentialNetworkServiceConfig> preferentialNetworkServiceConfigs) {
-        if (!isManagedProfile(userId)) {
+        if (!isManagedProfile(userId) && !isDeviceOwnerUserId(userId)) {
             return;
         }
         List<ProfileNetworkPreference> preferences = new ArrayList<>();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index d1c6b34..08bd3e4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -31,7 +31,6 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.Pair;
@@ -49,7 +48,6 @@
 import java.io.File;
 import java.time.LocalDate;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -108,12 +106,6 @@
 
             notifyChangeLocked();
             pushToActivityTaskManagerLocked();
-
-            for (ArrayMap.Entry<String, List<String>> entry :
-                    mData.mDeviceOwnerProtectedPackages.entrySet()) {
-                mPackageManagerInternal.setDeviceOwnerProtectedPackages(
-                        entry.getKey(), entry.getValue());
-            }
         }
     }
 
@@ -247,12 +239,6 @@
     void clearDeviceOwner() {
         synchronized (mData) {
             mData.mDeviceOwnerTypes.remove(mData.mDeviceOwner.packageName);
-            List<String> protectedPackages =
-                    mData.mDeviceOwnerProtectedPackages.remove(mData.mDeviceOwner.packageName);
-            if (protectedPackages != null) {
-                mPackageManagerInternal.setDeviceOwnerProtectedPackages(
-                        mData.mDeviceOwner.packageName, new ArrayList<>());
-            }
             mData.mDeviceOwner = null;
             mData.mDeviceOwnerUserId = UserHandle.USER_NULL;
 
@@ -296,12 +282,6 @@
         synchronized (mData) {
             Integer previousDeviceOwnerType = mData.mDeviceOwnerTypes.remove(
                     mData.mDeviceOwner.packageName);
-            List<String> previousProtectedPackages =
-                    mData.mDeviceOwnerProtectedPackages.remove(mData.mDeviceOwner.packageName);
-            if (previousProtectedPackages != null) {
-                mPackageManagerInternal.setDeviceOwnerProtectedPackages(
-                        mData.mDeviceOwner.packageName, new ArrayList<>());
-            }
             // We don't set a name because it's not used anyway.
             // See DevicePolicyManagerService#getDeviceOwnerName
             mData.mDeviceOwner = new OwnerInfo(null, target,
@@ -313,10 +293,6 @@
                 mData.mDeviceOwnerTypes.put(
                         mData.mDeviceOwner.packageName, previousDeviceOwnerType);
             }
-            if (previousProtectedPackages != null) {
-                mData.mDeviceOwnerProtectedPackages.put(
-                        mData.mDeviceOwner.packageName, previousProtectedPackages);
-            }
             notifyChangeLocked();
             pushToActivityTaskManagerLocked();
         }
@@ -504,34 +480,6 @@
         }
     }
 
-    void setDeviceOwnerProtectedPackages(String packageName, List<String> protectedPackages) {
-        synchronized (mData) {
-            if (!hasDeviceOwner()) {
-                Slog.e(TAG,
-                        "Attempting to set device owner protected packages when there is no "
-                                + "device owner");
-                return;
-            } else if (!mData.mDeviceOwner.packageName.equals(packageName)) {
-                Slog.e(TAG, "Attempting to set device owner protected packages when the provided "
-                        + "package name " + packageName
-                        + " does not match the device owner package name");
-                return;
-            }
-
-            mData.mDeviceOwnerProtectedPackages.put(packageName, protectedPackages);
-            mPackageManagerInternal.setDeviceOwnerProtectedPackages(packageName, protectedPackages);
-            writeDeviceOwner();
-        }
-    }
-
-    List<String> getDeviceOwnerProtectedPackages(String packageName) {
-        synchronized (mData) {
-            return mData.mDeviceOwnerProtectedPackages.containsKey(packageName)
-                    ? mData.mDeviceOwnerProtectedPackages.get(packageName)
-                    : Collections.emptyList();
-        }
-    }
-
     void writeDeviceOwner() {
         synchronized (mData) {
             pushToDevicePolicyManager();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
index 4fe4f0d..6948420 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
@@ -91,8 +91,10 @@
     // Device owner type for a managed device.
     final ArrayMap<String, Integer> mDeviceOwnerTypes = new ArrayMap<>();
 
-    final ArrayMap<String, List<String>> mDeviceOwnerProtectedPackages = new ArrayMap<>();
-
+    /** @deprecated moved to {@link ActiveAdmin#protectedPackages}. */
+    @Deprecated
+    @Nullable
+    ArrayMap<String, List<String>> mDeviceOwnerProtectedPackages;
 
     // Internal state for the profile owner packages.
     final ArrayMap<Integer, OwnerInfo> mProfileOwners = new ArrayMap<>();
@@ -366,21 +368,6 @@
                 }
             }
 
-            if (!mDeviceOwnerProtectedPackages.isEmpty()) {
-                for (ArrayMap.Entry<String, List<String>> entry :
-                        mDeviceOwnerProtectedPackages.entrySet()) {
-                    List<String> protectedPackages = entry.getValue();
-
-                    out.startTag(null, TAG_DEVICE_OWNER_PROTECTED_PACKAGES);
-                    out.attribute(null, ATTR_PACKAGE, entry.getKey());
-                    out.attributeInt(null, ATTR_SIZE, protectedPackages.size());
-                    for (int i = 0, size = protectedPackages.size(); i < size; i++) {
-                        out.attribute(null, ATTR_NAME + i, protectedPackages.get(i));
-                    }
-                    out.endTag(null, TAG_DEVICE_OWNER_PROTECTED_PACKAGES);
-                }
-            }
-
             if (mSystemUpdatePolicy != null) {
                 out.startTag(null, TAG_SYSTEM_UPDATE_POLICY);
                 mSystemUpdatePolicy.saveToXml(out);
@@ -444,6 +431,7 @@
                             null, ATTR_DEVICE_OWNER_TYPE_VALUE, DEVICE_OWNER_TYPE_DEFAULT);
                     mDeviceOwnerTypes.put(packageName, deviceOwnerType);
                     break;
+                // Deprecated fields below.
                 case TAG_DEVICE_OWNER_PROTECTED_PACKAGES:
                     packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
                     int protectedPackagesSize = parser.getAttributeInt(null, ATTR_SIZE, 0);
@@ -451,6 +439,9 @@
                     for (int i = 0; i < protectedPackagesSize; i++) {
                         protectedPackages.add(parser.getAttributeValue(null, ATTR_NAME + i));
                     }
+                    if (mDeviceOwnerProtectedPackages == null) {
+                        mDeviceOwnerProtectedPackages = new ArrayMap<>();
+                    }
                     mDeviceOwnerProtectedPackages.put(packageName, protectedPackages);
                     break;
                 default:
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
index 7556d69..253851c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
@@ -16,6 +16,7 @@
 
 package com.android.server.devicepolicy;
 
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.os.UserHandle;
 import android.util.Slog;
@@ -28,6 +29,8 @@
 import java.io.IOException;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Class for dealing with Device Policy Manager Service version upgrades.
@@ -91,30 +94,82 @@
 
         if (currentVersion == 1) {
             Slog.i(LOG_TAG, String.format("Upgrading from version %d", currentVersion));
-            // This upgrade step is for Device Owner scenario only: For devices upgrading to S,
-            // if there is a device owner, it retains the ability to control sensors-related
-            // permission grants.
-            for (int userId : allUsers) {
-                DevicePolicyData userData = allUsersData.get(userId);
-                if (userData == null) {
-                    continue;
-                }
-                for (ActiveAdmin admin : userData.mAdminList) {
-                    if (ownersData.mDeviceOwnerUserId == userId
-                            && ownersData.mDeviceOwner != null
-                            && ownersData.mDeviceOwner.admin.equals(admin.info.getComponent())) {
-                        Slog.i(LOG_TAG, String.format(
-                                "Marking Device Owner in user %d for permission grant ", userId));
-                        admin.mAdminCanGrantSensorsPermissions = true;
-                    }
-                }
-            }
+            upgradeSensorPermissionsAccess(allUsers, ownersData, allUsersData);
             currentVersion = 2;
         }
 
+        if (currentVersion == 2) {
+            Slog.i(LOG_TAG, String.format("Upgrading from version %d", currentVersion));
+            upgradeProtectedPackages(ownersData, allUsersData);
+            currentVersion = 3;
+        }
+
         writePoliciesAndVersion(allUsers, allUsersData, ownersData, currentVersion);
     }
 
+    /**
+     * This upgrade step is for Device Owner scenario only: For devices upgrading to S, if there is
+     * a device owner, it retains the ability to control sensors-related permission grants.
+     */
+    private void upgradeSensorPermissionsAccess(
+            int[] allUsers, OwnersData ownersData, SparseArray<DevicePolicyData> allUsersData) {
+        for (int userId : allUsers) {
+            DevicePolicyData userData = allUsersData.get(userId);
+            if (userData == null) {
+                continue;
+            }
+            for (ActiveAdmin admin : userData.mAdminList) {
+                if (ownersData.mDeviceOwnerUserId == userId
+                        && ownersData.mDeviceOwner != null
+                        && ownersData.mDeviceOwner.admin.equals(admin.info.getComponent())) {
+                    Slog.i(LOG_TAG, String.format(
+                            "Marking Device Owner in user %d for permission grant ", userId));
+                    admin.mAdminCanGrantSensorsPermissions = true;
+                }
+            }
+        }
+    }
+
+    /**
+     * This upgrade step moves device owner protected packages to ActiveAdmin.
+     * Initially these packages were stored in DevicePolicyData, then moved to Owners without
+     * employing PolicyVersionUpgrader. Here we check both places.
+     */
+    private void upgradeProtectedPackages(
+            OwnersData ownersData, SparseArray<DevicePolicyData> allUsersData) {
+        if (ownersData.mDeviceOwner == null) {
+            return;
+        }
+        List<String> protectedPackages = null;
+        DevicePolicyData doUserData = allUsersData.get(ownersData.mDeviceOwnerUserId);
+        if (doUserData == null) {
+            Slog.e(LOG_TAG, "No policy data for do user");
+            return;
+        }
+        if (ownersData.mDeviceOwnerProtectedPackages != null) {
+            protectedPackages = ownersData.mDeviceOwnerProtectedPackages
+                    .get(ownersData.mDeviceOwner.packageName);
+            if (protectedPackages != null) {
+                Slog.i(LOG_TAG, "Found protected packages in Owners");
+            }
+            ownersData.mDeviceOwnerProtectedPackages = null;
+        } else if (doUserData.mUserControlDisabledPackages != null) {
+            Slog.i(LOG_TAG, "Found protected packages in DevicePolicyData");
+            protectedPackages = doUserData.mUserControlDisabledPackages;
+            doUserData.mUserControlDisabledPackages = null;
+        }
+
+        ActiveAdmin doAdmin = doUserData.mAdminMap.get(ownersData.mDeviceOwner.admin);
+        if (doAdmin == null) {
+            Slog.e(LOG_TAG, "DO admin not found in DO user");
+            return;
+        }
+
+        if (protectedPackages != null) {
+            doAdmin.protectedPackages = new ArrayList<>(protectedPackages);
+        }
+    }
+
     private OwnersData loadOwners(int[] allUsers) {
         OwnersData ownersData = new OwnersData(mPathProvider);
         ownersData.load(allUsers);
@@ -146,17 +201,23 @@
             OwnersData ownersData) {
         final SparseArray<DevicePolicyData> allUsersData = new SparseArray<>();
         for (int user: allUsers) {
-            ComponentName owner = null;
-            if (ownersData.mDeviceOwnerUserId == user && ownersData.mDeviceOwner != null) {
-                owner = ownersData.mDeviceOwner.admin;
-            } else if (ownersData.mProfileOwners.containsKey(user)) {
-                owner = ownersData.mProfileOwners.get(user).admin;
-            }
+            ComponentName owner = getOwnerForUser(ownersData, user);
             allUsersData.append(user, loadDataForUser(user, loadVersion, owner));
         }
         return allUsersData;
     }
 
+    @Nullable
+    private ComponentName getOwnerForUser(OwnersData ownersData, int user) {
+        ComponentName owner = null;
+        if (ownersData.mDeviceOwnerUserId == user && ownersData.mDeviceOwner != null) {
+            owner = ownersData.mDeviceOwner.admin;
+        } else if (ownersData.mProfileOwners.containsKey(user)) {
+            owner = ownersData.mProfileOwners.get(user).admin;
+        }
+        return owner;
+    }
+
     private DevicePolicyData loadDataForUser(
             int userId, int loadVersion, ComponentName ownerComponent) {
         DevicePolicyData policy = new DevicePolicyData(userId);
diff --git a/services/proguard.flags b/services/proguard.flags
index 425da6c..bad02b4 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -33,6 +33,11 @@
   public <init>(...);
 }
 
+# Accessed from com.android.compos APEX
+-keep,allowoptimization,allowaccessmodification class com.android.internal.art.ArtStatsLog {
+   public static void write(...);
+}
+
 # Binder interfaces
 -keep,allowoptimization,allowaccessmodification class * extends android.os.IInterface
 -keep,allowoptimization,allowaccessmodification class * extends android.os.IHwInterface
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index ac54293..009dae5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -582,6 +582,7 @@
 
         DeviceConfigSession<Boolean> bgCurrentDrainMonitor = null;
         DeviceConfigSession<Long> bgCurrentDrainWindow = null;
+        DeviceConfigSession<Long> bgCurrentDrainInteractionGracePeriod = null;
         DeviceConfigSession<Float> bgCurrentDrainRestrictedBucketThreshold = null;
         DeviceConfigSession<Float> bgCurrentDrainBgRestrictedThreshold = null;
         DeviceConfigSession<Boolean> bgPromptFgsWithNotiToBgRestricted = null;
@@ -617,6 +618,14 @@
                             R.integer.config_bg_current_drain_window));
             bgCurrentDrainWindow.set(windowMs);
 
+            bgCurrentDrainInteractionGracePeriod = new DeviceConfigSession<>(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_INTERACTION_GRACE_PERIOD,
+                    DeviceConfig::getLong,
+                    (long) mContext.getResources().getInteger(
+                            R.integer.config_bg_current_drain_window));
+            bgCurrentDrainInteractionGracePeriod.set(windowMs);
+
             bgCurrentDrainRestrictedBucketThreshold = new DeviceConfigSession<>(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET,
@@ -768,6 +777,32 @@
 
             clearInvocations(mInjector.getAppStandbyInternal());
 
+            // It won't be restricted since user just interacted with it.
+            runTestBgCurrentDrainMonitorOnce(listener, stats, uids,
+                    zeros, new double[]{0, restrictBucketThresholdMah - 1},
+                    zeros, new double[]{restrictBucketThresholdMah + 1, 0},
+                    () -> {
+                        doReturn(mCurrentTimeMillis).when(stats).getStatsStartTimestamp();
+                        doReturn(mCurrentTimeMillis + windowMs)
+                                .when(stats).getStatsEndTimestamp();
+                        mCurrentTimeMillis += windowMs + 1;
+                        try {
+                            listener.verify(timeout, testUid, testPkgName,
+                                    RESTRICTION_LEVEL_RESTRICTED_BUCKET);
+                            fail("There shouldn't be any level change events");
+                        } catch (Exception e) {
+                            // Expected.
+                        }
+                        verify(mInjector.getAppStandbyInternal(), never()).restrictApp(
+                                eq(testPkgName),
+                                eq(testUser),
+                                anyInt(), anyInt());
+                    });
+
+            // Sleep a while.
+            Thread.sleep(windowMs);
+            clearInvocations(mInjector.getAppStandbyInternal());
+            // Now it should have been restricted.
             runTestBgCurrentDrainMonitorOnce(listener, stats, uids,
                     zeros, new double[]{0, restrictBucketThresholdMah - 1},
                     zeros, new double[]{restrictBucketThresholdMah + 1, 0},
@@ -1061,6 +1096,7 @@
         } finally {
             closeIfNotNull(bgCurrentDrainMonitor);
             closeIfNotNull(bgCurrentDrainWindow);
+            closeIfNotNull(bgCurrentDrainInteractionGracePeriod);
             closeIfNotNull(bgCurrentDrainRestrictedBucketThreshold);
             closeIfNotNull(bgCurrentDrainBgRestrictedThreshold);
             closeIfNotNull(bgPromptFgsWithNotiToBgRestricted);
@@ -1610,6 +1646,7 @@
 
         DeviceConfigSession<Boolean> bgCurrentDrainMonitor = null;
         DeviceConfigSession<Long> bgCurrentDrainWindow = null;
+        DeviceConfigSession<Long> bgCurrentDrainInteractionGracePeriod = null;
         DeviceConfigSession<Float> bgCurrentDrainRestrictedBucketThreshold = null;
         DeviceConfigSession<Float> bgCurrentDrainBgRestrictedThreshold = null;
         DeviceConfigSession<Float> bgCurrentDrainRestrictedBucketHighThreshold = null;
@@ -1655,6 +1692,14 @@
                             R.integer.config_bg_current_drain_window));
             bgCurrentDrainWindow.set(windowMs);
 
+            bgCurrentDrainInteractionGracePeriod = new DeviceConfigSession<>(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_INTERACTION_GRACE_PERIOD,
+                    DeviceConfig::getLong,
+                    (long) mContext.getResources().getInteger(
+                            R.integer.config_bg_current_drain_window));
+            bgCurrentDrainInteractionGracePeriod.set(windowMs);
+
             bgCurrentDrainRestrictedBucketThreshold = new DeviceConfigSession<>(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET,
@@ -2176,6 +2221,7 @@
         } finally {
             closeIfNotNull(bgCurrentDrainMonitor);
             closeIfNotNull(bgCurrentDrainWindow);
+            closeIfNotNull(bgCurrentDrainInteractionGracePeriod);
             closeIfNotNull(bgCurrentDrainRestrictedBucketThreshold);
             closeIfNotNull(bgCurrentDrainBgRestrictedThreshold);
             closeIfNotNull(bgCurrentDrainRestrictedBucketHighThreshold);
diff --git a/services/tests/servicestests/assets/PolicyVersionUpgraderTest/protected_packages_device_owner_2.xml b/services/tests/servicestests/assets/PolicyVersionUpgraderTest/protected_packages_device_owner_2.xml
new file mode 100644
index 0000000..0725d25
--- /dev/null
+++ b/services/tests/servicestests/assets/PolicyVersionUpgraderTest/protected_packages_device_owner_2.xml
@@ -0,0 +1,6 @@
+<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>

+<root>

+  <device-owner package="com.android.frameworks.servicestests" name="" component="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1" userRestrictionsMigrated="true" isPoOrganizationOwnedDevice="true" />

+  <device-owner-context userId="0" />

+  <device-owner-protected-packages package="com.android.frameworks.servicestests" size="2" name0="com.some.app" name1="foo.bar.baz" />

+</root>

diff --git a/services/tests/servicestests/assets/PolicyVersionUpgraderTest/protected_packages_device_policies.xml b/services/tests/servicestests/assets/PolicyVersionUpgraderTest/protected_packages_device_policies.xml
new file mode 100644
index 0000000..2d06ee6
--- /dev/null
+++ b/services/tests/servicestests/assets/PolicyVersionUpgraderTest/protected_packages_device_policies.xml
@@ -0,0 +1,13 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<policies setup-complete="true" provisioning-state="3">
+    <admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1">
+        <policies flags="991" />
+        <strong-auth-unlock-timeout value="0" />
+        <test-only-admin value="true" />
+        <cross-profile-calendar-packages />
+        <cross-profile-packages />
+    </admin>
+    <lock-task-features value="16" />
+    <protected-packages name="com.some.app" />
+    <protected-packages name="foo.bar.baz" />
+</policies>
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 08df438..19df5a2 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -89,8 +89,10 @@
 import android.os.Process;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
+import android.util.Pair;
 import android.view.Display;
 import android.view.KeyEvent;
+import android.view.MagnificationSpec;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
@@ -145,6 +147,8 @@
     private static final int USER_ID = 1;
     private static final int USER_ID2 = 2;
     private static final int INTERACTION_ID = 199;
+    private static final Pair<float[], MagnificationSpec> FAKE_MATRIX_AND_MAG_SPEC =
+            new Pair<>(new float[9], new MagnificationSpec());
     private static final int PID = Process.myPid();
     private static final long TID = Process.myTid();
     private static final int UID = Process.myUid();
@@ -188,6 +192,8 @@
                 .thenReturn(mMockFingerprintGestureDispatcher);
         when(mMockSystemSupport.getMagnificationProcessor())
                 .thenReturn(mMockMagnificationProcessor);
+        when(mMockSystemSupport.getWindowTransformationMatrixAndMagnificationSpec(anyInt()))
+                .thenReturn(FAKE_MATRIX_AND_MAG_SPEC);
 
         PowerManager powerManager =
                 new PowerManager(mMockContext, mMockIPowerManager, mMockIThermalService, mHandler);
diff --git a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
index d1390c6..e68a8a0 100644
--- a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
@@ -49,10 +49,11 @@
         assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
         assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
         assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
         // Different processes and tags should not get rate limited either.
         assertFalse(mRateLimiter.shouldRateLimit("tag", "process2").shouldRateLimit());
         assertFalse(mRateLimiter.shouldRateLimit("tag2", "process").shouldRateLimit());
-        // The 6th entry of the same process should be rate limited.
+        // The 7th entry of the same process should be rate limited.
         assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
     }
 
@@ -64,12 +65,13 @@
         assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
         assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
         assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
-        // The 6th entry of the same process should be rate limited.
+        assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
+        // The 7th entry of the same process should be rate limited.
         assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
 
-        // After 11 seconds there should be nothing left in the buffer and the same type of entry
+        // After 11 minutes there should be nothing left in the buffer and the same type of entry
         // should not get rate limited anymore.
-        mClock.setOffsetMillis(11000);
+        mClock.setOffsetMillis(11 * 60 * 1000);
 
         assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
     }
@@ -86,13 +88,15 @@
                 mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
         assertEquals(0,
                 mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
+        assertEquals(0,
+                mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
         assertEquals(1,
                 mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
         assertEquals(2,
                 mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated());
 
-        // After 11 seconds the rate limiting buffer will be cleared and rate limiting will stop.
-        mClock.setOffsetMillis(11000);
+        // After 11 minutes the rate limiting buffer will be cleared and rate limiting will stop.
+        mClock.setOffsetMillis(11 * 60 * 1000);
 
         // The first call after rate limiting stops will still return the number of dropped events.
         assertEquals(2,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
index 9efc10c..412722d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
@@ -63,8 +63,9 @@
 public class PolicyVersionUpgraderTest extends DpmTestBase {
     // NOTE: Only change this value if the corresponding CL also adds a test to test the upgrade
     // to the new version.
-    private static final int LATEST_TESTED_VERSION = 2;
+    private static final int LATEST_TESTED_VERSION = 3;
     public static final String PERMISSIONS_TAG = "admin-can-grant-sensors-permissions";
+    public static final String DEVICE_OWNER_XML = "device_owner_2.xml";
     private ComponentName mFakeAdmin;
 
     private class FakePolicyUpgraderDataProvider implements PolicyUpgraderDataProvider {
@@ -119,18 +120,18 @@
         mUpgrader = new PolicyVersionUpgrader(mProvider, getServices().pathProvider);
         mFakeAdmin = new ComponentName(
                 "com.android.frameworks.servicestests",
-                        "com.android.server.devicepolicy.DummyDeviceAdmins$Admin1");
+                "com.android.server.devicepolicy.DummyDeviceAdmins$Admin1");
         ActivityInfo activityInfo = createActivityInfo(mFakeAdmin);
         DeviceAdminInfo dai = createDeviceAdminInfo(activityInfo);
         mProvider.mComponentToDeviceAdminInfo.put(mFakeAdmin, dai);
-        mProvider.mUsers = new int[] {0};
+        mProvider.mUsers = new int[]{0};
     }
 
     @Test
     public void testSameVersionDoesNothing() throws IOException {
         writeVersionToXml(DevicePolicyManagerService.DPMS_VERSION);
         final int userId = mProvider.mUsers[0];
-        preparePoliciesFile(userId);
+        preparePoliciesFile(userId, "device_policies.xml");
         String oldContents = readPoliciesFile(userId);
 
         mUpgrader.upgradePolicy(DevicePolicyManagerService.DPMS_VERSION);
@@ -142,19 +143,19 @@
     @Test
     public void testUpgrade0To1RemovesPasswordMetrics() throws IOException, XmlPullParserException {
         final String activePasswordTag = "active-password";
-        mProvider.mUsers = new int[] {0, 10};
+        mProvider.mUsers = new int[]{0, 10};
         getServices().addUser(10, /* flags= */ 0, USER_TYPE_PROFILE_MANAGED);
         writeVersionToXml(0);
         for (int userId : mProvider.mUsers) {
-            preparePoliciesFile(userId);
+            preparePoliciesFile(userId, "device_policies.xml");
         }
         // Validate test set-up.
         assertThat(isTagPresent(readPoliciesFileToStream(0), activePasswordTag)).isTrue();
 
         mUpgrader.upgradePolicy(1);
 
-        assertThat(readVersionFromXml()).isGreaterThan(1);
-        for (int user: mProvider.mUsers) {
+        assertThat(readVersionFromXml()).isAtLeast(1);
+        for (int user : mProvider.mUsers) {
             assertThat(isTagPresent(readPoliciesFileToStream(user), activePasswordTag)).isFalse();
         }
     }
@@ -163,17 +164,17 @@
     public void testUpgrade1To2MarksDoForPermissionControl()
             throws IOException, XmlPullParserException {
         final int ownerUser = 10;
-        mProvider.mUsers = new int[] {0, ownerUser};
+        mProvider.mUsers = new int[]{0, ownerUser};
         getServices().addUser(ownerUser, FLAG_PRIMARY, USER_TYPE_FULL_SYSTEM);
         writeVersionToXml(1);
         for (int userId : mProvider.mUsers) {
-            preparePoliciesFile(userId);
+            preparePoliciesFile(userId, "device_policies.xml");
         }
-        prepareDeviceOwnerFile(ownerUser);
+        prepareDeviceOwnerFile(ownerUser, "device_owner_2.xml");
 
         mUpgrader.upgradePolicy(2);
 
-        assertThat(readVersionFromXml()).isEqualTo(2);
+        assertThat(readVersionFromXml()).isAtLeast(2);
         assertThat(getBooleanValueTag(readPoliciesFileToStream(mProvider.mUsers[0]),
                 PERMISSIONS_TAG)).isFalse();
         assertThat(getBooleanValueTag(readPoliciesFileToStream(ownerUser),
@@ -186,8 +187,8 @@
         getServices().addUser(ownerUser, FLAG_PRIMARY, USER_TYPE_FULL_SYSTEM);
         setUpPackageManagerForAdmin(admin1, UserHandle.getUid(ownerUser, 123 /* admin app ID */));
         writeVersionToXml(0);
-        preparePoliciesFile(ownerUser);
-        prepareDeviceOwnerFile(ownerUser);
+        preparePoliciesFile(ownerUser, "device_policies.xml");
+        prepareDeviceOwnerFile(ownerUser, "device_owner_2.xml");
 
         DevicePolicyManagerServiceTestable dpms;
         final long ident = getContext().binder.clearCallingIdentity();
@@ -202,11 +203,65 @@
             getContext().binder.restoreCallingIdentity(ident);
         }
 
+        assertThat(readVersionFromXml()).isEqualTo(DevicePolicyManagerService.DPMS_VERSION);
+
         // DO should be marked as able to grant sensors permission during upgrade and should be
         // reported as such via the API.
         assertThat(dpms.canAdminGrantSensorsPermissionsForUser(ownerUser)).isTrue();
     }
 
+    /**
+     * Up to Android R DO protected packages were stored in DevicePolicyData, verify that they are
+     * moved to ActiveAdmin.
+     */
+    @Test
+    public void testUserControlDisabledPackagesFromR() throws Exception {
+        final String oldTag = "protected-packages";
+        final String newTag = "protected_packages";
+        final int ownerUser = 0;
+        mProvider.mUsers = new int[]{0};
+        getServices().addUser(ownerUser, FLAG_PRIMARY, USER_TYPE_FULL_SYSTEM);
+        writeVersionToXml(2);
+        preparePoliciesFile(ownerUser, "protected_packages_device_policies.xml");
+        prepareDeviceOwnerFile(ownerUser, "device_owner_2.xml");
+
+        // Validate the setup.
+        assertThat(isTagPresent(readPoliciesFileToStream(ownerUser), oldTag)).isTrue();
+        assertThat(isTagPresent(readPoliciesFileToStream(ownerUser), newTag)).isFalse();
+
+        mUpgrader.upgradePolicy(3);
+
+        assertThat(readVersionFromXml()).isAtLeast(3);
+        assertThat(isTagPresent(readPoliciesFileToStream(ownerUser), oldTag)).isFalse();
+        assertThat(isTagPresent(readPoliciesFileToStream(ownerUser), newTag)).isTrue();
+    }
+
+    /**
+     * In Android S DO protected packages were stored in Owners, verify that they are moved to
+     * ActiveAdmin.
+     */
+    @Test
+    public void testUserControlDisabledPackagesFromS() throws Exception {
+        final String oldTag = "device-owner-protected-packages";
+        final String newTag = "protected_packages";
+        final int ownerUser = 0;
+        mProvider.mUsers = new int[]{0};
+        getServices().addUser(ownerUser, FLAG_PRIMARY, USER_TYPE_FULL_SYSTEM);
+        writeVersionToXml(2);
+        preparePoliciesFile(ownerUser, "device_policies.xml");
+        prepareDeviceOwnerFile(ownerUser, "protected_packages_device_owner_2.xml");
+
+        // Validate the setup.
+        assertThat(isTagPresent(readDoToStream(), oldTag)).isTrue();
+        assertThat(isTagPresent(readPoliciesFileToStream(ownerUser), newTag)).isFalse();
+
+        mUpgrader.upgradePolicy(3);
+
+        assertThat(readVersionFromXml()).isAtLeast(3);
+        assertThat(isTagPresent(readDoToStream(), oldTag)).isFalse();
+        assertThat(isTagPresent(readPoliciesFileToStream(ownerUser), newTag)).isTrue();
+    }
+
     @Test
     public void isLatestVersionTested() {
         assertThat(DevicePolicyManagerService.DPMS_VERSION).isEqualTo(LATEST_TESTED_VERSION);
@@ -226,32 +281,27 @@
         return Integer.parseInt(versionString);
     }
 
-    private void preparePoliciesFile(int userId) throws IOException {
+    private void preparePoliciesFile(int userId, String assetFile) throws IOException {
         JournaledFile policiesFile = mProvider.makeDevicePoliciesJournaledFile(userId);
         DpmTestUtils.writeToFile(
                 policiesFile.chooseForWrite(),
-                DpmTestUtils.readAsset(mRealTestContext,
-                        "PolicyVersionUpgraderTest/device_policies.xml"));
+                DpmTestUtils.readAsset(mRealTestContext, "PolicyVersionUpgraderTest/" + assetFile));
         policiesFile.commit();
     }
 
-    private void prepareDeviceOwnerFile(int userId) throws IOException {
-        File parentDir = getServices().pathProvider.getDataSystemDirectory();
-        File doFilePath = (new File(parentDir, "device_owner_2.xml")).getAbsoluteFile();
-        android.util.Log.i("YYYYYY", "DO paath: " + doFilePath);
+    private void prepareDeviceOwnerFile(int userId, String assetFile) throws IOException {
+        File doFilePath = getDoFilePath();
         String doFileContent = DpmTestUtils.readAsset(mRealTestContext,
-                "PolicyVersionUpgraderTest/device_owner_2.xml")
+                        "PolicyVersionUpgraderTest/" + assetFile)
                 // Substitute the right DO userId, XML in resources has 0
                 .replace("userId=\"0\"", "userId=\"" + userId + "\"");
         DpmTestUtils.writeToFile(doFilePath, doFileContent);
     }
 
-    private void prepareProfileOwnerFile(int userId) throws IOException {
-        File parentDir = getServices().pathProvider.getUserSystemDirectory(userId);
-        DpmTestUtils.writeToFile(
-                (new File(parentDir, "profile_owner.xml")).getAbsoluteFile(),
-                DpmTestUtils.readAsset(mRealTestContext,
-                        "PolicyVersionUpgraderTest/profile_owner.xml"));
+    private File getDoFilePath() {
+        File parentDir = getServices().pathProvider.getDataSystemDirectory();
+        File doFilePath = (new File(parentDir, DEVICE_OWNER_XML)).getAbsoluteFile();
+        return doFilePath;
     }
 
     private String readPoliciesFile(int userId) throws IOException {
@@ -259,6 +309,10 @@
         return new String(Files.asByteSource(policiesFile).read(), Charset.defaultCharset());
     }
 
+    private InputStream readDoToStream() throws IOException {
+        return new FileInputStream(getDoFilePath());
+    }
+
     private InputStream readPoliciesFileToStream(int userId) throws IOException {
         File policiesFile = mProvider.makeDevicePoliciesJournaledFile(userId).chooseForRead();
         return new FileInputStream(policiesFile);
diff --git a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
index cb97c9b..e78f0c7 100644
--- a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
@@ -20,9 +20,11 @@
 import android.content.ContextWrapper
 import android.hardware.display.DisplayViewport
 import android.hardware.input.InputManagerInternal
+import android.os.IInputConstants
 import android.os.test.TestLooper
 import android.platform.test.annotations.Presubmit
 import android.view.Display
+import android.view.PointerIcon
 import androidx.test.InstrumentationRegistry
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
@@ -31,6 +33,7 @@
 import org.junit.Test
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.Mockito.`when`
 import org.mockito.Mockito.doAnswer
@@ -38,6 +41,7 @@
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.junit.MockitoJUnit
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.TimeUnit
@@ -219,4 +223,23 @@
             secondRequestLatch.await(100, TimeUnit.MILLISECONDS))
         verify(native, times(2)).setPointerDisplayId(anyInt())
     }
-}
\ No newline at end of file
+
+    @Test
+    fun onDisplayRemoved_resetAllAdditionalInputProperties() {
+        localService.setVirtualMousePointerDisplayId(10)
+        localService.setPointerIconVisible(false, 10)
+        verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL))
+        localService.setPointerAcceleration(5f, 10)
+        verify(native).setPointerAcceleration(eq(5f))
+
+        service.onDisplayRemoved(10)
+        verify(native).displayRemoved(eq(10))
+        verify(native).setPointerIconType(eq(PointerIcon.TYPE_NOT_SPECIFIED))
+        verify(native).setPointerAcceleration(
+            eq(IInputConstants.DEFAULT_POINTER_ACCELERATION.toFloat()))
+
+        localService.setVirtualMousePointerDisplayId(10)
+        verify(native).setPointerDisplayId(eq(10))
+        verifyNoMoreInteractions(native)
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index a227cd3..035249e 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -70,6 +70,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -516,6 +517,7 @@
         }
     }
 
+    @Ignore("Causing breakages so ignoring to resolve, b/231667368")
     @Test
     public void initRecoveryService_alwaysUpdatesCertsWhenTestRootCertIsUsed() throws Exception {
         int uid = Binder.getCallingUid();
@@ -539,6 +541,7 @@
                 testRootCertAlias)).isEqualTo(TestData.getInsecureCertPathForEndpoint2());
     }
 
+    @Ignore("Causing breakages so ignoring to resolve, b/231667368")
     @Test
     public void initRecoveryService_updatesCertsIndependentlyForDifferentRoots() throws Exception {
         int uid = Binder.getCallingUid();
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index 20486b3..8167b44 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -454,14 +454,14 @@
                         + "    <library \n"
                         + "        name=\"foo\"\n"
                         + "        file=\"" + mFooJar + "\"\n"
-                        + "        on-bootclasspath-before=\"Q\"\n"
+                        + "        on-bootclasspath-before=\"A\"\n"
                         + "        on-bootclasspath-since=\"W\"\n"
                         + "     />\n\n"
                         + " </permissions>";
         parseSharedLibraries(contents);
         assertFooIsOnlySharedLibrary();
         SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
-        assertThat(entry.onBootclasspathBefore).isEqualTo("Q");
+        assertThat(entry.onBootclasspathBefore).isEqualTo("A");
         assertThat(entry.onBootclasspathSince).isEqualTo("W");
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java
index e9515fa..ffc0dcd 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java
@@ -82,8 +82,6 @@
         ImportanceExtractor extractor = new ImportanceExtractor();
         extractor.setConfig(mConfig);
 
-        when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
-          NotificationManager.IMPORTANCE_MIN);
         NotificationChannel channel =
                 new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED);
 
@@ -101,8 +99,6 @@
         ImportanceExtractor extractor = new ImportanceExtractor();
         extractor.setConfig(mConfig);
 
-        when(mConfig.getImportance(anyString(), anyInt())).thenReturn(
-          NotificationManager.IMPORTANCE_MIN);
         NotificationChannel channel =
                 new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_HIGH);
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelLoggerFake.java
index f609306..6f7bace 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelLoggerFake.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelLoggerFake.java
@@ -28,6 +28,13 @@
         CallRecord(NotificationChannelEvent event) {
             this.event = event;
         }
+
+        @Override
+        public String toString() {
+            return "CallRecord{" +
+                    "event=" + event +
+                    '}';
+        }
     }
 
     private List<CallRecord> mCalls = new ArrayList<>();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index c0cd7a7..267a506 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -52,6 +52,7 @@
 import static android.app.PendingIntent.FLAG_MUTABLE;
 import static android.app.PendingIntent.FLAG_ONE_SHOT;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.PackageManager.FEATURE_TELECOM;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -187,6 +188,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.Pair;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
@@ -220,6 +222,7 @@
 import com.google.common.collect.ImmutableList;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -272,6 +275,7 @@
     private WindowManagerInternal mWindowManagerInternal;
     @Mock
     private PermissionHelper mPermissionHelper;
+    private NotificationChannelLoggerFake mLogger = new NotificationChannelLoggerFake();
     private TestableContext mContext = spy(getContext());
     private final String PKG = mContext.getPackageName();
     private TestableLooper mTestableLooper;
@@ -384,9 +388,6 @@
                 "android.permission.WRITE_DEVICE_CONFIG",
                 "android.permission.READ_DEVICE_CONFIG",
                 "android.permission.READ_CONTACTS");
-        Settings.Secure.putIntForUser(
-                getContext().getContentResolver(),
-                Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 0, USER_SYSTEM);
 
         MockitoAnnotations.initMocks(this);
 
@@ -448,6 +449,8 @@
         mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
         when(mUm.getProfileIds(0, false)).thenReturn(new int[]{0});
 
+        when(mPackageManagerClient.hasSystemFeature(FEATURE_TELECOM)).thenReturn(true);
+
         ActivityManager.AppTask task = mock(ActivityManager.AppTask.class);
         List<ActivityManager.AppTask> taskList = new ArrayList<>();
         ActivityManager.RecentTaskInfo taskInfo = new ActivityManager.RecentTaskInfo();
@@ -506,7 +509,7 @@
                 mAppOpsManager, mAppOpsService, mUm, mHistoryManager, mStatsManager,
                 mock(TelephonyManager.class),
                 mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class),
-                mTelecomManager);
+                mTelecomManager, mLogger);
         // Return first true for RoleObserver main-thread check
         when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
         mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
@@ -582,6 +585,7 @@
         assertNotNull(mBinderService.getNotificationChannel(
                 PKG, mContext.getUserId(), PKG, TEST_CHANNEL_ID));
         clearInvocations(mRankingHandler);
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
     }
 
     @After
@@ -1234,8 +1238,7 @@
     @Test
     public void testEnqueuedBlockedNotifications_blockedApp() throws Exception {
         when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
-
-        mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
 
         final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
@@ -1248,8 +1251,7 @@
     @Test
     public void testEnqueuedBlockedNotifications_blockedAppForegroundService() throws Exception {
         when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
-
-        mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
 
         final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
@@ -1342,6 +1344,30 @@
     }
 
     @Test
+    public void testSetNotificationsEnabledForPackage_noChange() throws Exception {
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
+        mBinderService.setNotificationsEnabledForPackage(mContext.getPackageName(), mUid, true);
+
+        verify(mPermissionHelper, never()).setNotificationPermission(
+                anyString(), anyInt(), anyBoolean(), anyBoolean());
+    }
+
+    @Test
+    public void testSetNotificationsEnabledForPackage() throws Exception {
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
+        mBinderService.setNotificationsEnabledForPackage(mContext.getPackageName(), mUid, false);
+
+        verify(mPermissionHelper).setNotificationPermission(
+                mContext.getPackageName(), UserHandle.getUserId(mUid), false, true);
+
+        verify(mAppOpsManager, never()).setMode(anyInt(), anyInt(), anyString(), anyInt());
+        List<NotificationChannelLoggerFake.CallRecord> calls = mLogger.getCalls();
+        Assert.assertEquals(
+                NotificationChannelLogger.NotificationChannelEvent.APP_NOTIFICATIONS_BLOCKED,
+                calls.get(calls.size() -1).event);
+    }
+
+    @Test
     public void testBlockedNotifications_blockedByAssistant() throws Exception {
         when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
         when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
@@ -1368,7 +1394,6 @@
 
     @Test
     public void testBlockedNotifications_blockedByUser() throws Exception {
-        mService.setPreferencesHelper(mPreferencesHelper);
         when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
         when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
 
@@ -1377,7 +1402,7 @@
         NotificationRecord r = generateNotificationRecord(channel);
         mService.addEnqueuedNotification(r);
 
-        when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(IMPORTANCE_NONE);
+        when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false);
 
         NotificationManagerService.PostNotificationRunnable runnable =
                 mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
@@ -1390,8 +1415,32 @@
     }
 
     @Test
+    public void testEnqueueNotificationInternal_noChannel() throws Exception {
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+        NotificationRecord nr = generateNotificationRecord(
+                new NotificationChannel("did not create", "", IMPORTANCE_DEFAULT));
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+        waitForIdle();
+
+        verify(mPermissionHelper).hasPermission(mUid);
+        verify(mPermissionHelper, never()).hasPermission(Process.SYSTEM_UID);
+
+        reset(mPermissionHelper);
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+        waitForIdle();
+
+        verify(mPermissionHelper).hasPermission(mUid);
+        assertThat(mService.mChannelToastsSent).contains(mUid);
+    }
+
+    @Test
     public void testEnqueueNotification_appBlocked() throws Exception {
-        mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
 
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testEnqueueNotification_appBlocked", 0,
@@ -2686,6 +2735,49 @@
     }
 
     @Test
+    public void testDefaultChannelUpdatesApp_postMigrationToPermissions() throws Exception {
+        final NotificationChannel defaultChannel = mBinderService.getNotificationChannel(
+                PKG_N_MR1, ActivityManager.getCurrentUser(), PKG_N_MR1,
+                NotificationChannel.DEFAULT_CHANNEL_ID);
+        defaultChannel.setImportance(IMPORTANCE_NONE);
+
+        mBinderService.updateNotificationChannelForPackage(PKG_N_MR1, mUid, defaultChannel);
+
+        verify(mPermissionHelper).setNotificationPermission(
+                PKG_N_MR1, ActivityManager.getCurrentUser(), false, true);
+    }
+
+    @Test
+    public void testPostNotification_appPermissionFixed() throws Exception {
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
+        when(mPermissionHelper.isPermissionFixed(PKG, 0)).thenReturn(true);
+
+        NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel);
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testPostNotification_appPermissionFixed", 0,
+                temp.getNotification(), 0);
+        waitForIdle();
+        assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(PKG);
+        assertThat(mService.getNotificationRecord(notifs[0].getKey()).isImportanceFixed()).isTrue();
+    }
+
+    @Test
+    public void testSummaryNotification_appPermissionFixed() {
+        NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(temp);
+
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
+        when(mPermissionHelper.isPermissionFixed(PKG, temp.getUserId())).thenReturn(true);
+
+        NotificationRecord r = mService.createAutoGroupSummary(
+                temp.getUserId(), temp.getSbn().getPackageName(), temp.getKey(), false);
+
+        assertThat(r.isImportanceFixed()).isTrue();
+    }
+
+    @Test
     public void testTvExtenderChannelOverride_onTv() throws Exception {
         mService.setIsTelevision(true);
         mService.setPreferencesHelper(mPreferencesHelper);
@@ -2718,13 +2810,12 @@
 
     @Test
     public void testUpdateAppNotifyCreatorBlock() throws Exception {
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.getImportance(PKG, mUid)).thenReturn(IMPORTANCE_DEFAULT);
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
 
-        // should trigger a broadcast
         mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
         Thread.sleep(500);
         waitForIdle();
+
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
         verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
 
@@ -2736,7 +2827,7 @@
 
     @Test
     public void testUpdateAppNotifyCreatorBlock_notIfMatchesExistingSetting() throws Exception {
-        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
 
         mBinderService.setNotificationsEnabledForPackage(PKG, 0, false);
         verify(mContext, never()).sendBroadcastAsUser(any(), any(), eq(null));
@@ -2744,15 +2835,12 @@
 
     @Test
     public void testUpdateAppNotifyCreatorUnblock() throws Exception {
-        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
 
-        // should not trigger a broadcast
-        when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_ALLOWED);
-
-        // should trigger a broadcast
-        mBinderService.setNotificationsEnabledForPackage(PKG, 0, true);
+        mBinderService.setNotificationsEnabledForPackage(PKG, mUid, true);
         Thread.sleep(500);
         waitForIdle();
+
         ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
         verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
 
@@ -4344,7 +4432,7 @@
 
         assertEquals(IMPORTANCE_LOW,
                 mService.getNotificationRecord(sbn.getKey()).getImportance());
-        assertEquals(IMPORTANCE_UNSPECIFIED, mBinderService.getPackageImportance(
+        assertEquals(IMPORTANCE_DEFAULT, mBinderService.getPackageImportance(
                 sbn.getPackageName()));
 
         nb = new Notification.Builder(mContext)
@@ -4860,6 +4948,7 @@
 
     @Test
     public void testBackup() throws Exception {
+        mService.setPreferencesHelper(mPreferencesHelper);
         int systemChecks = mService.countSystemChecks;
         when(mListeners.queryPackageForServices(anyString(), anyInt(), anyInt()))
                 .thenReturn(new ArraySet<>());
@@ -5963,8 +6052,7 @@
                 .thenReturn(false);
 
         // notifications from this package are blocked by the user
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
 
         setAppInForegroundForToasts(mUid, true);
 
@@ -6260,8 +6348,7 @@
                 .thenReturn(false);
 
         // notifications from this package are blocked by the user
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
 
         setAppInForegroundForToasts(mUid, false);
 
@@ -6347,8 +6434,7 @@
                 .thenReturn(true);
 
         // notifications from this package are NOT blocked by the user
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_LOW);
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
 
         // enqueue toast -> no toasts enqueued
         ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(),
@@ -6369,8 +6455,7 @@
                 .thenReturn(false);
 
         // notifications from this package are blocked by the user
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
 
         setAppInForegroundForToasts(mUid, false);
 
@@ -6393,8 +6478,7 @@
                 .thenReturn(true);
 
         // notifications from this package ARE blocked by the user
-        mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
 
         setAppInForegroundForToasts(mUid, false);
 
@@ -7303,6 +7387,14 @@
     }
 
     @Test
+    public void testAreNotificationsEnabledForPackage() throws Exception {
+        mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
+                mUid);
+
+        verify(mPermissionHelper).hasPermission(mUid);
+    }
+
+    @Test
     public void testAreNotificationsEnabledForPackage_crossUser() throws Exception {
         try {
             mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
@@ -7311,21 +7403,31 @@
         } catch (SecurityException e) {
             // pass
         }
+        verify(mPermissionHelper, never()).hasPermission(anyInt());
 
         // cross user, with permission, no problem
         enableInteractAcrossUsers();
         mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
                 mUid + UserHandle.PER_USER_RANGE);
 
-        verify(mPermissionHelper, never()).hasPermission(anyInt());
+        verify(mPermissionHelper).hasPermission(mUid + UserHandle.PER_USER_RANGE);
     }
 
     @Test
-    public void testAreNotificationsEnabledForPackage_viaInternalService() throws Exception {
-        assertEquals(mInternalService.areNotificationsEnabledForPackage(
-                mContext.getPackageName(), mUid),
-                mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(), mUid));
-        verify(mPermissionHelper, never()).hasPermission(anyInt());
+    public void testAreNotificationsEnabledForPackage_viaInternalService() {
+        mInternalService.areNotificationsEnabledForPackage(mContext.getPackageName(), mUid);
+        verify(mPermissionHelper).hasPermission(mUid);
+    }
+
+    @Test
+    public void testGetPackageImportance() throws Exception {
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
+        assertThat(mBinderService.getPackageImportance(mContext.getPackageName()))
+                .isEqualTo(IMPORTANCE_DEFAULT);
+
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+        assertThat(mBinderService.getPackageImportance(mContext.getPackageName()))
+                .isEqualTo(IMPORTANCE_NONE);
     }
 
     @Test
@@ -8975,48 +9077,13 @@
     }
 
     @Test
-    public void testMigrationDisabledByDefault() {
-        assertThat(mService.mEnableAppSettingMigration).isFalse();
-    }
-
-    @Test
-    public void testPostNotification_channelLockedFixed() throws Exception {
-        mTestNotificationChannel.setImportanceLockedByOEM(true);
-
-        NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG,
-                "testPostNotification_appPermissionFixed", 0,
-                temp.getNotification(), 0);
-        waitForIdle();
-        assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
-        StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(PKG);
-        assertThat(mService.getNotificationRecord(notifs[0].getKey()).isImportanceFixed()).isTrue();
-
-        mBinderService.cancelAllNotifications(PKG, 0);
-        waitForIdle();
-
-        mTestNotificationChannel.setImportanceLockedByOEM(false);
-        mTestNotificationChannel.setImportanceLockedByCriticalDeviceFunction(true);
-
-        temp = generateNotificationRecord(mTestNotificationChannel);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG,
-                "testPostNotification_appPermissionFixed", 0,
-                temp.getNotification(), 0);
-        waitForIdle();
-        assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
-        notifs = mBinderService.getActiveNotifications(PKG);
-        assertThat(mService.getNotificationRecord(notifs[0].getKey()).isImportanceFixed()).isTrue();
-    }
-
-    @Test
     public void testGetNotificationChannelsBypassingDnd_blocked() throws RemoteException {
         mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.getImportance(PKG, mUid)).thenReturn(IMPORTANCE_NONE);
+
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
 
         assertThat(mBinderService.getNotificationChannelsBypassingDnd(PKG, mUid).getList())
                 .isEmpty();
-        verify(mPermissionHelper, never()).hasPermission(anyInt());
         verify(mPreferencesHelper, never()).getNotificationChannelsBypassingDnd(PKG, mUid);
     }
 
@@ -9110,8 +9177,7 @@
                 nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
         NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.setNotificationsEnabledForPackage(
-                r.getSbn().getPackageName(), r.getUid(), false);
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
 
         // normal blocked notifications - blocked
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
@@ -9149,6 +9215,67 @@
     }
 
     @Test
+    public void testMediaNotificationsBypassBlock_atPost() throws Exception {
+        when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+        Notification.Builder nb = new Notification.Builder(
+                mContext, mTestNotificationChannel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .addAction(new Notification.Action.Builder(null, "test", null).build());
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false);
+
+        mService.addEnqueuedNotification(r);
+        NotificationManagerService.PostNotificationRunnable runnable =
+                mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                        r.getUid(), SystemClock.elapsedRealtime());
+        runnable.run();
+        waitForIdle();
+
+        verify(mUsageStats).registerBlocked(any());
+        verify(mUsageStats, never()).registerPostedByApp(any());
+
+        // just using the style - blocked
+        mService.clearNotifications();
+        reset(mUsageStats);
+        nb.setStyle(new Notification.MediaStyle());
+        sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mService.addEnqueuedNotification(r);
+        runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                r.getUid(), SystemClock.elapsedRealtime());
+        runnable.run();
+        waitForIdle();
+
+        verify(mUsageStats).registerBlocked(any());
+        verify(mUsageStats, never()).registerPostedByApp(any());
+
+        // style + media session - bypasses block
+        mService.clearNotifications();
+        reset(mUsageStats);
+        nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
+        sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mService.addEnqueuedNotification(r);
+        runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                r.getUid(), SystemClock.elapsedRealtime());
+        runnable.run();
+        waitForIdle();
+
+        verify(mUsageStats, never()).registerBlocked(any());
+        verify(mUsageStats).registerPostedByApp(any());
+    }
+
+    @Test
     public void testCallNotificationsBypassBlock() throws Exception {
         when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
                 .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
@@ -9163,8 +9290,7 @@
                 nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
         NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.setNotificationsEnabledForPackage(
-                r.getSbn().getPackageName(), r.getUid(), false);
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
 
         // normal blocked notifications - blocked
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
@@ -9194,12 +9320,152 @@
                 r.getSbn().getPackageName(), r.getUser())).thenReturn(true);
         assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
                 r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
+
+        // set telecom manager to null - blocked
+        mService.setTelecomManager(null);
+        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+                           r.getSbn().getId(), r.getSbn().getTag(), r, false))
+                .isFalse();
+
+        // set telecom feature to false - blocked
+        when(mPackageManagerClient.hasSystemFeature(FEATURE_TELECOM)).thenReturn(false);
+        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
+                           r.getSbn().getId(), r.getSbn().getTag(), r, false))
+                .isFalse();
     }
 
     @Test
-    public void testGetAllUsersNotificationPermissions_migrationNotEnabled() {
-        // make sure we don't bother if the migration is not enabled
-        assertThat(mService.getAllUsersNotificationPermissions()).isNull();
+    public void testCallNotificationsBypassBlock_atPost() throws Exception {
+        when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
+        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
+
+        Notification.Builder nb =
+                new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                        .setContentTitle("foo")
+                        .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                        .addAction(new Notification.Action.Builder(null, "test", null).build());
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mBinderService.setNotificationsEnabledForPackage(
+                r.getSbn().getPackageName(), r.getUid(), false);
+
+        // normal blocked notifications - blocked
+        mService.addEnqueuedNotification(r);
+        NotificationManagerService.PostNotificationRunnable runnable =
+                mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                        r.getUid(), SystemClock.elapsedRealtime());
+        runnable.run();
+        waitForIdle();
+
+        verify(mUsageStats).registerBlocked(any());
+        verify(mUsageStats, never()).registerPostedByApp(any());
+
+        // just using the style - blocked
+        mService.clearNotifications();
+        reset(mUsageStats);
+        Person person = new Person.Builder().setName("caller").build();
+        nb.setStyle(Notification.CallStyle.forOngoingCall(person, mock(PendingIntent.class)));
+        nb.setFullScreenIntent(mock(PendingIntent.class), true);
+        sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, nb.build(),
+                UserHandle.getUserHandleForUid(mUid), null, 0);
+        r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mService.addEnqueuedNotification(r);
+        runnable = mService.new PostNotificationRunnable(
+                r.getKey(), r.getSbn().getPackageName(), r.getUid(), SystemClock.elapsedRealtime());
+        runnable.run();
+        waitForIdle();
+
+        verify(mUsageStats).registerBlocked(any());
+        verify(mUsageStats, never()).registerPostedByApp(any());
+
+        // style + managed call - bypasses block
+        mService.clearNotifications();
+        reset(mUsageStats);
+        when(mTelecomManager.isInManagedCall()).thenReturn(true);
+
+        mService.addEnqueuedNotification(r);
+        runnable.run();
+        waitForIdle();
+
+        verify(mUsageStats, never()).registerBlocked(any());
+        verify(mUsageStats).registerPostedByApp(any());
+
+        // style + self managed call - bypasses block
+        mService.clearNotifications();
+        reset(mUsageStats);
+        when(mTelecomManager.isInSelfManagedCall(r.getSbn().getPackageName(), r.getUser()))
+                .thenReturn(true);
+
+        mService.addEnqueuedNotification(r);
+        runnable.run();
+        waitForIdle();
+
+        verify(mUsageStats, never()).registerBlocked(any());
+        verify(mUsageStats).registerPostedByApp(any());
+
+        // set telecom manager to null - notifications should be blocked
+        // but post notifications runnable should not crash
+        mService.clearNotifications();
+        reset(mUsageStats);
+        mService.setTelecomManager(null);
+
+        mService.addEnqueuedNotification(r);
+        runnable.run();
+        waitForIdle();
+
+        verify(mUsageStats).registerBlocked(any());
+        verify(mUsageStats, never()).registerPostedByApp(any());
+
+        // set FEATURE_TELECOM to false - notifications should be blocked
+        // but post notifications runnable should not crash
+        mService.setTelecomManager(mTelecomManager);
+        when(mPackageManagerClient.hasSystemFeature(FEATURE_TELECOM)).thenReturn(false);
+        reset(mUsageStats);
+        mService.setTelecomManager(null);
+
+        mService.addEnqueuedNotification(r);
+        runnable.run();
+        waitForIdle();
+
+        verify(mUsageStats).registerBlocked(any());
+        verify(mUsageStats, never()).registerPostedByApp(any());
+    }
+
+    @Test
+    public void testGetAllUsersNotificationPermissions() {
+        // In this case, there are multiple users each with notification permissions (and also,
+        // for good measure, some without).
+        // make sure the collection returned contains info for all of them
+        final List<UserInfo> userInfos = new ArrayList<>();
+        userInfos.add(new UserInfo(0, "user0", 0));
+        userInfos.add(new UserInfo(1, "user1", 0));
+        userInfos.add(new UserInfo(2, "user2", 0));
+        when(mUm.getUsers()).thenReturn(userInfos);
+
+        // construct the permissions for each of them
+        ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> permissions0 = new ArrayMap<>(),
+                permissions1 = new ArrayMap<>();
+        permissions0.put(new Pair<>(10, "package1"), new Pair<>(true, false));
+        permissions0.put(new Pair<>(20, "package2"), new Pair<>(false, true));
+        permissions1.put(new Pair<>(11, "package1"), new Pair<>(false, false));
+        permissions1.put(new Pair<>(21, "package2"), new Pair<>(true, true));
+        when(mPermissionHelper.getNotificationPermissionValues(0)).thenReturn(permissions0);
+        when(mPermissionHelper.getNotificationPermissionValues(1)).thenReturn(permissions1);
+        when(mPermissionHelper.getNotificationPermissionValues(2)).thenReturn(new ArrayMap<>());
+
+        ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> combinedPermissions =
+                mService.getAllUsersNotificationPermissions();
+        assertTrue(combinedPermissions.get(new Pair<>(10, "package1")).first);
+        assertFalse(combinedPermissions.get(new Pair<>(10, "package1")).second);
+        assertFalse(combinedPermissions.get(new Pair<>(20, "package2")).first);
+        assertTrue(combinedPermissions.get(new Pair<>(20, "package2")).second);
+        assertFalse(combinedPermissions.get(new Pair<>(11, "package1")).first);
+        assertFalse(combinedPermissions.get(new Pair<>(11, "package1")).second);
+        assertTrue(combinedPermissions.get(new Pair<>(21, "package2")).first);
+        assertTrue(combinedPermissions.get(new Pair<>(21, "package2")).second);
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
deleted file mode 100755
index b751c7f..0000000
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ /dev/null
@@ -1,877 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.notification;
-
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_IGNORED;
-import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-import static android.app.NotificationManager.IMPORTANCE_NONE;
-import static android.app.PendingIntent.FLAG_MUTABLE;
-import static android.app.PendingIntent.FLAG_ONE_SHOT;
-import static android.content.pm.PackageManager.FEATURE_WATCH;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.UserHandle.USER_SYSTEM;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyLong;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
-import android.app.AlarmManager;
-import android.app.AppOpsManager;
-import android.app.IActivityManager;
-import android.app.INotificationManager;
-import android.app.IUriGrantsManager;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.StatsManager;
-import android.app.admin.DevicePolicyManagerInternal;
-import android.app.usage.UsageStatsManagerInternal;
-import android.companion.ICompanionDeviceManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.ParceledListSlice;
-import android.content.pm.ShortcutInfo;
-import android.content.pm.ShortcutServiceInternal;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.media.AudioManager;
-import android.media.session.MediaSession;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.service.notification.NotificationListenerFilter;
-import android.service.notification.StatusBarNotification;
-import android.telecom.TelecomManager;
-import android.telephony.TelephonyManager;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableContext;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-import android.testing.TestablePermissions;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.AtomicFile;
-import android.util.Pair;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.internal.app.IAppOpsService;
-import com.android.internal.logging.InstanceIdSequence;
-import com.android.internal.logging.InstanceIdSequenceFake;
-import com.android.server.DeviceIdleInternal;
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-import com.android.server.UiServiceTestCase;
-import com.android.server.lights.LightsManager;
-import com.android.server.lights.LogicalLight;
-import com.android.server.notification.NotificationManagerService.NotificationAssistants;
-import com.android.server.notification.NotificationManagerService.NotificationListeners;
-import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.server.utils.quota.MultiRateLimiter;
-import com.android.server.wm.ActivityTaskManagerInternal;
-import com.android.server.wm.WindowManagerInternal;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-/**
- * Tests that NMS reads/writes the app notification state from Package/PermissionManager when
- * migration is enabled. Because the migration field is read onStart
- * TODO (b/194833441): migrate these tests to NotificationManagerServiceTest when the migration is
- * permanently enabled.
- */
-public class NotificationPermissionMigrationTest extends UiServiceTestCase {
-    private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
-    private static final int UID_HEADLESS = 1000000;
-
-    private final int mUid = Binder.getCallingUid();
-    private TestableNotificationManagerService mService;
-    private INotificationManager mBinderService;
-    private NotificationManagerInternal mInternalService;
-    private ShortcutHelper mShortcutHelper;
-    @Mock
-    private IPackageManager mPackageManager;
-    @Mock
-    private PackageManager mPackageManagerClient;
-    @Mock
-    private PackageManagerInternal mPackageManagerInternal;
-    @Mock
-    private WindowManagerInternal mWindowManagerInternal;
-    @Mock
-    private PermissionHelper mPermissionHelper;
-    private TestableContext mContext = spy(getContext());
-    private final String PKG = mContext.getPackageName();
-    private TestableLooper mTestableLooper;
-    @Mock
-    private RankingHelper mRankingHelper;
-    @Mock private PreferencesHelper mPreferencesHelper;
-    AtomicFile mPolicyFile;
-    File mFile;
-    @Mock
-    private NotificationUsageStats mUsageStats;
-    @Mock
-    private UsageStatsManagerInternal mAppUsageStats;
-    @Mock
-    private AudioManager mAudioManager;
-    @Mock
-    private LauncherApps mLauncherApps;
-    @Mock
-    private ShortcutServiceInternal mShortcutServiceInternal;
-    @Mock
-    private UserManager mUserManager;
-    @Mock
-    ActivityManager mActivityManager;
-    @Mock
-    Resources mResources;
-    @Mock
-    RankingHandler mRankingHandler;
-    @Mock
-    ActivityManagerInternal mAmi;
-    @Mock
-    private Looper mMainLooper;
-
-    @Mock
-    IIntentSender pi1;
-
-    private static final int MAX_POST_DELAY = 1000;
-
-    private NotificationChannel mTestNotificationChannel = new NotificationChannel(
-            TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
-
-    private static final String VALID_CONVO_SHORTCUT_ID = "shortcut";
-
-    @Mock
-    private NotificationListeners mListeners;
-    @Mock
-    private NotificationListenerFilter mNlf;
-    @Mock private NotificationAssistants mAssistants;
-    @Mock private ConditionProviders mConditionProviders;
-    private ManagedServices.ManagedServiceInfo mListener;
-    @Mock private ICompanionDeviceManager mCompanionMgr;
-    @Mock SnoozeHelper mSnoozeHelper;
-    @Mock GroupHelper mGroupHelper;
-    @Mock
-    IBinder mPermOwner;
-    @Mock
-    IActivityManager mAm;
-    @Mock
-    ActivityTaskManagerInternal mAtm;
-    @Mock
-    IUriGrantsManager mUgm;
-    @Mock
-    UriGrantsManagerInternal mUgmInternal;
-    @Mock
-    AppOpsManager mAppOpsManager;
-    @Mock
-    private TestableNotificationManagerService.NotificationAssistantAccessGrantedCallback
-            mNotificationAssistantAccessGrantedCallback;
-    @Mock
-    UserManager mUm;
-    @Mock
-    NotificationHistoryManager mHistoryManager;
-    @Mock
-    StatsManager mStatsManager;
-    @Mock
-    AlarmManager mAlarmManager;
-    @Mock
-    MultiRateLimiter mToastRateLimiter;
-    BroadcastReceiver mPackageIntentReceiver;
-    NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
-    private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
-            1 << 30);
-    @Mock
-    StatusBarManagerInternal mStatusBar;
-
-    private NotificationManagerService.WorkerHandler mWorkerHandler;
-
-    @Before
-    public void setUp() throws Exception {
-        // These should be the only difference in setup from NMSTest
-        Settings.Secure.putIntForUser(
-                getContext().getContentResolver(),
-                Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 1, USER_SYSTEM);
-        Settings.Global.putInt(getContext().getContentResolver(),
-                Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, 1);
-
-        // Shell permissions will override permissions of our app, so add all necessary permissions
-        // for this test here:
-        InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
-                "android.permission.WRITE_DEVICE_CONFIG",
-                "android.permission.READ_DEVICE_CONFIG",
-                "android.permission.READ_CONTACTS");
-
-        MockitoAnnotations.initMocks(this);
-
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
-
-        DeviceIdleInternal deviceIdleInternal = mock(DeviceIdleInternal.class);
-        when(deviceIdleInternal.getNotificationAllowlistDuration()).thenReturn(3000L);
-
-        LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
-        LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
-        LocalServices.removeServiceForTest(WindowManagerInternal.class);
-        LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
-        LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
-        LocalServices.addService(StatusBarManagerInternal.class, mStatusBar);
-        LocalServices.removeServiceForTest(DeviceIdleInternal.class);
-        LocalServices.addService(DeviceIdleInternal.class, deviceIdleInternal);
-        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
-        LocalServices.addService(ActivityManagerInternal.class, mAmi);
-        LocalServices.removeServiceForTest(PackageManagerInternal.class);
-        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
-        mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
-        when(mUm.getProfileIds(0, false)).thenReturn(new int[]{0});
-
-        doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
-
-        mService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger,
-                mNotificationInstanceIdSequence);
-
-        // Use this testable looper.
-        mTestableLooper = TestableLooper.get(this);
-        // MockPackageManager - default returns ApplicationInfo with matching calling UID
-        mContext.setMockPackageManager(mPackageManagerClient);
-
-        when(mPackageManager.getApplicationInfo(anyString(), anyLong(), anyInt()))
-                .thenAnswer((Answer<ApplicationInfo>) invocation -> {
-                    Object[] args = invocation.getArguments();
-                    return getApplicationInfo((String) args[0], mUid);
-                });
-        when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
-                .thenAnswer((Answer<ApplicationInfo>) invocation -> {
-                    Object[] args = invocation.getArguments();
-                    return getApplicationInfo((String) args[0], mUid);
-                });
-        when(mPackageManagerClient.getPackageUidAsUser(any(), anyInt())).thenReturn(mUid);
-        when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenAnswer(
-                (Answer<Boolean>) invocation -> {
-                    Object[] args = invocation.getArguments();
-                    return (int) args[1] == mUid;
-                });
-        final LightsManager mockLightsManager = mock(LightsManager.class);
-        when(mockLightsManager.getLight(anyInt())).thenReturn(mock(LogicalLight.class));
-        when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
-        when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
-        when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner);
-        when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG});
-        when(mPackageManagerClient.getPackagesForUid(anyInt())).thenReturn(new String[]{PKG});
-        mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
-
-        // write to a test file; the system file isn't readable from tests
-        mFile = new File(mContext.getCacheDir(), "test.xml");
-        mFile.createNewFile();
-        final String preupgradeXml = "<notification-policy></notification-policy>";
-        mPolicyFile = new AtomicFile(mFile);
-        FileOutputStream fos = mPolicyFile.startWrite();
-        fos.write(preupgradeXml.getBytes());
-        mPolicyFile.finishWrite(fos);
-
-        // Setup managed services
-        when(mNlf.isTypeAllowed(anyInt())).thenReturn(true);
-        when(mNlf.isPackageAllowed(any())).thenReturn(true);
-        when(mNlf.isPackageAllowed(null)).thenReturn(true);
-        when(mListeners.getNotificationListenerFilter(any())).thenReturn(mNlf);
-        mListener = mListeners.new ManagedServiceInfo(
-                null, new ComponentName(PKG, "test_class"),
-                UserHandle.getUserId(mUid), true, null, 0, 123);
-        ComponentName defaultComponent = ComponentName.unflattenFromString("config/device");
-        ArraySet<ComponentName> components = new ArraySet<>();
-        components.add(defaultComponent);
-        when(mListeners.getDefaultComponents()).thenReturn(components);
-        when(mConditionProviders.getDefaultPackages())
-                .thenReturn(new ArraySet<>(Arrays.asList("config")));
-        when(mAssistants.getDefaultComponents()).thenReturn(components);
-        when(mAssistants.queryPackageForServices(
-                anyString(), anyInt(), anyInt())).thenReturn(components);
-        when(mListeners.checkServiceTokenLocked(null)).thenReturn(mListener);
-        ManagedServices.Config listenerConfig = new ManagedServices.Config();
-        listenerConfig.xmlTag = NotificationListeners.TAG_ENABLED_NOTIFICATION_LISTENERS;
-        when(mListeners.getConfig()).thenReturn(listenerConfig);
-        ManagedServices.Config assistantConfig = new ManagedServices.Config();
-        assistantConfig.xmlTag = NotificationAssistants.TAG_ENABLED_NOTIFICATION_ASSISTANTS;
-        when(mAssistants.getConfig()).thenReturn(assistantConfig);
-        ManagedServices.Config dndConfig = new ManagedServices.Config();
-        dndConfig.xmlTag = ConditionProviders.TAG_ENABLED_DND_APPS;
-        when(mConditionProviders.getConfig()).thenReturn(dndConfig);
-
-        when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true);
-
-        // apps allowed as convos
-        mService.setStringArrayResourceValue(PKG_O);
-
-        mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper()));
-        mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient,
-                mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
-                mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, mGroupHelper, mAm, mAtm,
-                mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
-                mAppOpsManager, mock(IAppOpsService.class), mUm, mHistoryManager, mStatsManager,
-                mock(TelephonyManager.class), mAmi, mToastRateLimiter, mPermissionHelper,
-                mock(UsageStatsManagerInternal.class), mock(TelecomManager.class));
-        // Return first true for RoleObserver main-thread check
-        when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
-        mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
-
-        mService.setAudioManager(mAudioManager);
-
-        mShortcutHelper = mService.getShortcutHelper();
-        mShortcutHelper.setLauncherApps(mLauncherApps);
-        mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal);
-        mShortcutHelper.setUserManager(mUserManager);
-
-        // Capture PackageIntentReceiver
-        ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
-                ArgumentCaptor.forClass(BroadcastReceiver.class);
-        ArgumentCaptor<IntentFilter> intentFilterCaptor =
-                ArgumentCaptor.forClass(IntentFilter.class);
-
-        verify(mContext, atLeastOnce()).registerReceiverAsUser(broadcastReceiverCaptor.capture(),
-                any(), intentFilterCaptor.capture(), any(), any());
-        verify(mContext, atLeastOnce()).registerReceiver(broadcastReceiverCaptor.capture(),
-                intentFilterCaptor.capture());
-        List<BroadcastReceiver> broadcastReceivers = broadcastReceiverCaptor.getAllValues();
-        List<IntentFilter> intentFilters = intentFilterCaptor.getAllValues();
-
-        for (int i = 0; i < intentFilters.size(); i++) {
-            final IntentFilter filter = intentFilters.get(i);
-            if (filter.hasAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED)
-                    && filter.hasAction(Intent.ACTION_PACKAGES_UNSUSPENDED)
-                    && filter.hasAction(Intent.ACTION_PACKAGES_SUSPENDED)) {
-                mPackageIntentReceiver = broadcastReceivers.get(i);
-            }
-        }
-        assertNotNull("package intent receiver should exist", mPackageIntentReceiver);
-
-        // Pretend the shortcut exists
-        List<ShortcutInfo> shortcutInfos = new ArrayList<>();
-        ShortcutInfo info = mock(ShortcutInfo.class);
-        when(info.getPackage()).thenReturn(PKG);
-        when(info.getId()).thenReturn(VALID_CONVO_SHORTCUT_ID);
-        when(info.getUserId()).thenReturn(USER_SYSTEM);
-        when(info.isLongLived()).thenReturn(true);
-        when(info.isEnabled()).thenReturn(true);
-        shortcutInfos.add(info);
-        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos);
-        when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
-                anyString(), anyInt(), any())).thenReturn(true);
-        when(mUserManager.isUserUnlocked(any(UserHandle.class))).thenReturn(true);
-
-        // Set the testable bubble extractor
-        RankingHelper rankingHelper = mService.getRankingHelper();
-        BubbleExtractor extractor = rankingHelper.findExtractor(BubbleExtractor.class);
-        extractor.setActivityManager(mActivityManager);
-
-        // Tests call directly into the Binder.
-        mBinderService = mService.getBinderService();
-        mInternalService = mService.getInternalService();
-
-        mBinderService.createNotificationChannels(
-                PKG, new ParceledListSlice(Arrays.asList(mTestNotificationChannel)));
-        mBinderService.createNotificationChannels(
-                PKG_P, new ParceledListSlice(Arrays.asList(mTestNotificationChannel)));
-        mBinderService.createNotificationChannels(
-                PKG_O, new ParceledListSlice(Arrays.asList(mTestNotificationChannel)));
-        assertNotNull(mBinderService.getNotificationChannel(
-                PKG, mContext.getUserId(), PKG, TEST_CHANNEL_ID));
-        clearInvocations(mRankingHandler);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        if (mFile != null) mFile.delete();
-
-        try {
-            mService.onDestroy();
-        } catch (IllegalStateException | IllegalArgumentException e) {
-            // can throw if a broadcast receiver was never registered
-        }
-
-        InstrumentationRegistry.getInstrumentation()
-                .getUiAutomation().dropShellPermissionIdentity();
-        // Remove scheduled messages that would be processed when the test is already done, and
-        // could cause issues, for example, messages that remove/cancel shown toasts (this causes
-        // problematic interactions with mocks when they're no longer working as expected).
-        mWorkerHandler.removeCallbacksAndMessages(null);
-    }
-
-    private ApplicationInfo getApplicationInfo(String pkg, int uid) {
-        final ApplicationInfo applicationInfo = new ApplicationInfo();
-        applicationInfo.uid = uid;
-        switch (pkg) {
-            case PKG_N_MR1:
-                applicationInfo.targetSdkVersion = Build.VERSION_CODES.N_MR1;
-                break;
-            case PKG_O:
-                applicationInfo.targetSdkVersion = Build.VERSION_CODES.O;
-                break;
-            case PKG_P:
-                applicationInfo.targetSdkVersion = Build.VERSION_CODES.P;
-                break;
-            default:
-                applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
-                break;
-        }
-        return applicationInfo;
-    }
-
-    public void waitForIdle() {
-        mTestableLooper.processAllMessages();
-    }
-
-    private NotificationRecord generateNotificationRecord(NotificationChannel channel) {
-        return generateNotificationRecord(channel, null);
-    }
-
-    private NotificationRecord generateNotificationRecord(NotificationChannel channel,
-            Notification.TvExtender extender) {
-        if (channel == null) {
-            channel = mTestNotificationChannel;
-        }
-        Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
-                .setContentTitle("foo")
-                .setSmallIcon(android.R.drawable.sym_def_app_icon)
-                .addAction(new Notification.Action.Builder(null, "test", null).build());
-        if (extender != null) {
-            nb.extend(extender);
-        }
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
-                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
-        return new NotificationRecord(mContext, sbn, channel);
-    }
-
-    private void enableInteractAcrossUsers() {
-        TestablePermissions perms = mContext.getTestablePermissions();
-        perms.setPermission(android.Manifest.permission.INTERACT_ACROSS_USERS, PERMISSION_GRANTED);
-    }
-
-    @Test
-    public void testAreNotificationsEnabledForPackage() throws Exception {
-        mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
-                mUid);
-
-        verify(mPermissionHelper).hasPermission(mUid);
-    }
-
-    @Test
-    public void testAreNotificationsEnabledForPackage_crossUser() throws Exception {
-        try {
-            mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
-                    mUid + UserHandle.PER_USER_RANGE);
-            fail("Cannot call cross user without permission");
-        } catch (SecurityException e) {
-            // pass
-        }
-        verify(mPermissionHelper, never()).hasPermission(anyInt());
-
-        // cross user, with permission, no problem
-        enableInteractAcrossUsers();
-        mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
-                mUid + UserHandle.PER_USER_RANGE);
-
-        verify(mPermissionHelper).hasPermission(mUid + UserHandle.PER_USER_RANGE);
-    }
-
-    @Test
-    public void testAreNotificationsEnabledForPackage_viaInternalService() {
-        mInternalService.areNotificationsEnabledForPackage(mContext.getPackageName(), mUid);
-        verify(mPermissionHelper).hasPermission(mUid);
-    }
-
-    @Test
-    public void testGetPackageImportance() throws Exception {
-        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
-        assertThat(mBinderService.getPackageImportance(mContext.getPackageName()))
-                .isEqualTo(IMPORTANCE_DEFAULT);
-
-        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
-        assertThat(mBinderService.getPackageImportance(mContext.getPackageName()))
-                .isEqualTo(IMPORTANCE_NONE);
-    }
-
-    @Test
-    public void testEnqueueNotificationInternal_noChannel() throws Exception {
-        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
-        NotificationRecord nr = generateNotificationRecord(
-                new NotificationChannel("did not create", "", IMPORTANCE_DEFAULT));
-
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
-                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
-        waitForIdle();
-
-        verify(mPermissionHelper).hasPermission(mUid);
-        verify(mPermissionHelper, never()).hasPermission(Process.SYSTEM_UID);
-
-        reset(mPermissionHelper);
-        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
-
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
-                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
-        waitForIdle();
-
-        verify(mPermissionHelper).hasPermission(mUid);
-        assertThat(mService.mChannelToastsSent).contains(mUid);
-    }
-
-    @Test
-    public void testSetNotificationsEnabledForPackage_noChange() throws Exception {
-        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
-        mBinderService.setNotificationsEnabledForPackage(mContext.getPackageName(), mUid, true);
-
-        verify(mPermissionHelper, never()).setNotificationPermission(
-                anyString(), anyInt(), anyBoolean(), anyBoolean());
-    }
-
-    @Test
-    public void testSetNotificationsEnabledForPackage() throws Exception {
-        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
-        mBinderService.setNotificationsEnabledForPackage(mContext.getPackageName(), mUid, false);
-
-        verify(mPermissionHelper).setNotificationPermission(
-                mContext.getPackageName(), UserHandle.getUserId(mUid), false, true);
-
-        verify(mAppOpsManager, never()).setMode(anyInt(), anyInt(), anyString(), anyInt());
-    }
-
-    @Test
-    public void testUpdateAppNotifyCreatorBlock() throws Exception {
-        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
-
-        mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
-        Thread.sleep(500);
-        waitForIdle();
-
-        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
-
-        assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
-                captor.getValue().getAction());
-        assertEquals(PKG, captor.getValue().getPackage());
-        assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
-    }
-
-    @Test
-    public void testUpdateAppNotifyCreatorUnblock() throws Exception {
-        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
-
-        mBinderService.setNotificationsEnabledForPackage(PKG, mUid, true);
-        Thread.sleep(500);
-        waitForIdle();
-
-        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
-
-        assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
-                captor.getValue().getAction());
-        assertEquals(PKG, captor.getValue().getPackage());
-        assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
-    }
-
-    @Test
-    public void testGetNotificationChannelsBypassingDnd_blocked() throws RemoteException {
-        mService.setPreferencesHelper(mPreferencesHelper);
-
-        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
-
-        assertThat(mBinderService.getNotificationChannelsBypassingDnd(PKG, mUid).getList())
-                .isEmpty();
-        verify(mPreferencesHelper, never()).getImportance(anyString(), anyInt());
-        verify(mPreferencesHelper, never()).getNotificationChannelsBypassingDnd(PKG, mUid);
-    }
-
-    @Test
-    public void testBlockedNotifications_blockedByUser() throws Exception {
-        when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
-        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
-
-        NotificationChannel channel = new NotificationChannel("id", "name",
-                NotificationManager.IMPORTANCE_HIGH);
-        NotificationRecord r = generateNotificationRecord(channel);
-        mService.addEnqueuedNotification(r);
-
-        when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false);
-
-        NotificationManagerService.PostNotificationRunnable runnable =
-                mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
-                        r.getUid(), SystemClock.elapsedRealtime());
-        runnable.run();
-        waitForIdle();
-
-        verify(mUsageStats).registerBlocked(any());
-        verify(mUsageStats, never()).registerPostedByApp(any());
-    }
-
-    @Test
-    public void testEnqueueNotification_appBlocked() throws Exception {
-        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
-
-        mBinderService.enqueueNotificationWithTag(PKG, PKG,
-                "testEnqueueNotification_appBlocked", 0,
-                generateNotificationRecord(null).getNotification(), 0);
-        waitForIdle();
-        verify(mWorkerHandler, never()).post(
-                any(NotificationManagerService.EnqueueNotificationRunnable.class));
-    }
-
-    @Test
-    public void testDefaultChannelDoesNotUpdateApp_postMigrationToPermissions() throws Exception {
-        final NotificationChannel defaultChannel = mBinderService.getNotificationChannel(
-                PKG_N_MR1, ActivityManager.getCurrentUser(), PKG_N_MR1,
-                NotificationChannel.DEFAULT_CHANNEL_ID);
-        defaultChannel.setImportance(IMPORTANCE_NONE);
-
-        mBinderService.updateNotificationChannelForPackage(PKG_N_MR1, mUid, defaultChannel);
-
-        verify(mPermissionHelper).setNotificationPermission(
-                PKG_N_MR1, ActivityManager.getCurrentUser(), false, true);
-    }
-
-    @Test
-    public void testPostNotification_appPermissionFixed() throws Exception {
-        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
-        when(mPermissionHelper.isPermissionFixed(PKG, 0)).thenReturn(true);
-
-        NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG,
-                "testPostNotification_appPermissionFixed", 0,
-                temp.getNotification(), 0);
-        waitForIdle();
-        assertThat(mService.getNotificationRecordCount()).isEqualTo(1);
-        StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(PKG);
-        assertThat(mService.getNotificationRecord(notifs[0].getKey()).isImportanceFixed()).isTrue();
-    }
-
-    @Test
-    public void testSummaryNotification_appPermissionFixed() {
-        NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel);
-        mService.addNotification(temp);
-
-        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
-        when(mPermissionHelper.isPermissionFixed(PKG, temp.getUserId())).thenReturn(true);
-
-        NotificationRecord r = mService.createAutoGroupSummary(
-                temp.getUserId(), temp.getSbn().getPackageName(), temp.getKey(), false);
-
-        assertThat(r.isImportanceFixed()).isTrue();
-    }
-
-    @Test
-    public void testMediaNotificationsBypassBlock() throws Exception {
-        when(mAmi.getPendingIntentFlags(any(IIntentSender.class)))
-                .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT);
-        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
-
-        Notification.Builder nb = new Notification.Builder(
-                mContext, mTestNotificationChannel.getId())
-                .setContentTitle("foo")
-                .setSmallIcon(android.R.drawable.sym_def_app_icon)
-                .addAction(new Notification.Action.Builder(null, "test", null).build());
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
-                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
-        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
-        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
-
-        // normal blocked notifications - blocked
-        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
-
-        // just using the style - blocked
-        nb.setStyle(new Notification.MediaStyle());
-        sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
-                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
-        r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
-        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
-
-        // using the style, but incorrect type in session - blocked
-        nb.setStyle(new Notification.MediaStyle());
-        Bundle extras = new Bundle();
-        extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, new Intent());
-        nb.addExtras(extras);
-        sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
-                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
-        r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
-        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse();
-
-        // style + media session - bypasses block
-        nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
-        sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
-                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
-        r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
-        assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(),
-                r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue();
-    }
-
-    @Test
-    public void testMediaNotificationsBypassBlock_atPost() throws Exception {
-        when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
-        when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
-
-        Notification.Builder nb = new Notification.Builder(
-                mContext, mTestNotificationChannel.getId())
-                .setContentTitle("foo")
-                .setSmallIcon(android.R.drawable.sym_def_app_icon)
-                .addAction(new Notification.Action.Builder(null, "test", null).build());
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
-                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
-        NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
-        when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false);
-
-        mService.addEnqueuedNotification(r);
-        NotificationManagerService.PostNotificationRunnable runnable =
-                mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
-                        r.getUid(), SystemClock.elapsedRealtime());
-        runnable.run();
-        waitForIdle();
-
-        verify(mUsageStats).registerBlocked(any());
-        verify(mUsageStats, never()).registerPostedByApp(any());
-
-        // just using the style - blocked
-        mService.clearNotifications();
-        reset(mUsageStats);
-        nb.setStyle(new Notification.MediaStyle());
-        sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
-                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
-        r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
-        mService.addEnqueuedNotification(r);
-        runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
-                r.getUid(), SystemClock.elapsedRealtime());
-        runnable.run();
-        waitForIdle();
-
-        verify(mUsageStats).registerBlocked(any());
-        verify(mUsageStats, never()).registerPostedByApp(any());
-
-        // style + media session - bypasses block
-        mService.clearNotifications();
-        reset(mUsageStats);
-        nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
-        sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
-                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
-        r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-
-        mService.addEnqueuedNotification(r);
-        runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
-                r.getUid(), SystemClock.elapsedRealtime());
-        runnable.run();
-        waitForIdle();
-
-        verify(mUsageStats, never()).registerBlocked(any());
-        verify(mUsageStats).registerPostedByApp(any());
-    }
-
-    @Test
-    public void testGetAllUsersNotificationPermissions() {
-        // In this case, there are multiple users each with notification permissions (and also,
-        // for good measure, some without).
-        // make sure the collection returned contains info for all of them
-        final List<UserInfo> userInfos = new ArrayList<>();
-        userInfos.add(new UserInfo(0, "user0", 0));
-        userInfos.add(new UserInfo(1, "user1", 0));
-        userInfos.add(new UserInfo(2, "user2", 0));
-        when(mUm.getUsers()).thenReturn(userInfos);
-
-        // construct the permissions for each of them
-        ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> permissions0 = new ArrayMap<>(),
-                permissions1 = new ArrayMap<>();
-        permissions0.put(new Pair<>(10, "package1"), new Pair<>(true, false));
-        permissions0.put(new Pair<>(20, "package2"), new Pair<>(false, true));
-        permissions1.put(new Pair<>(11, "package1"), new Pair<>(false, false));
-        permissions1.put(new Pair<>(21, "package2"), new Pair<>(true, true));
-        when(mPermissionHelper.getNotificationPermissionValues(0)).thenReturn(permissions0);
-        when(mPermissionHelper.getNotificationPermissionValues(1)).thenReturn(permissions1);
-        when(mPermissionHelper.getNotificationPermissionValues(2)).thenReturn(new ArrayMap<>());
-
-        ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> combinedPermissions =
-                mService.getAllUsersNotificationPermissions();
-        assertTrue(combinedPermissions.get(new Pair<>(10, "package1")).first);
-        assertFalse(combinedPermissions.get(new Pair<>(10, "package1")).second);
-        assertFalse(combinedPermissions.get(new Pair<>(20, "package2")).first);
-        assertTrue(combinedPermissions.get(new Pair<>(20, "package2")).second);
-        assertFalse(combinedPermissions.get(new Pair<>(11, "package1")).first);
-        assertFalse(combinedPermissions.get(new Pair<>(11, "package1")).second);
-        assertTrue(combinedPermissions.get(new Pair<>(21, "package2")).first);
-        assertTrue(combinedPermissions.get(new Pair<>(21, "package2")).second);
-    }
-}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index d89141c..5468220 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -1065,9 +1065,8 @@
     }
 
     @Test
-    public void testApplyImportanceAdjustmentsForNonOemDefaultAppLockedChannels() {
+    public void testApplyImportanceAdjustments() {
         NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
-        channel.setImportanceLockedByOEM(false);
 
         StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
                 true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index 46b47f4..3a352cb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -88,51 +88,13 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager, true, false);
+        mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager, false);
         PackageInfo testPkgInfo = new PackageInfo();
         testPkgInfo.requestedPermissions = new String[]{ Manifest.permission.POST_NOTIFICATIONS };
         when(mPackageManager.getPackageInfo(anyString(), anyLong(), anyInt()))
                 .thenReturn(testPkgInfo);
     }
 
-    // TODO (b/194833441): Remove when the migration is enabled
-    @Test
-    public void testMethodsThrowIfMigrationDisabled() throws IllegalAccessException,
-            InvocationTargetException {
-        PermissionHelper permHelper =
-                new PermissionHelper(mPmi, mPackageManager, mPermManager, false, false);
-
-        Method[] allMethods = PermissionHelper.class.getDeclaredMethods();
-        for (Method method : allMethods) {
-            if (Modifier.isPublic(method.getModifiers()) &&
-                    !Objects.equals("isMigrationEnabled", method.getName())) {
-                Parameter[] params = method.getParameters();
-                List<Object> args = Lists.newArrayListWithCapacity(params.length);
-                for (int i = 0; i < params.length; i++) {
-                    Type type = params[i].getParameterizedType();
-                    if (type.getTypeName().equals("java.lang.String")) {
-                        args.add("");
-                    } else if (type.getTypeName().equals("boolean")){
-                        args.add(false);
-                    } else if (type.getTypeName().equals("int")) {
-                        args.add(1);
-                    } else if (type.getTypeName().equals(
-                            "com.android.server.notification.PermissionHelper$PackagePermission")) {
-                        args.add(null);
-                    }
-                }
-                try {
-                    method.invoke(permHelper, args.toArray());
-                    fail("Method should have thrown because migration flag is disabled");
-                } catch (InvocationTargetException e) {
-                    if (!(e.getTargetException() instanceof IllegalStateException)) {
-                        throw e;
-                    }
-                }
-            }
-        }
-    }
-
     @Test
     public void testHasPermission() throws Exception {
         when(mPmi.checkPostNotificationsPermissionGrantedOrLegacyAccess(anyInt()))
@@ -304,7 +266,7 @@
     @Test
     public void testSetNotificationPermission_pkgPerm_grantedByDefaultPermSet_allUserSet()
             throws Exception {
-        mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager, true, true);
+        mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager, true);
         when(mPmi.checkPermission(anyString(), anyString(), anyInt()))
                 .thenReturn(PERMISSION_DENIED);
         when(mPermManager.getPermissionFlags(anyString(),
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 6d08959..a5cec7e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -278,6 +278,14 @@
         when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(),
                 anyString(), eq(null), anyString())).thenReturn(MODE_DEFAULT);
 
+        ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+        appPermissions.put(new Pair(UID_P, PKG_P), new Pair(true, false));
+        appPermissions.put(new Pair(UID_O, PKG_O), new Pair(true, false));
+        appPermissions.put(new Pair(UID_N_MR1, PKG_N_MR1), new Pair(true, false));
+
+        when(mPermissionHelper.getNotificationPermissionValues(USER_SYSTEM))
+                .thenReturn(appPermissions);
+
         mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory();
 
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
@@ -408,6 +416,13 @@
         NotificationChannel channel10 = new NotificationChannel("id10", "name10", IMPORTANCE_HIGH);
         assertTrue(mHelper.createNotificationChannel(package10, uid10, channel10, true, false));
 
+        ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+        appPermissions.put(new Pair(uid0, package0), new Pair(false, false));
+        appPermissions.put(new Pair(uid10, package10), new Pair(true, false));
+
+        when(mPermissionHelper.getNotificationPermissionValues(10))
+                .thenReturn(appPermissions);
+
         ByteArrayOutputStream baos = writeXmlAndPurge(package10, uid10, true, 10);
 
         // Reset state.
@@ -433,6 +448,12 @@
         NotificationChannel channel0 = new NotificationChannel("id0", "name0", IMPORTANCE_HIGH);
         assertTrue(mHelper.createNotificationChannel(package0, uid0, channel0, true, false));
 
+        ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+        appPermissions.put(new Pair(uid0, package0), new Pair(true, false));
+
+        when(mPermissionHelper.getNotificationPermissionValues(USER_SYSTEM))
+                .thenReturn(appPermissions);
+
         ByteArrayOutputStream baos = writeXmlAndPurge(package0, uid0, true, 0);
 
         // Reset state.
@@ -478,7 +499,6 @@
         assertTrue(mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false));
 
         mHelper.setShowBadge(PKG_N_MR1, UID_N_MR1, true);
-        mHelper.setAppImportanceLocked(PKG_N_MR1, UID_N_MR1);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false,
                 UserHandle.USER_ALL, channel1.getId(), channel2.getId(),
@@ -489,7 +509,6 @@
         loadStreamXml(baos, false, UserHandle.USER_ALL);
 
         assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
-        assertTrue(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1));
         assertEquals(channel1,
                 mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false));
         compareChannels(channel2,
@@ -550,8 +569,6 @@
         mHelper.setInvalidMsgAppDemoted(PKG_P, UID_P, true);
         mHelper.setValidBubbleSent(PKG_P, UID_P);
 
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_NONE);
-
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true,
                 USER_SYSTEM, channel1.getId(), channel2.getId(), channel3.getId(),
                 NotificationChannel.DEFAULT_CHANNEL_ID);
@@ -562,7 +579,6 @@
 
         loadStreamXml(baos, true, USER_SYSTEM);
 
-        assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_O, UID_O));
         assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
         assertTrue(mHelper.hasSentInvalidMsg(PKG_P, UID_P));
         assertFalse(mHelper.hasSentInvalidMsg(PKG_N_MR1, UID_N_MR1));
@@ -601,7 +617,6 @@
 
     @Test
     public void testReadXml_oldXml_migrates() throws Exception {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
 
@@ -672,7 +687,6 @@
 
     @Test
     public void testReadXml_oldXml_backup_migratesWhenPkgInstalled() throws Exception {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
 
@@ -751,7 +765,6 @@
 
     @Test
     public void testReadXml_newXml_noMigration_showPermissionNotification() throws Exception {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
 
@@ -809,7 +822,6 @@
 
     @Test
     public void testReadXml_newXml_noMigration_noPermissionNotification() throws Exception {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
 
@@ -866,7 +878,6 @@
 
     @Test
     public void testReadXml_oldXml_migration_NoUid() throws Exception {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
 
@@ -900,7 +911,6 @@
 
     @Test
     public void testReadXml_newXml_noMigration_NoUid() throws Exception {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
 
@@ -933,7 +943,6 @@
 
     @Test
     public void testChannelXmlForNonBackup_postMigration() throws Exception {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
 
@@ -1014,7 +1023,6 @@
 
     @Test
     public void testChannelXmlForBackup_postMigration() throws Exception {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
 
@@ -1101,7 +1109,6 @@
 
     @Test
     public void testChannelXmlForBackup_postMigration_noExternal() throws Exception {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
 
@@ -1181,7 +1188,6 @@
 
     @Test
     public void testChannelXmlForBackup_postMigration_noLocalSettings() throws Exception {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
 
@@ -1303,6 +1309,12 @@
 
     @Test
     public void testBackupRestoreXml_withNullSoundUri() throws Exception {
+        ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+        appPermissions.put(new Pair(UID_N_MR1, PKG_N_MR1), new Pair(true, false));
+
+        when(mPermissionHelper.getNotificationPermissionValues(USER_SYSTEM))
+                .thenReturn(appPermissions);
+
         NotificationChannel channel =
                 new NotificationChannel("id", "name", IMPORTANCE_LOW);
         channel.setSound(null, mAudioAttributes);
@@ -1472,14 +1484,6 @@
     }
 
     @Test
-    public void testCreateChannel_blocked() throws Exception {
-        mHelper.setImportance(PKG_N_MR1, UID_N_MR1, IMPORTANCE_NONE);
-
-        assertTrue(mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
-                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false));
-    }
-
-    @Test
     public void testCreateChannel_badImportance() throws Exception {
         try {
             mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1,
@@ -1543,12 +1547,10 @@
 
     @Test
     public void testUpdate_preUpgrade_updatesAppFields() throws Exception {
-        mHelper.setImportance(PKG_N_MR1, UID_N_MR1, IMPORTANCE_UNSPECIFIED);
         assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
         assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
         assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
                 mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1));
-        assertFalse(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1));
 
         NotificationChannel defaultChannel = mHelper.getNotificationChannel(
                 PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false);
@@ -1566,8 +1568,6 @@
         assertEquals(Notification.PRIORITY_MAX, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
         assertEquals(Notification.VISIBILITY_SECRET, mHelper.getPackageVisibility(PKG_N_MR1,
                 UID_N_MR1));
-        assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_N_MR1, UID_N_MR1));
-        assertTrue(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1));
     }
 
     @Test
@@ -1592,9 +1592,6 @@
         assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_O, UID_O));
         assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
                 mHelper.getPackageVisibility(PKG_O, UID_O));
-        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_O,
-                UID_O));
-        assertFalse(mHelper.getIsAppImportanceLocked(PKG_O, UID_O));
     }
 
     @Test
@@ -1629,8 +1626,6 @@
         assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1));
         assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
                 mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1));
-        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1,
-                UID_N_MR1));
     }
 
     @Test
@@ -2015,8 +2010,9 @@
     }
 
     @Test
-    public void testCreateAndDeleteCanChannelsBypassDnd_localSettings() throws Exception {
+    public void testCreateAndDeleteCanChannelsBypassDnd_localSettings() {
         int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
+        when(mPermissionHelper.hasPermission(uid)).thenReturn(true);
 
         // create notification channel that can't bypass dnd
         // expected result: areChannelsBypassingDnd = false
@@ -2029,7 +2025,6 @@
 
         // create notification channel that can bypass dnd
         // expected result: areChannelsBypassingDnd = true
-        assertTrue(mHelper.getImportance(PKG_N_MR1, uid) != IMPORTANCE_NONE);
         NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
         channel2.setBypassDnd(true);
         mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true);
@@ -2052,8 +2047,6 @@
     @Test
     public void testCreateAndUpdateChannelsBypassingDnd_permissionHelper() {
         int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
-
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         when(mPermissionHelper.hasPermission(uid)).thenReturn(true);
 
         // create notification channel that can't bypass dnd
@@ -2076,10 +2069,8 @@
     }
 
     @Test
-    public void testCreateAndDeleteCanChannelsBypassDnd_permissionHelper() throws Exception {
+    public void testCreateAndDeleteCanChannelsBypassDnd_permissionHelper() {
         int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
-
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         when(mPermissionHelper.hasPermission(uid)).thenReturn(true);
 
         // create notification channel that can't bypass dnd
@@ -2113,8 +2104,9 @@
     }
 
     @Test
-    public void testBlockedGroupDoesNotBypassDnd() throws Exception {
+    public void testBlockedGroupDoesNotBypassDnd() {
         int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
+        when(mPermissionHelper.hasPermission(uid)).thenReturn(true);
 
         // start in a 'allowed to bypass dnd state'
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
@@ -2140,33 +2132,10 @@
     }
 
     @Test
-    public void testBlockedAppsDoNotBypassDnd_localSettings() throws Exception {
+    public void testBlockedAppsDoNotBypassDnd_localSettings() {
         int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
-
-        // start in a 'allowed to bypass dnd state'
-        mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
-                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
-        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
-                mPermissionHelper, mLogger,
-                mAppOpsManager, mStatsEventBuilderFactory);
-
-        mHelper.setImportance(PKG_N_MR1, uid, IMPORTANCE_NONE);
-        // create notification channel that can bypass dnd, but app is blocked
-        // expected result: areChannelsBypassingDnd = false
-        NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
-        channel2.setBypassDnd(true);
-        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true);
-        assertFalse(mHelper.areChannelsBypassingDnd());
-        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
-        resetZenModeHelper();
-    }
-
-    @Test
-    public void testBlockedAppsDoNotBypassDnd_permissionHelper() throws Exception {
-        int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         when(mPermissionHelper.hasPermission(uid)).thenReturn(false);
+
         // start in a 'allowed to bypass dnd state'
         mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
                 NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
@@ -2186,8 +2155,32 @@
     }
 
     @Test
-    public void testUpdateCanChannelsBypassDnd() throws Exception {
+    public void testBlockedAppsDoNotBypassDnd_permissionHelper() {
         int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
+        when(mPermissionHelper.hasPermission(uid)).thenReturn(false);
+
+        // start in a 'allowed to bypass dnd state'
+        mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0,
+                NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
+        when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
+        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+                mPermissionHelper, mLogger,
+                mAppOpsManager, mStatsEventBuilderFactory);
+
+        // create notification channel that can bypass dnd, but app is blocked
+        // expected result: areChannelsBypassingDnd = false
+        NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        channel2.setBypassDnd(true);
+        mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true);
+        assertFalse(mHelper.areChannelsBypassingDnd());
+        verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any());
+        resetZenModeHelper();
+    }
+
+    @Test
+    public void testUpdateCanChannelsBypassDnd() {
+        int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1;
+        when(mPermissionHelper.hasPermission(uid)).thenReturn(true);
 
         // create notification channel that can't bypass dnd
         // expected result: areChannelsBypassingDnd = false
@@ -2405,8 +2398,8 @@
             when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(legacy);
 
             // create records with the default channel for all user 0 and user 1 uids
-            mHelper.getImportance(PKG_N_MR1, user0Uids[i]);
-            mHelper.getImportance(PKG_N_MR1, user1Uids[i]);
+            mHelper.canShowBadge(PKG_N_MR1, user0Uids[i]);
+            mHelper.canShowBadge(PKG_N_MR1, user1Uids[i]);
         }
 
         mHelper.onUserRemoved(1);
@@ -2445,17 +2438,6 @@
     }
 
     @Test
-    public void testOnPackageChanged_packageRemoval_importance() throws Exception {
-        mHelper.setImportance(PKG_N_MR1, UID_N_MR1, NotificationManager.IMPORTANCE_HIGH);
-
-        mHelper.onPackagesChanged(true, USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{
-                UID_N_MR1});
-
-        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1,
-                UID_N_MR1));
-    }
-
-    @Test
     public void testOnPackageChanged_packageRemoval_groups() throws Exception {
         NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
         mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true);
@@ -2496,17 +2478,14 @@
         mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
         mHelper.createNotificationChannelGroup(
                 PKG_O, UID_O, new NotificationChannelGroup("1", "bye"), true);
-        mHelper.lockChannelsForOEM(pkg.toArray(new String[]{}));
         mHelper.updateDefaultApps(UserHandle.getUserId(UID_O), null, pkgPair);
         mHelper.setNotificationDelegate(PKG_O, UID_O, "", 1);
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_NONE);
         mHelper.setBubblesAllowed(PKG_O, UID_O, DEFAULT_BUBBLE_PREFERENCE);
         mHelper.setShowBadge(PKG_O, UID_O, false);
         mHelper.setAppImportanceLocked(PKG_O, UID_O);
 
         mHelper.clearData(PKG_O, UID_O);
 
-        assertEquals(IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_O, UID_O));
         assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), DEFAULT_BUBBLE_PREFERENCE);
         assertTrue(mHelper.canShowBadge(PKG_O, UID_O));
         assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
@@ -2518,13 +2497,10 @@
         mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
 
         assertTrue(channel.isImportanceLockedByCriticalDeviceFunction());
-        assertTrue(channel.isImportanceLockedByOEM());
     }
 
     @Test
     public void testRecordDefaults() throws Exception {
-        assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1,
-                UID_N_MR1));
         assertEquals(true, mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1));
         assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size());
     }
@@ -2760,69 +2736,7 @@
     }
 
     @Test
-    public void testDumpJson_prePermissionMigration() throws Exception {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
-        // before the migration is active, we want to verify that:
-        //   - all notification importance info should come from package preferences
-        //   - if there are permissions granted or denied from packages PreferencesHelper doesn't
-        //     know about, those are ignored if migration is not enabled
-
-        // package permissions map to be passed in
-        ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
-        appPermissions.put(new Pair(1, "first"), new Pair(true, false));    // not in local prefs
-        appPermissions.put(new Pair(3, "third"), new Pair(false, false));   // not in local prefs
-        appPermissions.put(new Pair(UID_P, PKG_P), new Pair(true, false));  // in local prefs
-        appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
-
-        NotificationChannel channel1 =
-                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
-        NotificationChannel channel3 = new NotificationChannel("id3", "name3", IMPORTANCE_HIGH);
-
-        mHelper.createNotificationChannel(PKG_P, UID_P, channel1, true, false);
-        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, false, false);
-        mHelper.setImportance(PKG_N_MR1, UID_N_MR1, IMPORTANCE_NONE);
-        mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
-
-        // in the json array, all of the individual package preferences are simply elements in the
-        // values array. this set is to collect expected outputs for each of our packages.
-        // the key/value pairs are: (userId, package name) -> expected importance
-        ArrayMap<Pair<Integer, String>, String> expected = new ArrayMap<>();
-        expected.put(new Pair(UserHandle.getUserId(UID_P), PKG_P), "LOW");
-        expected.put(new Pair(UserHandle.getUserId(UID_O), PKG_O), "HIGH");
-        expected.put(new Pair(UserHandle.getUserId(UID_N_MR1), PKG_N_MR1), "NONE");
-
-        JSONArray actual = (JSONArray) mHelper.dumpJson(
-                new NotificationManagerService.DumpFilter(), appPermissions)
-                .get("PackagePreferencess");
-        assertThat(actual.length()).isEqualTo(expected.size());
-        for (int i = 0; i < actual.length(); i++) {
-            JSONObject pkgInfo = actual.getJSONObject(i);
-            Pair<Integer, String> pkgKey =
-                    new Pair(pkgInfo.getInt("userId"), pkgInfo.getString("packageName"));
-            assertTrue(expected.containsKey(pkgKey));
-            assertThat(pkgInfo.getString("importance")).isEqualTo(expected.get(pkgKey));
-        }
-
-        // also make sure that (more likely to actually happen) if we don't provide an array of
-        // app preferences (and do null instead), the same thing happens, so do the same checks
-        JSONArray actualWithNullInput = (JSONArray) mHelper.dumpJson(
-                new NotificationManagerService.DumpFilter(), null)
-                .get("PackagePreferencess");
-        assertThat(actualWithNullInput.length()).isEqualTo(expected.size());
-        for (int i = 0; i < actualWithNullInput.length(); i++) {
-            JSONObject pkgInfo = actualWithNullInput.getJSONObject(i);
-            Pair<Integer, String> pkgKey =
-                    new Pair(pkgInfo.getInt("userId"), pkgInfo.getString("packageName"));
-            assertTrue(expected.containsKey(pkgKey));
-            assertThat(pkgInfo.getString("importance")).isEqualTo(expected.get(pkgKey));
-        }
-    }
-
-    @Test
     public void testDumpJson_postPermissionMigration() throws Exception {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         // when getting a json dump, we want to verify that:
         //   - all notification importance info should come from the permission, even if the data
         //     isn't there yet but is present in package preferences
@@ -2844,11 +2758,8 @@
 
         mHelper.createNotificationChannel(PKG_P, UID_P, channel1, true, false);
         mHelper.createNotificationChannel(PKG_P, UID_P, channel2, false, false);
-        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_LOW);
         mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, false, false);
-        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
         mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false);
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
 
         // in the json array, all of the individual package preferences are simply elements in the
         // values array. this set is to collect expected outputs for each of our packages.
@@ -2887,11 +2798,10 @@
     public void testDumpJson_givenNullInput_postMigration() throws Exception {
         // simple test just to make sure nothing dies if we pass in null input even post migration
         // for some reason, even though in practice this should not be how one calls this method
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
 
-        // some packages exist, with some importance info that won't be looked at
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
-        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+        // some packages exist
+        mHelper.canShowBadge(PKG_O, UID_O);
+        mHelper.canShowBadge(PKG_P, UID_P);
 
         JSONArray actual = (JSONArray) mHelper.dumpJson(
                 new NotificationManagerService.DumpFilter(), null)
@@ -2908,44 +2818,16 @@
     }
 
     @Test
-    public void testDumpBansJson_prePermissionMigration() throws Exception {
-        // confirm that the package bans that are in json are only from package preferences, and
-        // not from the passed-in permissions map
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
-
-        ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
-        appPermissions.put(new Pair(1, "first"), new Pair(true, false));    // not in local prefs
-        appPermissions.put(new Pair(3, "third"), new Pair(false, false));   // not in local prefs
-        appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
-
-        // package preferences: only PKG_P is banned
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
-        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
-
-        // make sure that's the only thing in the package ban output
-        JSONArray actual = mHelper.dumpBansJson(
-                new NotificationManagerService.DumpFilter(), appPermissions);
-        assertThat(actual.length()).isEqualTo(1);
-
-        JSONObject ban = actual.getJSONObject(0);
-        assertThat(ban.getInt("userId")).isEqualTo(UserHandle.getUserId(UID_P));
-        assertThat(ban.getString("packageName")).isEqualTo(PKG_P);
-    }
-
-    @Test
     public void testDumpBansJson_postPermissionMigration() throws Exception {
         // confirm that the package bans that are in the output include all packages that
         // have their permission set to false, and not based on PackagePreferences importance
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
 
         ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
         appPermissions.put(new Pair(1, "first"), new Pair(true, false));    // not in local prefs
         appPermissions.put(new Pair(3, "third"), new Pair(false, false));   // not in local prefs
         appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
 
-        // package preferences: PKG_O not banned based on local importance, and PKG_P is
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
-        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+        mHelper.canShowBadge(PKG_O, UID_O);
 
         // expected output
         ArraySet<Pair<Integer, String>> expected = new ArraySet<>();
@@ -2967,10 +2849,6 @@
     @Test
     public void testDumpBansJson_givenNullInput() throws Exception {
         // no one should do this, but...
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
-
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
-        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
 
         JSONArray actual = mHelper.dumpBansJson(
                 new NotificationManagerService.DumpFilter(), null);
@@ -2978,59 +2856,8 @@
     }
 
     @Test
-    public void testDumpString_prePermissionMigration() {
-        // confirm that the string resulting from dumpImpl contains only info from package prefs
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
-
-        ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
-        appPermissions.put(new Pair(1, "first"), new Pair(true, false));    // not in local prefs
-        appPermissions.put(new Pair(3, "third"), new Pair(false, true));    // not in local prefs
-        appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
-
-        // local package preferences: PKG_O is not banned even though the permissions would
-        // indicate so
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
-        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
-
-        // get dump output as a string so we can inspect the contents later
-        StringWriter sw = new StringWriter();
-        PrintWriter pw = new PrintWriter(sw);
-        mHelper.dump(pw, "", new NotificationManagerService.DumpFilter(), appPermissions);
-        pw.flush();
-        String actual = sw.toString();
-
-        // expected (substring) output for each preference
-        ArrayList<String> expected = new ArrayList<>();
-        expected.add(PKG_O + " (" + UID_O + ") importance=HIGH");
-        expected.add(PKG_P + " (" + UID_P + ") importance=NONE");
-
-        // make sure the things in app permissions do NOT show up
-        ArrayList<String> notExpected = new ArrayList<>();
-        notExpected.add("first (1) importance=DEFAULT");
-        notExpected.add("third (3) importance=NONE");
-        notExpected.add("userSet=");  // no user-set information pre migration
-
-        for (String exp : expected) {
-            assertTrue(actual.contains(exp));
-        }
-
-        for (String notExp : notExpected) {
-            assertFalse(actual.contains(notExp));
-        }
-
-        // also make sure it works the same if we pass in a null input
-        StringWriter sw2 = new StringWriter();
-        PrintWriter pw2 = new PrintWriter(sw2);
-        mHelper.dump(pw2, "", new NotificationManagerService.DumpFilter(), null);
-        pw.flush();
-        String actualWithNullInput = sw2.toString();
-        assertThat(actualWithNullInput).isEqualTo(actual);
-    }
-
-    @Test
     public void testDumpString_postPermissionMigration() {
         // confirm that the string resulting from dumpImpl contains only importances from permission
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
 
         ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
         appPermissions.put(new Pair(1, "first"), new Pair(true, false));    // not in local prefs
@@ -3038,8 +2865,8 @@
         appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
 
         // local package preferences
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
-        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+        mHelper.canShowBadge(PKG_O, UID_O);
+        mHelper.canShowBadge(PKG_P, UID_P);
 
         // get dump output as a string so we can inspect the contents later
         StringWriter sw = new StringWriter();
@@ -3072,11 +2899,10 @@
     @Test
     public void testDumpString_givenNullInput() {
         // test that this doesn't choke on null input
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
 
         // local package preferences
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
-        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+        mHelper.canShowBadge(PKG_O, UID_O);
+        mHelper.canShowBadge(PKG_P, UID_P);
 
         // get dump output
         StringWriter sw = new StringWriter();
@@ -3090,48 +2916,8 @@
     }
 
     @Test
-    public void testDumpProto_prePermissionMigration() throws Exception {
-        // test that dumping to proto gets the importances from the right place
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
-
-        ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
-        appPermissions.put(new Pair(1, "first"), new Pair(true, false));    // not in local prefs
-        appPermissions.put(new Pair(3, "third"), new Pair(false, false));   // not in local prefs
-        appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
-
-        // local package preferences
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
-        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
-
-        // expected output: only the local preferences
-        // map format: (uid, package name) -> importance (int)
-        ArrayMap<Pair<Integer, String>, Integer> expected = new ArrayMap<>();
-        expected.put(new Pair(UID_O, PKG_O), IMPORTANCE_HIGH);
-        expected.put(new Pair(UID_P, PKG_P), IMPORTANCE_NONE);
-
-        // get the proto output and inspect its contents
-        ProtoOutputStream proto = new ProtoOutputStream();
-        mHelper.dump(proto, new NotificationManagerService.DumpFilter(), appPermissions);
-
-        RankingHelperProto actual = RankingHelperProto.parseFrom(proto.getBytes());
-        assertThat(actual.records.length).isEqualTo(expected.size());
-        for (int i = 0; i < actual.records.length; i++) {
-            RankingHelperProto.RecordProto record = actual.records[i];
-            Pair<Integer, String> pkgKey = new Pair(record.uid, record.package_);
-            assertTrue(expected.containsKey(pkgKey));
-            assertThat(record.importance).isEqualTo(expected.get(pkgKey));
-        }
-
-        // also check that it's the same as passing in null input
-        ProtoOutputStream proto2 = new ProtoOutputStream();
-        mHelper.dump(proto2, new NotificationManagerService.DumpFilter(), null);
-        assertThat(proto.getBytes()).isEqualTo(proto2.getBytes());
-    }
-
-    @Test
     public void testDumpProto_postPermissionMigration() throws Exception {
         // test that dumping to proto gets the importances from the right place
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
 
         // permissions -- these should take precedence
         ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
@@ -3140,8 +2926,8 @@
         appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
 
         // local package preferences
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
-        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_LOW);
+        mHelper.canShowBadge(PKG_O, UID_O);
+        mHelper.canShowBadge(PKG_P, UID_P);
 
         // expected output: all the packages, but only the ones provided via appPermissions
         // should have importance set (aka not PKG_P)
@@ -3429,14 +3215,6 @@
     }
 
     @Test
-    public void testAppBlockedLogging() {
-        mHelper.setEnabled(PKG_N_MR1, 1020, false);
-        assertEquals(1, mLogger.getCalls().size());
-        assertEquals(
-                NotificationChannelLogger.NotificationChannelEvent.APP_NOTIFICATIONS_BLOCKED,
-                mLogger.get(0).event);
-    }
-    @Test
     public void testXml_statusBarIcons_default() throws Exception {
         String preQXml = "<ranking version=\"1\">\n"
                 + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -3517,7 +3295,7 @@
 
     @Test
     public void testIsDelegateAllowed_noDelegate() {
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_UNSPECIFIED);
+        mHelper.canShowBadge(PKG_O, UID_O);
 
         assertFalse(mHelper.isDelegateAllowed(PKG_O, UID_O, "whatever", 0));
     }
@@ -3555,7 +3333,7 @@
 
     @Test
     public void testDelegateXml_noDelegate() throws Exception {
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_UNSPECIFIED);
+        mHelper.canShowBadge(PKG_O, UID_O);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
         mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
@@ -3744,337 +3522,7 @@
     }
 
     @Test
-    public void testLockChannelsForOEM_emptyList() {
-        mHelper.lockChannelsForOEM(null);
-        mHelper.lockChannelsForOEM(new String[0]);
-        // no exception
-    }
-
-    @Test
-    public void testLockChannelsForOEM_appWide() {
-        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
-        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
-        NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT);
-        // different uids, same package
-        mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
-        mHelper.createNotificationChannel(PKG_O, 3, b, false, false);
-        mHelper.createNotificationChannel(PKG_O, 30, c, true, true);
-
-        mHelper.lockChannelsForOEM(new String[] {PKG_O});
-
-        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
-                .isImportanceLockedByOEM());
-        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false)
-                .isImportanceLockedByOEM());
-        assertTrue(mHelper.getNotificationChannel(PKG_O, 30, c.getId(), false)
-                .isImportanceLockedByOEM());
-    }
-
-    @Test
-    public void testLockChannelsForOEM_onlyGivenPkg() {
-        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
-        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
-        mHelper.createNotificationChannel(PKG_N_MR1, 30, b, false, false);
-
-        mHelper.lockChannelsForOEM(new String[] {PKG_O});
-
-        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
-                .isImportanceLockedByOEM());
-        assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, 30, b.getId(), false)
-                .isImportanceLockedByOEM());
-    }
-
-    @Test
-    public void testLockChannelsForOEM_channelSpecific() {
-        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
-        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
-        NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT);
-        // different uids, same package
-        mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
-        mHelper.createNotificationChannel(PKG_O, 3, b, false, false);
-        mHelper.createNotificationChannel(PKG_O, 30, c, true, true);
-
-        mHelper.lockChannelsForOEM(new String[] {PKG_O + ":b", PKG_O + ":c"});
-
-        assertFalse(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
-                .isImportanceLockedByOEM());
-        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false)
-                .isImportanceLockedByOEM());
-        assertTrue(mHelper.getNotificationChannel(PKG_O, 30, c.getId(), false)
-                .isImportanceLockedByOEM());
-    }
-
-    @Test
-    public void testLockChannelsForOEM_onlyGivenPkg_appDoesNotExistYet() {
-        mHelper.lockChannelsForOEM(new String[] {PKG_O});
-
-        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
-        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
-        mHelper.createNotificationChannel(PKG_N_MR1, 30, b, false, false);
-
-        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
-                .isImportanceLockedByOEM());
-        assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, 30, b.getId(), false)
-                .isImportanceLockedByOEM());
-    }
-
-    @Test
-    public void testLockChannelsForOEM_channelSpecific_appDoesNotExistYet() {
-        mHelper.lockChannelsForOEM(new String[] {PKG_O + ":b", PKG_O + ":c"});
-
-        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
-        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
-        NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT);
-        // different uids, same package
-        mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
-        mHelper.createNotificationChannel(PKG_O, 3, b, false, false);
-        mHelper.createNotificationChannel(PKG_O, 30, c, true, true);
-
-        assertFalse(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
-                .isImportanceLockedByOEM());
-        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false)
-                .isImportanceLockedByOEM());
-        assertTrue(mHelper.getNotificationChannel(PKG_O, 30, c.getId(), false)
-                .isImportanceLockedByOEM());
-    }
-
-    @Test
-    public void testLockChannelsForOEM_onlyGivenPkg_appDoesNotExistYet_restoreData()
-            throws Exception {
-        mHelper.lockChannelsForOEM(new String[] {PKG_O});
-
-        final String xml = "<ranking version=\"1\">\n"
-                + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
-                + "<channel id=\"a\" name=\"a\" importance=\"3\"/>"
-                + "<channel id=\"b\" name=\"b\" importance=\"3\"/>"
-                + "</package>"
-                + "<package name=\"" + PKG_N_MR1 + "\" uid=\"" + UID_N_MR1 + "\" >\n"
-                + "<channel id=\"a\" name=\"a\" importance=\"3\"/>"
-                + "<channel id=\"b\" name=\"b\" importance=\"3\"/>"
-                + "</package>"
-                + "</ranking>";
-        TypedXmlPullParser parser = Xml.newFastPullParser();
-        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
-                null);
-        parser.nextTag();
-        mHelper.readXml(parser, false, UserHandle.USER_ALL);
-
-        assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, "a", false)
-                .isImportanceLockedByOEM());
-        assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "b", false)
-                .isImportanceLockedByOEM());
-    }
-
-    @Test
-    public void testLockChannelsForOEM_onlyGivenPkg_appDoesNotExistYet_restoreData_postMigration()
-            throws Exception {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
-        mHelper.lockChannelsForOEM(new String[] {PKG_O});
-
-        final String xml = "<ranking version=\"1\">\n"
-                + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
-                + "<channel id=\"a\" name=\"a\" importance=\"3\"/>"
-                + "<channel id=\"b\" name=\"b\" importance=\"3\"/>"
-                + "</package>"
-                + "</ranking>";
-        TypedXmlPullParser parser = Xml.newFastPullParser();
-        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
-                null);
-        parser.nextTag();
-        mHelper.readXml(parser, false, UserHandle.USER_ALL);
-
-        assertFalse(mHelper.getNotificationChannel(PKG_O, UID_O, "a", false)
-                .isImportanceLockedByOEM());
-    }
-
-    @Test
-    public void testLockChannelsForOEM_channelSpecific_appDoesNotExistYet_restoreData()
-            throws Exception {
-        mHelper.lockChannelsForOEM(new String[] {PKG_O + ":b", PKG_O + ":c"});
-
-        final String xml = "<ranking version=\"1\">\n"
-                + "<package name=\"" + PKG_O + "\" uid=\"" + 3 + "\" >\n"
-                + "<channel id=\"a\" name=\"a\" importance=\"3\"/>"
-                + "<channel id=\"b\" name=\"b\" importance=\"3\"/>"
-                + "</package>"
-                + "<package name=\"" + PKG_O + "\" uid=\"" + 30 + "\" >\n"
-                + "<channel id=\"c\" name=\"c\" importance=\"3\"/>"
-                + "</package>"
-                + "</ranking>";
-        TypedXmlPullParser parser = Xml.newFastPullParser();
-        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
-                null);
-        parser.nextTag();
-        mHelper.readXml(parser, false, UserHandle.USER_ALL);
-
-        assertFalse(mHelper.getNotificationChannel(PKG_O, 3, "a", false)
-                .isImportanceLockedByOEM());
-        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, "b", false)
-                .isImportanceLockedByOEM());
-        assertTrue(mHelper.getNotificationChannel(PKG_O, 30, "c", false)
-                .isImportanceLockedByOEM());
-    }
-
-    @Test
-    public void testLockChannelsForOEM_channelSpecific_appDoesNotExistYet_restoreData_postMigration()
-            throws Exception {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
-        mHelper.lockChannelsForOEM(new String[] {PKG_O + ":b", PKG_O + ":c"});
-
-        final String xml = "<ranking version=\"1\">\n"
-                + "<package name=\"" + PKG_O + "\" uid=\"" + 3 + "\" >\n"
-                + "<channel id=\"a\" name=\"a\" importance=\"3\"/>"
-                + "<channel id=\"b\" name=\"b\" importance=\"3\"/>"
-                + "</package>"
-                + "<package name=\"" + PKG_O + "\" uid=\"" + 30 + "\" >\n"
-                + "<channel id=\"c\" name=\"c\" importance=\"3\"/>"
-                + "</package>"
-                + "</ranking>";
-        TypedXmlPullParser parser = Xml.newFastPullParser();
-        parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
-                null);
-        parser.nextTag();
-        mHelper.readXml(parser, false, UserHandle.USER_ALL);
-
-        assertFalse(mHelper.getNotificationChannel(PKG_O, 3, "a", false)
-                .isImportanceLockedByOEM());
-        assertFalse(mHelper.getNotificationChannel(PKG_O, 3, "b", false)
-                .isImportanceLockedByOEM());
-        assertFalse(mHelper.getNotificationChannel(PKG_O, 30, "c", false)
-                .isImportanceLockedByOEM());
-    }
-
-    @Test
-    public void testLockChannelsForOEM_channelSpecific_clearData() {
-        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
-        mHelper.getImportance(PKG_O, UID_O);
-        mHelper.lockChannelsForOEM(new String[] {PKG_O + ":" + a.getId()});
-        mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
-        assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false)
-                .isImportanceLockedByOEM());
-
-        mHelper.clearData(PKG_O, UID_O);
-
-        // it's back!
-        mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
-        // and still locked
-        assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false)
-                .isImportanceLockedByOEM());
-    }
-
-    @Test
-    public void testLockChannelsForOEM_channelDoesNotExistYet_appWide() {
-        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
-        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
-
-        mHelper.lockChannelsForOEM(new String[] {PKG_O});
-
-        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
-                .isImportanceLockedByOEM());
-
-        mHelper.createNotificationChannel(PKG_O, 3, b, true, false);
-        assertTrue(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false)
-                .isImportanceLockedByOEM());
-    }
-
-    @Test
-    public void testLockChannelsForOEM_channelDoesNotExistYet_channelSpecific() {
-        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
-        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
-
-        mHelper.lockChannelsForOEM(new String[] {PKG_O + ":a", PKG_O + ":b"});
-
-        assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false)
-                .isImportanceLockedByOEM());
-
-        mHelper.createNotificationChannel(PKG_O, UID_O, b, true, false);
-        assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, b.getId(), false)
-                .isImportanceLockedByOEM());
-    }
-
-    @Test
-    public void testLockChannelsForOEM_channelSpecific_clearData_postMigration() {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
-        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
-        mHelper.getImportance(PKG_O, UID_O);
-        mHelper.lockChannelsForOEM(new String[] {PKG_O + ":" + a.getId()});
-        mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
-        assertFalse(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false)
-                .isImportanceLockedByOEM());
-
-        mHelper.clearData(PKG_O, UID_O);
-
-        // it's back!
-        mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
-        // and never locked
-        assertFalse(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false)
-                .isImportanceLockedByOEM());
-    }
-
-    @Test
-    public void testLockChannelsForOEM_channelDoesNotExistYet_appWide_postMigration() {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
-        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
-        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(PKG_O, 3, a, true, false);
-
-        mHelper.lockChannelsForOEM(new String[] {PKG_O});
-
-        assertFalse(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false)
-                .isImportanceLockedByOEM());
-
-        mHelper.createNotificationChannel(PKG_O, 3, b, true, false);
-        assertFalse(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false)
-                .isImportanceLockedByOEM());
-    }
-
-    @Test
-    public void testLockChannelsForOEM_channelDoesNotExistYet_channelSpecific_postMigration() {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
-        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
-        NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
-
-        mHelper.lockChannelsForOEM(new String[] {PKG_O + ":a", PKG_O + ":b"});
-
-        assertFalse(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false)
-                .isImportanceLockedByOEM());
-
-        mHelper.createNotificationChannel(PKG_O, UID_O, b, true, false);
-        assertFalse(mHelper.getNotificationChannel(PKG_O, UID_O, b.getId(), false)
-                .isImportanceLockedByOEM());
-    }
-
-    @Test
-    public void testUpdateNotificationChannel_oemLockedImportance() {
-        NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
-        mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false);
-
-        mHelper.lockChannelsForOEM(new String[] {PKG_O});
-
-        NotificationChannel update = new NotificationChannel("a", "a", IMPORTANCE_NONE);
-        update.setAllowBubbles(false);
-
-        mHelper.updateNotificationChannel(PKG_O, UID_O, update, true);
-
-        assertEquals(IMPORTANCE_HIGH,
-                mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance());
-        assertEquals(false,
-                mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canBubble());
-
-        mHelper.updateNotificationChannel(PKG_O, UID_O, update, true);
-
-        assertEquals(IMPORTANCE_HIGH,
-                mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance());
-    }
-
-    @Test
     public void testUpdateNotificationChannel_fixedPermission() {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(true);
 
         NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
@@ -4093,7 +3541,6 @@
 
     @Test
     public void testUpdateNotificationChannel_fixedPermission_butUserPreviouslyBlockedIt() {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(true);
 
         NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_NONE);
@@ -4112,7 +3559,6 @@
 
     @Test
     public void testUpdateNotificationChannel_fixedPermission_butAppAllowsIt() {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(true);
 
         NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
@@ -4132,7 +3578,6 @@
 
     @Test
     public void testUpdateNotificationChannel_notFixedPermission() {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
         when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(false);
 
         NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH);
@@ -5312,56 +4757,7 @@
     }
 
     @Test
-    public void testPullPackagePreferencesStats_prePermissionMigration() {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
-
-        // build a collection of app permissions that should be passed in but ignored
-        ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
-        appPermissions.put(new Pair(1, "first"), new Pair(true, false));    // not in local prefs
-        appPermissions.put(new Pair(3, "third"), new Pair(false, false));   // not in local prefs
-        appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
-
-        // package preferences: PKG_O not banned based on local importance, and PKG_P is
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
-        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
-
-        // expected output. format: uid -> importance, as only uid (and not package name)
-        // is in PackageNotificationPreferences
-        ArrayMap<Integer, Integer> expected = new ArrayMap<>();
-        expected.put(UID_O, IMPORTANCE_HIGH);
-        expected.put(UID_P, IMPORTANCE_NONE);
-
-        // unexpected output. these UIDs should not show up in the output at all
-        ArraySet<Integer> unexpected = new ArraySet<>();
-        unexpected.add(1);
-        unexpected.add(3);
-
-        ArrayList<StatsEvent> events = new ArrayList<>();
-        mHelper.pullPackagePreferencesStats(events, appPermissions);
-
-        for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
-            if (builder.getAtomId() == PACKAGE_NOTIFICATION_PREFERENCES) {
-                int uid = builder.getInt(PackageNotificationPreferences.UID_FIELD_NUMBER);
-
-                // this shouldn't be any of the forbidden uids
-                assertFalse(unexpected.contains(uid));
-
-                // if it's one of the expected ids, then make sure the importance matches
-                assertTrue(expected.containsKey(uid));
-                assertThat(expected.get(uid)).isEqualTo(
-                            builder.getInt(PackageNotificationPreferences.IMPORTANCE_FIELD_NUMBER));
-
-                // pre-migration, the userSet field will always default to false
-                boolean userSet = builder.getBoolean(
-                        PackageNotificationPreferences.USER_SET_IMPORTANCE_FIELD_NUMBER);
-                assertFalse(userSet);
-            }
-        }
-    }
-
-    @Test
     public void testPullPackagePreferencesStats_postPermissionMigration() {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
 
         // build a collection of app permissions that should be passed in but ignored
         ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
@@ -5369,9 +4765,9 @@
         appPermissions.put(new Pair(3, "third"), new Pair(false, true));   // not in local prefs
         appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, true)); // in local prefs
 
-        // package preferences: PKG_O not banned based on local importance, and PKG_P is
-        mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
-        mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE);
+        // local preferences
+        mHelper.canShowBadge(PKG_O, UID_O);
+        mHelper.canShowBadge(PKG_P, UID_P);
 
         // expected output. format: uid -> importance, as only uid (and not package name)
         // is in PackageNotificationPreferences
@@ -5433,27 +4829,4 @@
         assertTrue((channelB.getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0);
         assertTrue((channelC.getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0);
     }
-
-    @Test
-    public void testDefaultChannelUpdatesApp_preMigrationToPermissions() throws Exception {
-        final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG_N_MR1,
-                UID_N_MR1,
-                NotificationChannel.DEFAULT_CHANNEL_ID, false);
-        defaultChannel.setImportance(IMPORTANCE_NONE);
-        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true);
-
-        assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_N_MR1, UID_N_MR1));
-    }
-
-    @Test
-    public void testDefaultChannelDoesNotUpdateApp_postMigrationToPermissions() throws Exception {
-        when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
-        final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG_N_MR1,
-                UID_N_MR1,
-                NotificationChannel.DEFAULT_CHANNEL_ID, false);
-        defaultChannel.setImportance(IMPORTANCE_NONE);
-        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true);
-
-        assertEquals(IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1, UID_N_MR1));
-    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 0bfd202..98c156e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -168,7 +168,8 @@
                     mock(StatsManager.class), mock(TelephonyManager.class),
                     mock(ActivityManagerInternal.class),
                     mock(MultiRateLimiter.class), mock(PermissionHelper.class),
-                    mock(UsageStatsManagerInternal.class), mock (TelecomManager.class));
+                    mock(UsageStatsManagerInternal.class), mock (TelecomManager.class),
+                    mock(NotificationChannelLogger.class));
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 3592158..eb6395b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -39,10 +39,8 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.junit.Assert.assertEquals;
@@ -50,9 +48,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 
 import android.graphics.Rect;
 import android.os.Binder;
@@ -380,7 +376,7 @@
         doReturn(false).when(dc).onDescendantOrientationChanged(any());
         final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
                 dc, "exiting app");
-        final ActivityRecord exitingActivity = exitingAppWindow.mActivityRecord;
+        final ActivityRecord exitingActivity= exitingAppWindow.mActivityRecord;
         // Wait until everything in animation handler get executed to prevent the exiting window
         // from being removed during WindowSurfacePlacer Traversal.
         waitUntilHandlersIdle();
@@ -409,41 +405,6 @@
     }
 
     @Test
-    public void testDelayWhileRecents() {
-        final DisplayContent dc = createNewDisplay(Display.STATE_ON);
-        doReturn(false).when(dc).onDescendantOrientationChanged(any());
-        final Task task = createTask(dc);
-
-        // Simulate activity1 launches activity2.
-        final ActivityRecord activity1 = createActivityRecord(task);
-        activity1.setVisible(true);
-        activity1.mVisibleRequested = false;
-        activity1.allDrawn = true;
-        final ActivityRecord activity2 = createActivityRecord(task);
-        activity2.setVisible(false);
-        activity2.mVisibleRequested = true;
-        activity2.allDrawn = true;
-
-        dc.mClosingApps.add(activity1);
-        dc.mOpeningApps.add(activity2);
-        dc.prepareAppTransition(TRANSIT_OPEN);
-        assertTrue(dc.mAppTransition.containsTransitRequest(TRANSIT_OPEN));
-
-        // Wait until everything in animation handler get executed to prevent the exiting window
-        // from being removed during WindowSurfacePlacer Traversal.
-        waitUntilHandlersIdle();
-
-        // Start recents
-        doReturn(true).when(task)
-                .isSelfAnimating(anyInt(), eq(ANIMATION_TYPE_RECENTS));
-
-        dc.mAppTransitionController.handleAppTransitionReady();
-
-        verify(activity1, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean());
-        verify(activity2, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean());
-    }
-
-    @Test
     public void testGetAnimationStyleResId() {
         // Verify getAnimationStyleResId will return as LayoutParams.windowAnimations when without
         // specifying window type.
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 49cd343..873d9f3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -306,6 +306,7 @@
         if (focus) {
             doReturn(window.getWindowInfo().token)
                     .when(mWindowManagerInternal).getFocusedWindowToken();
+            doReturn(window).when(mWm).getFocusedWindowLocked();
         }
     }
 }
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java
index e3485de..ec94f8a 100644
--- a/telecomm/java/android/telecom/PhoneAccountHandle.java
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -46,6 +46,14 @@
  * See {@link PhoneAccount}, {@link TelecomManager}.
  */
 public final class PhoneAccountHandle implements Parcelable {
+    /**
+     * Expected component name of Telephony phone accounts; ONLY used to determine if we should log
+     * the phone account handle ID.
+     */
+    private static final ComponentName TELEPHONY_COMPONENT_NAME =
+            new ComponentName("com.android.phone",
+                    "com.android.services.telephony.TelephonyConnectionService");
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 127403196)
     private final ComponentName mComponentName;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -136,14 +144,23 @@
 
     @Override
     public String toString() {
-        // Note: Log.pii called for mId as it can contain personally identifying phone account
-        // information such as SIP account IDs.
-        return new StringBuilder().append(mComponentName)
-                    .append(", ")
-                    .append(Log.pii(mId))
-                    .append(", ")
-                    .append(mUserHandle)
-                    .toString();
+        StringBuilder sb = new StringBuilder()
+                .append(mComponentName)
+                .append(", ");
+
+        if (TELEPHONY_COMPONENT_NAME.equals(mComponentName)) {
+            // Telephony phone account handles are now keyed by subscription id which is not
+            // sensitive.
+            sb.append(mId);
+        } else {
+            // Note: Log.pii called for mId as it can contain personally identifying phone account
+            // information such as SIP account IDs.
+            sb.append(Log.pii(mId));
+        }
+        sb.append(", ");
+        sb.append(mUserHandle);
+
+        return sb.toString();
     }
 
     @Override
diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java
index f47cf33..e7d95e4 100644
--- a/telephony/java/android/telephony/AnomalyReporter.java
+++ b/telephony/java/android/telephony/AnomalyReporter.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID;
+
 import static com.android.internal.telephony.TelephonyStatsLog.TELEPHONY_ANOMALY_DETECTED;
 
 import android.annotation.NonNull;
@@ -73,6 +75,7 @@
      *
      * This method sends the {@link TelephonyManager#ACTION_ANOMALY_REPORTED} broadcast, which is
      * system protected. Invoking this method unless you are the system will result in an error.
+     * Carrier Id will be set as UNKNOWN_CARRIER_ID.
      *
      * @param eventId a fixed event ID that will be sent for each instance of the same event. This
      *        ID should be generated randomly.
@@ -81,6 +84,23 @@
      *        static and must not contain any sensitive information (especially PII).
      */
     public static void reportAnomaly(@NonNull UUID eventId, String description) {
+        reportAnomaly(eventId, description, UNKNOWN_CARRIER_ID);
+    }
+
+    /**
+     * If enabled, build and send an intent to a Debug Service for logging.
+     *
+     * This method sends the {@link TelephonyManager#ACTION_ANOMALY_REPORTED} broadcast, which is
+     * system protected. Invoking this method unless you are the system will result in an error.
+     *
+     * @param eventId a fixed event ID that will be sent for each instance of the same event. This
+     *        ID should be generated randomly.
+     * @param description an optional description, that if included will be used as the subject for
+     *        identification and discussion of this event. This description should ideally be
+     *        static and must not contain any sensitive information (especially PII).
+     * @param carrierId the carrier of the id associated with this event.
+     */
+    public static void reportAnomaly(@NonNull UUID eventId, String description, int carrierId) {
         if (sContext == null) {
             Rlog.w(TAG, "AnomalyReporter not yet initialized, dropping event=" + eventId);
             return;
@@ -88,7 +108,7 @@
 
         TelephonyStatsLog.write(
                 TELEPHONY_ANOMALY_DETECTED,
-                0, // TODO: carrier id needs to be populated
+                carrierId,
                 eventId.getLeastSignificantBits(),
                 eventId.getMostSignificantBits());
 
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index e21301e..70fe6b1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4980,6 +4980,25 @@
                 KEY_PREFIX + "use_sip_uri_for_presence_subscribe_bool";
 
         /**
+         * Flag indicating whether or not to use TEL URI when setting the entity uri field and
+         * contact element of each tuple.
+         *
+         * When {@code true}, the device sets the entity uri field and contact element to be
+         * TEL URI. This is done by first searching for the first TEL URI provided in
+         * p-associated-uri header. If there are no TEL URIs in the p-associated-uri header, we will
+         * convert the first SIP URI provided in the header to a TEL URI. If there are no URIs in
+         * the p-associated-uri header, we will then fall back to using the SIM card to generate the
+         * TEL URI.
+         * If {@code false}, the first URI provided in the p-associated-uri header is used,
+         * independent of the URI scheme. If there are no URIs available from p-associated-uri
+         * header, we will try to generate a SIP URI or TEL URI from the information provided by the
+         * SIM card, depending on the information available.
+         * @hide
+         */
+        public static final String KEY_USE_TEL_URI_FOR_PIDF_XML_BOOL =
+                KEY_PREFIX + "use_tel_uri_for_pidf_xml";
+
+        /**
          * An integer key associated with the period of time in seconds the non-rcs capability
          * information of each contact is cached on the device.
          * <p>
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index fa6de1a..ac1f376 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -1673,4 +1673,9 @@
             return UNKNOWN;
         }
     }
+
+    /** @hide */
+    public static boolean isFailCauseExisting(@DataFailureCause int failCause) {
+        return sFailCauseMap.containsKey(failCause);
+    }
 }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index d07d809..2337a5a 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -3426,10 +3426,21 @@
      * Get subscriptionInfo list of subscriptions that are in the same group of given subId.
      * See {@link #createSubscriptionGroup(List)} for more details.
      *
-     * Caller will either have {@link android.Manifest.permission#READ_PHONE_STATE}
-     * permission or had carrier privilege permission on the subscription.
+     * Caller must have {@link android.Manifest.permission#READ_PHONE_STATE}
+     * or carrier privilege permission on the subscription.
      * {@link TelephonyManager#hasCarrierPrivileges()}
      *
+     * <p>Starting with API level 33, the caller needs the additional permission
+     * {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER}
+     * to get the list of subscriptions associated with a group UUID.
+     * This method can be invoked if one of the following requirements is met:
+     * <ul>
+     *     <li>If the app has carrier privilege permission.
+     *     {@link TelephonyManager#hasCarrierPrivileges()}
+     *     <li>If the app has {@link android.Manifest.permission#READ_PHONE_STATE} and
+     *     {@link Manifest.permission#USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER} permission.
+     * </ul>
+     *
      * @throws IllegalStateException if Telephony service is in bad state.
      * @throws SecurityException if the caller doesn't meet the requirements
      *             outlined above.
diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp
index d4fa1dd..62e16a5 100644
--- a/tests/ApkVerityTest/Android.bp
+++ b/tests/ApkVerityTest/Android.bp
@@ -37,8 +37,8 @@
         "general-tests",
         "vts",
     ],
-    data_device_bins: [
-        "block_device_writer",
+    target_required: [
+        "block_device_writer_module",
     ],
     data: [
         ":ApkVerityTestCertDer",
diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/ApkVerityTest/block_device_writer/Android.bp
index e5d009d..fdfa41f 100644
--- a/tests/ApkVerityTest/block_device_writer/Android.bp
+++ b/tests/ApkVerityTest/block_device_writer/Android.bp
@@ -24,7 +24,12 @@
 }
 
 cc_test {
-    name: "block_device_writer",
+    // Depending on how the test runs, the executable may be uploaded to different location.
+    // Before the bug in the file pusher is fixed, workaround by making the name unique.
+    // See b/124718249#comment12.
+    name: "block_device_writer_module",
+    stem: "block_device_writer",
+
     srcs: ["block_device_writer.cpp"],
     cflags: [
         "-D_FILE_OFFSET_BITS=64",
@@ -37,7 +42,22 @@
         "libbase",
         "libutils",
     ],
-    compile_multilib: "first",
+    // For some reasons, cuttlefish (x86) uses x86_64 test suites for testing. Unfortunately, when
+    // the uploader does not pick up the executable from correct output location. The following
+    // workaround allows the test to:
+    //  * upload the 32-bit exectuable for both 32 and 64 bits devices to use
+    //  * refer to the same executable name in Java
+    //  * no need to force the Java test to be archiecture specific.
+    //
+    // See b/145573317 for details.
+    multilib: {
+        lib32: {
+            suffix: "",
+        },
+        lib64: {
+            suffix: "64", // not really used
+        },
+    },
 
     auto_gen_config: false,
     test_suites: [
diff --git a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
index 730daf3..5c2c15b 100644
--- a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
+++ b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java
@@ -32,7 +32,7 @@
  * <p>To use this class, please push block_device_writer binary to /data/local/tmp.
  * 1. In Android.bp, add:
  * <pre>
- *      data_device_bins: ["block_device_writer"],
+ *     target_required: ["block_device_writer_module"],
  * </pre>
  * 2. In AndroidText.xml, add:
  * <pre>