Merge "Makes acknowledgeNewUserDisclaimer() a @TestApi"
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 96114dc..ffa534e 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -1376,6 +1376,11 @@
         }
     }
 
+    private boolean isAllowedBlobAccess(int uid, String packageName) {
+        return (!Process.isSupplemental(uid) && !Process.isIsolated(uid)
+                && !mPackageManagerInternal.isInstantApp(packageName, UserHandle.getUserId(uid)));
+    }
+
     private class PackageChangedReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -1437,8 +1442,7 @@
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
 
-            if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
-                    packageName, UserHandle.getUserId(callingUid))) {
+            if (!isAllowedBlobAccess(callingUid, packageName)) {
                 throw new SecurityException("Caller not allowed to create session; "
                         + "callingUid=" + callingUid + ", callingPackage=" + packageName);
             }
@@ -1487,8 +1491,7 @@
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
 
-            if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
-                    packageName, UserHandle.getUserId(callingUid))) {
+            if (!isAllowedBlobAccess(callingUid, packageName)) {
                 throw new SecurityException("Caller not allowed to open blob; "
                         + "callingUid=" + callingUid + ", callingPackage=" + packageName);
             }
@@ -1519,8 +1522,7 @@
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
 
-            if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
-                    packageName, UserHandle.getUserId(callingUid))) {
+            if (!isAllowedBlobAccess(callingUid, packageName)) {
                 throw new SecurityException("Caller not allowed to open blob; "
                         + "callingUid=" + callingUid + ", callingPackage=" + packageName);
             }
@@ -1544,8 +1546,7 @@
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
 
-            if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
-                    packageName, UserHandle.getUserId(callingUid))) {
+            if (!isAllowedBlobAccess(callingUid, packageName)) {
                 throw new SecurityException("Caller not allowed to open blob; "
                         + "callingUid=" + callingUid + ", callingPackage=" + packageName);
             }
@@ -1628,8 +1629,7 @@
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
 
-            if (Process.isIsolated(callingUid) || mPackageManagerInternal.isInstantApp(
-                    packageName, UserHandle.getUserId(callingUid))) {
+            if (!isAllowedBlobAccess(callingUid, packageName)) {
                 throw new SecurityException("Caller not allowed to open blob; "
                         + "callingUid=" + callingUid + ", callingPackage=" + packageName);
             }
diff --git a/core/api/current.txt b/core/api/current.txt
index 2c085b4..77f869a 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -112,6 +112,7 @@
     field public static final String MANAGE_ONGOING_CALLS = "android.permission.MANAGE_ONGOING_CALLS";
     field public static final String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
     field public static final String MANAGE_WIFI_AUTO_JOIN = "android.permission.MANAGE_WIFI_AUTO_JOIN";
+    field public static final String MANAGE_WIFI_INTERFACES = "android.permission.MANAGE_WIFI_INTERFACES";
     field public static final String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
     field public static final String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
     field public static final String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
@@ -342,6 +343,7 @@
     field public static final int allowSingleTap = 16843353; // 0x1010259
     field public static final int allowTaskReparenting = 16843268; // 0x1010204
     field public static final int allowUndo = 16843999; // 0x10104df
+    field public static final int allowUntrustedActivityEmbedding;
     field public static final int alpha = 16843551; // 0x101031f
     field public static final int alphabeticModifiers = 16844110; // 0x101054e
     field public static final int alphabeticShortcut = 16843235; // 0x10101e3
@@ -906,6 +908,7 @@
     field public static final int keyboardNavigationCluster = 16844096; // 0x1010540
     field public static final int keycode = 16842949; // 0x10100c5
     field public static final int killAfterRestore = 16843420; // 0x101029c
+    field public static final int knownActivityEmbeddingCerts;
     field public static final int knownCerts = 16844330; // 0x101062a
     field public static final int lStar = 16844359; // 0x1010647
     field public static final int label = 16842753; // 0x1010001
@@ -10968,6 +10971,7 @@
     ctor public ActivityInfo(android.content.pm.ActivityInfo);
     method public int describeContents();
     method public void dump(android.util.Printer, String);
+    method @NonNull public java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts();
     method public final int getThemeResource();
     field public static final int COLOR_MODE_DEFAULT = 0; // 0x0
     field public static final int COLOR_MODE_HDR = 2; // 0x2
@@ -10995,6 +10999,7 @@
     field public static final int DOCUMENT_LAUNCH_NEVER = 3; // 0x3
     field public static final int DOCUMENT_LAUNCH_NONE = 0; // 0x0
     field public static final int FLAG_ALLOW_TASK_REPARENTING = 64; // 0x40
+    field public static final int FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING = 268435456; // 0x10000000
     field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8
     field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000
     field public static final int FLAG_CLEAR_TASK_ON_LAUNCH = 4; // 0x4
@@ -11084,6 +11089,7 @@
     method public void dump(android.util.Printer, String);
     method public static CharSequence getCategoryTitle(android.content.Context, int);
     method public int getGwpAsanMode();
+    method @NonNull public java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts();
     method public int getMemtagMode();
     method public int getNativeHeapZeroInitialized();
     method public int getRequestRawExternalStorageAccess();
@@ -52414,6 +52420,8 @@
     method public void requestAutofill(@NonNull android.view.View, int, @NonNull android.graphics.Rect);
     method public void setAutofillRequestCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.autofill.AutofillRequestCallback);
     method public void setUserData(@Nullable android.service.autofill.UserData);
+    method public boolean showAutofillDialog(@NonNull android.view.View);
+    method public boolean showAutofillDialog(@NonNull android.view.View, int);
     method public void unregisterCallback(@Nullable android.view.autofill.AutofillManager.AutofillCallback);
     field public static final String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
     field public static final String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 321ebcf..5e5f387 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -851,6 +851,10 @@
     field public static final int SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY = 11; // 0xb
   }
 
+  public static class Notification.MediaStyle extends android.app.Notification.Style {
+    method @NonNull @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public android.app.Notification.MediaStyle setRemotePlaybackInfo(@NonNull CharSequence, @DrawableRes int, @Nullable android.app.PendingIntent);
+  }
+
   public static final class Notification.TvExtender implements android.app.Notification.Extender {
     ctor public Notification.TvExtender();
     ctor public Notification.TvExtender(android.app.Notification);
@@ -5172,6 +5176,15 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.Keyphrase> CREATOR;
   }
 
+  public static final class SoundTrigger.KeyphraseRecognitionExtra implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getCoarseConfidenceLevel();
+    method public int getKeyphraseId();
+    method public int getRecognitionModes();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra> CREATOR;
+  }
+
   public static final class SoundTrigger.KeyphraseSoundModel extends android.hardware.soundtrigger.SoundTrigger.SoundModel implements android.os.Parcelable {
     ctor public SoundTrigger.KeyphraseSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], @Nullable android.hardware.soundtrigger.SoundTrigger.Keyphrase[], int);
     ctor public SoundTrigger.KeyphraseSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], @Nullable android.hardware.soundtrigger.SoundTrigger.Keyphrase[]);
@@ -8901,7 +8914,7 @@
     method @Nullable public android.net.wifi.nl80211.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String);
     method @NonNull public java.util.List<android.net.wifi.nl80211.NativeScanResult> getScanResults(@NonNull String, int);
     method @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String);
-    method public boolean notifyCountryCodeChanged();
+    method public void notifyCountryCodeChanged(@Nullable String);
     method @Nullable public static android.net.wifi.nl80211.WifiNl80211Manager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
     method @Deprecated public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback);
     method public boolean registerCountryCodeChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.CountryCodeChangedListener);
@@ -11136,7 +11149,8 @@
     method public int encodeSmdxSubjectAndReasonCode(@Nullable String, @Nullable String);
     method @CallSuper public android.os.IBinder onBind(android.content.Intent);
     method public abstract int onDeleteSubscription(int, String);
-    method public android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean, @Nullable android.os.Bundle);
+    method @Deprecated public android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean, @Nullable android.os.Bundle);
+    method @NonNull public android.service.euicc.DownloadSubscriptionResult onDownloadSubscription(int, int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean, @NonNull android.os.Bundle);
     method @Deprecated public int onDownloadSubscription(int, @NonNull android.telephony.euicc.DownloadableSubscription, boolean, boolean);
     method @Deprecated public abstract int onEraseSubscriptions(int);
     method public int onEraseSubscriptions(int, @android.telephony.euicc.EuiccCardManager.ResetOption int);
@@ -11755,8 +11769,13 @@
   public static class AlwaysOnHotwordDetector.EventPayload {
     method @Nullable public android.os.ParcelFileDescriptor getAudioStream();
     method @Nullable public android.media.AudioFormat getCaptureAudioFormat();
+    method @Nullable public byte[] getData();
+    method public int getDataFormat();
     method @Nullable public android.service.voice.HotwordDetectedResult getHotwordDetectedResult();
-    method @Nullable public byte[] getTriggerAudio();
+    method @NonNull public java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra> getKeyphraseRecognitionExtras();
+    method @Deprecated @Nullable public byte[] getTriggerAudio();
+    field public static final int DATA_FORMAT_RAW = 0; // 0x0
+    field public static final int DATA_FORMAT_TRIGGER_AUDIO = 1; // 0x1
   }
 
   public static final class AlwaysOnHotwordDetector.ModelParamRange {
@@ -11827,6 +11846,7 @@
   }
 
   public interface HotwordDetector {
+    method public default void destroy();
     method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public boolean startRecognition();
     method public boolean startRecognition(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @Nullable android.os.PersistableBundle);
     method public boolean stopRecognition();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 960fee8..bcba21b0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -300,6 +300,9 @@
 
   public class Notification implements android.os.Parcelable {
     method public boolean shouldShowForegroundImmediately();
+    field public static final String EXTRA_MEDIA_REMOTE_DEVICE = "android.mediaRemoteDevice";
+    field public static final String EXTRA_MEDIA_REMOTE_ICON = "android.mediaRemoteIcon";
+    field public static final String EXTRA_MEDIA_REMOTE_INTENT = "android.mediaRemoteIntent";
   }
 
   public final class NotificationChannel implements android.os.Parcelable {
@@ -1277,6 +1280,10 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.KeyphraseMetadata> CREATOR;
   }
 
+  public static final class SoundTrigger.KeyphraseRecognitionExtra implements android.os.Parcelable {
+    ctor public SoundTrigger.KeyphraseRecognitionExtra(int, int, int);
+  }
+
   public static final class SoundTrigger.ModelParamRange implements android.os.Parcelable {
     ctor public SoundTrigger.ModelParamRange(int, int);
   }
@@ -2399,6 +2406,19 @@
     method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public void triggerHardwareRecognitionEventForTest(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[]);
   }
 
+  public static final class AlwaysOnHotwordDetector.EventPayload.Builder {
+    ctor public AlwaysOnHotwordDetector.EventPayload.Builder();
+    method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload build();
+    method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setAudioStream(@NonNull android.os.ParcelFileDescriptor);
+    method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setCaptureAudioFormat(@NonNull android.media.AudioFormat);
+    method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setCaptureAvailable(boolean);
+    method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setCaptureSession(int);
+    method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setData(@NonNull byte[]);
+    method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setDataFormat(int);
+    method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setHotwordDetectedResult(@NonNull android.service.voice.HotwordDetectedResult);
+    method @NonNull public android.service.voice.AlwaysOnHotwordDetector.EventPayload.Builder setKeyphraseRecognitionExtras(@NonNull java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra>);
+  }
+
   public final class VisibleActivityInfo implements android.os.Parcelable {
     ctor public VisibleActivityInfo(int, @NonNull android.os.IBinder);
   }
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index cce7dd3..a58ceaa 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -215,6 +215,14 @@
     public abstract boolean isSystemReady();
 
     /**
+     * Returns package name given pid.
+     *
+     * @param pid The pid we are searching package name for.
+     */
+    @Nullable
+    public abstract String getPackageNameByPid(int pid);
+
+    /**
      * Sets if the given pid has an overlay UI or not.
      *
      * @param pid The pid we are setting overlay UI for.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b838016..9dd206e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1330,6 +1330,32 @@
     public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
 
     /**
+     * {@link #extras} key: A {@code CharSequence} name of a remote device used for a media session
+     * associated with a {@link Notification.MediaStyle} notification. This will show in the media
+     * controls output switcher instead of the local device name.
+     * @hide
+     */
+    @TestApi
+    public static final String EXTRA_MEDIA_REMOTE_DEVICE = "android.mediaRemoteDevice";
+
+    /**
+     * {@link #extras} key: A {@code int} resource ID for an icon that should show in the output
+     * switcher of the media controls for a {@link Notification.MediaStyle} notification.
+     * @hide
+     */
+    @TestApi
+    public static final String EXTRA_MEDIA_REMOTE_ICON = "android.mediaRemoteIcon";
+
+    /**
+     * {@link #extras} key: A {@code PendingIntent} that will replace the default action for the
+     * media controls output switcher chip, associated with a {@link Notification.MediaStyle}
+     * notification. This should launch an activity.
+     * @hide
+     */
+    @TestApi
+    public static final String EXTRA_MEDIA_REMOTE_INTENT = "android.mediaRemoteIntent";
+
+    /**
      * {@link #extras} key: the indices of actions to be shown in the compact view,
      * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
      */
@@ -8943,6 +8969,9 @@
 
         private int[] mActionsToShowInCompact = null;
         private MediaSession.Token mToken;
+        private CharSequence mDeviceName;
+        private int mDeviceIcon;
+        private PendingIntent mDeviceIntent;
 
         public MediaStyle() {
         }
@@ -8976,6 +9005,32 @@
         }
 
         /**
+         * For media notifications associated with playback on a remote device, provide device
+         * information that will replace the default values for the output switcher chip on the
+         * media control, as well as an intent to use when the output switcher chip is tapped,
+         * on devices where this is supported.
+         *
+         * @param deviceName The name of the remote device to display
+         * @param iconResource Icon resource representing the device
+         * @param chipIntent PendingIntent to send when the output switcher is tapped. May be
+         *                   {@code null}, in which case the output switcher will be disabled.
+         *                   This intent should open an Activity or it will be ignored.
+         * @return MediaStyle
+         *
+         * @hide
+         */
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL)
+        @NonNull
+        public MediaStyle setRemotePlaybackInfo(@NonNull CharSequence deviceName,
+                @DrawableRes int iconResource, @Nullable PendingIntent chipIntent) {
+            mDeviceName = deviceName;
+            mDeviceIcon = iconResource;
+            mDeviceIntent = chipIntent;
+            return this;
+        }
+
+        /**
          * @hide
          */
         @Override
@@ -9023,6 +9078,15 @@
             if (mActionsToShowInCompact != null) {
                 extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
             }
+            if (mDeviceName != null) {
+                extras.putCharSequence(EXTRA_MEDIA_REMOTE_DEVICE, mDeviceName);
+            }
+            if (mDeviceIcon > 0) {
+                extras.putInt(EXTRA_MEDIA_REMOTE_ICON, mDeviceIcon);
+            }
+            if (mDeviceIntent != null) {
+                extras.putParcelable(EXTRA_MEDIA_REMOTE_INTENT, mDeviceIntent);
+            }
         }
 
         /**
@@ -9038,6 +9102,15 @@
             if (extras.containsKey(EXTRA_COMPACT_ACTIONS)) {
                 mActionsToShowInCompact = extras.getIntArray(EXTRA_COMPACT_ACTIONS);
             }
+            if (extras.containsKey(EXTRA_MEDIA_REMOTE_DEVICE)) {
+                mDeviceName = extras.getCharSequence(EXTRA_MEDIA_REMOTE_DEVICE);
+            }
+            if (extras.containsKey(EXTRA_MEDIA_REMOTE_ICON)) {
+                mDeviceIcon = extras.getInt(EXTRA_MEDIA_REMOTE_ICON);
+            }
+            if (extras.containsKey(EXTRA_MEDIA_REMOTE_INTENT)) {
+                mDeviceIntent = extras.getParcelable(EXTRA_MEDIA_REMOTE_INTENT);
+            }
         }
 
         /**
diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java
index b40fbee..eadb7e3 100644
--- a/core/java/android/app/NotificationHistory.java
+++ b/core/java/android/app/NotificationHistory.java
@@ -610,6 +610,7 @@
         // Data can be too large for a transact. Write the data as a Blob, which will be written to
         // ashmem if too large.
         dest.writeBlob(data.marshall());
+        data.recycle();
     }
 
     public static final @NonNull Creator<NotificationHistory> CREATOR
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 5f00342..ea12942 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -920,7 +920,7 @@
     public void setNavBarModeOverride(@NavBarModeOverride int navBarModeOverride) {
         if (navBarModeOverride != NAV_BAR_MODE_OVERRIDE_NONE
                 && navBarModeOverride != NAV_BAR_MODE_OVERRIDE_KIDS) {
-            throw new IllegalArgumentException(
+            throw new UnsupportedOperationException(
                     "Supplied navBarModeOverride not supported: " + navBarModeOverride);
         }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6cb64b1..6614be7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9714,9 +9714,9 @@
      * service. When zero or more packages have been added, accessibility services that are not in
      * the list and not part of the system can not be enabled by the user.
      * <p>
-     * Calling with a null value for the list disables the restriction so that all services can be
-     * used, calling with an empty list only allows the built-in system services. Any non-system
-     * accessibility service that's currently enabled must be included in the list.
+     * Calling with a {@code null} value for the list disables the restriction so that all services
+     * can be used, calling with an empty list only allows the built-in system services. Any
+     * non-system accessibility service that's currently enabled must be included in the list.
      * <p>
      * System accessibility services are always available to the user and this method can't
      * disable them.
@@ -9742,8 +9742,8 @@
     /**
      * Returns the list of permitted accessibility services set by this device or profile owner.
      * <p>
-     * An empty list means no accessibility services except system services are allowed. Null means
-     * all accessibility services are allowed.
+     * An empty list means no accessibility services except system services are allowed.
+     * {@code null} means all accessibility services are allowed.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @return List of accessiblity service package names.
@@ -9788,7 +9788,7 @@
      * Returns the list of accessibility services permitted by the device or profiles
      * owners of this user.
      *
-     * <p>Null means all accessibility services are allowed, if a non-null list is returned
+     * <p>{@code null} means all accessibility services are allowed, if a non-null list is returned
      * it will contain the intersection of the permitted lists for any device or profile
      * owners that apply to this user. It will also include any system accessibility services.
      *
@@ -9934,6 +9934,8 @@
      *
      * @return List of input method package names.
      * @hide
+     *
+     * @see #setPermittedAccessibilityServices(ComponentName, List)
      */
     @SystemApi
     @RequiresPermission(anyOf = {
@@ -9954,29 +9956,30 @@
     /**
      * Returns the list of input methods permitted.
      *
-     * <p>When this method returns empty list means all input methods are allowed, if a non-empty
-     * list is returned it will contain the intersection of the permitted lists for any device or
-     * profile owners that apply to this user. It will also include any system input methods.
+     * <p>{@code null} means all input methods are allowed, if a non-null list is returned
+     * it will contain the intersection of the permitted lists for any device or profile
+     * owners that apply to this user. It will also include any system input methods.
      *
      * @return List of input method package names.
      * @hide
+     *
+     * @see #setPermittedAccessibilityServices(ComponentName, List)
      */
     @UserHandleAware
     @RequiresPermission(allOf = {
             android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
             android.Manifest.permission.MANAGE_USERS
             }, conditional = true)
-    public @NonNull List<String> getPermittedInputMethods() {
+    public @Nullable List<String> getPermittedInputMethods() {
         throwIfParentInstance("getPermittedInputMethods");
-        List<String> result = null;
         if (mService != null) {
             try {
-                result = mService.getPermittedInputMethodsAsUser(myUserId());
+                return mService.getPermittedInputMethodsAsUser(myUserId());
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
         }
-        return result != null ? result : Collections.emptyList();
+        return null;
     }
 
     /**
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 8bea006..0673b3a 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -18,6 +18,8 @@
 
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.compat.CompatChanges;
@@ -35,10 +37,16 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
+import android.util.ArraySet;
 import android.util.Printer;
 
+import com.android.internal.util.Parcelling;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.Set;
 
 /**
  * Information you can retrieve about a particular application
@@ -48,6 +56,9 @@
  */
 public class ActivityInfo extends ComponentInfo implements Parcelable {
 
+    private static final Parcelling.BuiltIn.ForStringSet sForStringSet =
+            Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForStringSet.class);
+
      // NOTE: When adding new data members be sure to update the copy-constructor, Parcel
      // constructor, and writeToParcel.
 
@@ -525,6 +536,13 @@
     public static final int FLAG_PREFER_MINIMAL_POST_PROCESSING = 0x2000000;
 
     /**
+     * Bit in {@link #flags}: If set, indicates that the activity can be embedded by untrusted
+     * hosts. In this case the interactions with and visibility of the embedded activity may be
+     * limited. Set from the {@link android.R.attr#allowUntrustedActivityEmbedding} attribute.
+     */
+    public static final int FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING = 0x10000000;
+
+    /**
      * @hide Bit in {@link #flags}: If set, this component will only be seen
      * by the system user.  Only works with broadcast receivers.  Set from the
      * android.R.attr#systemUserOnly attribute.
@@ -561,7 +579,8 @@
      * {@link #FLAG_STATE_NOT_NEEDED}, {@link #FLAG_EXCLUDE_FROM_RECENTS},
      * {@link #FLAG_ALLOW_TASK_REPARENTING}, {@link #FLAG_NO_HISTORY},
      * {@link #FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS},
-     * {@link #FLAG_HARDWARE_ACCELERATED}, {@link #FLAG_SINGLE_USER}.
+     * {@link #FLAG_HARDWARE_ACCELERATED}, {@link #FLAG_SINGLE_USER},
+     * {@link #FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING}.
      */
     public int flags;
 
@@ -1080,6 +1099,13 @@
     private static final long CHECK_MIN_WIDTH_HEIGHT_FOR_MULTI_WINDOW = 197654537L;
 
     /**
+     * Optional set of a certificates identifying apps that are allowed to embed this activity. From
+     * the "knownActivityEmbeddingCerts" attribute.
+     */
+    @Nullable
+    private Set<String> mKnownActivityEmbeddingCerts;
+
+    /**
      * Convert Java change bits to native.
      *
      * @hide
@@ -1227,6 +1253,7 @@
         launchMode = orig.launchMode;
         documentLaunchMode = orig.documentLaunchMode;
         permission = orig.permission;
+        mKnownActivityEmbeddingCerts = orig.mKnownActivityEmbeddingCerts;
         taskAffinity = orig.taskAffinity;
         targetActivity = orig.targetActivity;
         flags = orig.flags;
@@ -1442,6 +1469,31 @@
         return mMinAspectRatio;
     }
 
+    /**
+     * Gets the trusted host certificate digests of apps that are allowed to embed this activity.
+     * The digests are computed using the SHA-256 digest algorithm.
+     * @see android.R.attr#knownActivityEmbeddingCerts
+     */
+    @NonNull
+    public Set<String> getKnownActivityEmbeddingCerts() {
+        return mKnownActivityEmbeddingCerts == null ? Collections.emptySet()
+                : mKnownActivityEmbeddingCerts;
+    }
+
+    /**
+     * Sets the trusted host certificates of apps that are allowed to embed this activity.
+     * @see #getKnownActivityEmbeddingCerts()
+     * @hide
+     */
+    public void setKnownActivityEmbeddingCerts(@NonNull Set<String> knownActivityEmbeddingCerts) {
+        // Convert the provided digest to upper case for consistent Set membership
+        // checks when verifying the signing certificate digests of requesting apps.
+        mKnownActivityEmbeddingCerts = new ArraySet<>();
+        for (String knownCert : knownActivityEmbeddingCerts) {
+            mKnownActivityEmbeddingCerts.add(knownCert.toUpperCase(Locale.US));
+        }
+    }
+
     private boolean isChangeEnabled(long changeId) {
         return CompatChanges.isChangeEnabled(changeId, applicationInfo.packageName,
                 UserHandle.getUserHandleForUid(applicationInfo.uid));
@@ -1573,6 +1625,9 @@
         if (supportsSizeChanges) {
             pw.println(prefix + "supportsSizeChanges=true");
         }
+        if (mKnownActivityEmbeddingCerts != null) {
+            pw.println(prefix + "knownActivityEmbeddingCerts=" + mKnownActivityEmbeddingCerts);
+        }
         super.dumpBack(pw, prefix, dumpFlags);
     }
 
@@ -1618,6 +1673,7 @@
         dest.writeFloat(mMaxAspectRatio);
         dest.writeFloat(mMinAspectRatio);
         dest.writeBoolean(supportsSizeChanges);
+        sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
     }
 
     /**
@@ -1739,6 +1795,10 @@
         mMaxAspectRatio = source.readFloat();
         mMinAspectRatio = source.readFloat();
         supportsSizeChanges = source.readBoolean();
+        mKnownActivityEmbeddingCerts = sForStringSet.unparcel(source);
+        if (mKnownActivityEmbeddingCerts.isEmpty()) {
+            mKnownActivityEmbeddingCerts = null;
+        }
     }
 
     /**
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 567f649..2528e16 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -35,6 +35,7 @@
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Printer;
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
@@ -52,7 +53,9 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Locale;
 import java.util.Objects;
+import java.util.Set;
 import java.util.UUID;
 
 /**
@@ -62,6 +65,8 @@
  */
 public class ApplicationInfo extends PackageItemInfo implements Parcelable {
     private static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class);
+    private static final Parcelling.BuiltIn.ForStringSet sForStringSet =
+            Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForStringSet.class);
 
     /**
      * Default task affinity of all activities in this application. See
@@ -1550,6 +1555,13 @@
      */
     private int localeConfigRes;
 
+    /**
+     * Optional set of a certificates identifying apps that are allowed to embed activities of this
+     * application. From the "knownActivityEmbeddingCerts" attribute.
+     */
+    @Nullable
+    private Set<String> mKnownActivityEmbeddingCerts;
+
     public void dump(Printer pw, String prefix) {
         dump(pw, prefix, DUMP_FLAG_ALL);
     }
@@ -1673,6 +1685,9 @@
             }
         }
         pw.println(prefix + "createTimestamp=" + createTimestamp);
+        if (mKnownActivityEmbeddingCerts != null) {
+            pw.println(prefix + "knownActivityEmbeddingCerts=" + mKnownActivityEmbeddingCerts);
+        }
         super.dumpBack(pw, prefix);
     }
 
@@ -1787,6 +1802,11 @@
             }
             proto.end(detailToken);
         }
+        if (!ArrayUtils.isEmpty(mKnownActivityEmbeddingCerts)) {
+            for (String knownCert : mKnownActivityEmbeddingCerts) {
+                proto.write(ApplicationInfoProto.KNOWN_ACTIVITY_EMBEDDING_CERTS, knownCert);
+            }
+        }
         proto.end(token);
     }
 
@@ -1837,6 +1857,7 @@
         super(orig);
         taskAffinity = orig.taskAffinity;
         permission = orig.permission;
+        mKnownActivityEmbeddingCerts = orig.mKnownActivityEmbeddingCerts;
         processName = orig.processName;
         className = orig.className;
         theme = orig.theme;
@@ -2006,6 +2027,7 @@
             }
         }
         dest.writeInt(localeConfigRes);
+        sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
     }
 
     public static final @android.annotation.NonNull Parcelable.Creator<ApplicationInfo> CREATOR
@@ -2102,6 +2124,10 @@
             }
         }
         localeConfigRes = source.readInt();
+        mKnownActivityEmbeddingCerts = sForStringSet.unparcel(source);
+        if (mKnownActivityEmbeddingCerts.isEmpty()) {
+            mKnownActivityEmbeddingCerts = null;
+        }
     }
 
     /**
@@ -2658,7 +2684,6 @@
         return localeConfigRes;
     }
 
-
     /**
      *  List of all shared libraries this application is linked against. This
      *  list will only be set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES
@@ -2675,4 +2700,29 @@
         return sharedLibraryInfos;
     }
 
+    /**
+     * Gets the trusted host certificate digests of apps that are allowed to embed activities of
+     * this application. The digests are computed using the SHA-256 digest algorithm.
+     * @see android.R.attr#knownActivityEmbeddingCerts
+     */
+    @NonNull
+    public Set<String> getKnownActivityEmbeddingCerts() {
+        return mKnownActivityEmbeddingCerts == null ? Collections.emptySet()
+                : mKnownActivityEmbeddingCerts;
+    }
+
+    /**
+     * Sets the trusted host certificates of apps that are allowed to embed activities of this
+     * application.
+     * @see #getKnownActivityEmbeddingCerts()
+     * @hide
+     */
+    public void setKnownActivityEmbeddingCerts(@NonNull Set<String> knownActivityEmbeddingCerts) {
+        // Convert the provided digest to upper case for consistent Set membership
+        // checks when verifying the signing certificate digests of requesting apps.
+        mKnownActivityEmbeddingCerts = new ArraySet<>();
+        for (String knownCert : knownActivityEmbeddingCerts) {
+            mKnownActivityEmbeddingCerts.add(knownCert.toUpperCase(Locale.US));
+        }
+    }
 }
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index 515a009..ac1bcf3 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -131,7 +131,8 @@
     /** Usage: The buffer will be written to by the GPU */
     public static final long USAGE_GPU_COLOR_OUTPUT       = 1 << 9;
     /**
-     * The buffer will be used as a composer HAL overlay layer.
+     * The buffer will be used as a hardware composer overlay layer. That is, it will be displayed
+     * using the system compositor via {@link SurfaceControl}
      *
      * This flag is currently only needed when using
      * {@link android.view.SurfaceControl.Transaction#setBuffer(SurfaceControl, HardwareBuffer)}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index b0439d0..c363909 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -61,6 +61,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Locale;
 import java.util.UUID;
 
@@ -1576,31 +1577,58 @@
     }
 
     /**
-     *  Additional data conveyed by a {@link KeyphraseRecognitionEvent}
-     *  for a key phrase detection.
-     *
-     * @hide
+     * Additional data conveyed by a {@link KeyphraseRecognitionEvent}
+     * for a key phrase detection.
      */
-    public static class KeyphraseRecognitionExtra implements Parcelable {
-        /** The keyphrase ID */
+    public static final class KeyphraseRecognitionExtra implements Parcelable {
+        /**
+         * The keyphrase ID
+         *
+         * @hide
+         */
         @UnsupportedAppUsage
         public final int id;
 
-        /** Recognition modes matched for this event */
+        /**
+         * Recognition modes matched for this event
+         *
+         * @hide
+         */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         public final int recognitionModes;
 
-        /** Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER when user identification
-         * is not performed */
+        /**
+         * Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER when user identification
+         * is not performed
+         *
+         * @hide
+         */
         @UnsupportedAppUsage
         public final int coarseConfidenceLevel;
 
-        /** Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to
-         * be recognized (RecognitionConfig) */
+        /**
+         * Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to
+         * be recognized (RecognitionConfig)
+         *
+         * @hide
+         */
         @UnsupportedAppUsage
         @NonNull
         public final ConfidenceLevel[] confidenceLevels;
 
+
+        /**
+         * @hide
+         */
+        @TestApi
+        public KeyphraseRecognitionExtra(int id, @RecognitionModes int recognitionModes,
+                int coarseConfidenceLevel) {
+            this(id, recognitionModes, coarseConfidenceLevel, new ConfidenceLevel[0]);
+        }
+
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage
         public KeyphraseRecognitionExtra(int id, int recognitionModes, int coarseConfidenceLevel,
                 @Nullable ConfidenceLevel[] confidenceLevels) {
@@ -1611,7 +1639,47 @@
                     confidenceLevels != null ? confidenceLevels : new ConfidenceLevel[0];
         }
 
-        public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseRecognitionExtra> CREATOR
+        /**
+         * The keyphrase ID associated with this class' additional data
+         */
+        public int getKeyphraseId() {
+            return id;
+        }
+
+        /**
+         * Recognition modes matched for this event
+         */
+        @RecognitionModes
+        public int getRecognitionModes() {
+            return recognitionModes;
+        }
+
+        /**
+         * Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER when user identification
+         * is not performed
+         *
+         * <p>The confidence level is expressed in percent (0% -100%).
+         */
+        public int getCoarseConfidenceLevel() {
+            return coarseConfidenceLevel;
+        }
+
+        /**
+         * Detected confidence level for users defined in a keyphrase.
+         *
+         * <p>The confidence level is expressed in percent (0% -100%).
+         *
+         * <p>The user ID is derived from the system ID
+         * {@link android.os.UserHandle#getIdentifier()}.
+         *
+         * @hide
+         */
+        @NonNull
+        public Collection<ConfidenceLevel> getConfidenceLevels() {
+            return Arrays.asList(confidenceLevels);
+        }
+
+        public static final @NonNull Parcelable.Creator<KeyphraseRecognitionExtra> CREATOR
                 = new Parcelable.Creator<KeyphraseRecognitionExtra>() {
             public KeyphraseRecognitionExtra createFromParcel(Parcel in) {
                 return KeyphraseRecognitionExtra.fromParcel(in);
@@ -1632,7 +1700,7 @@
         }
 
         @Override
-        public void writeToParcel(Parcel dest, int flags) {
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
             dest.writeInt(id);
             dest.writeInt(recognitionModes);
             dest.writeInt(coarseConfidenceLevel);
@@ -1657,21 +1725,28 @@
 
         @Override
         public boolean equals(@Nullable Object obj) {
-            if (this == obj)
+            if (this == obj) {
                 return true;
-            if (obj == null)
+            }
+            if (obj == null) {
                 return false;
-            if (getClass() != obj.getClass())
+            }
+            if (getClass() != obj.getClass()) {
                 return false;
+            }
             KeyphraseRecognitionExtra other = (KeyphraseRecognitionExtra) obj;
-            if (!Arrays.equals(confidenceLevels, other.confidenceLevels))
+            if (!Arrays.equals(confidenceLevels, other.confidenceLevels)) {
                 return false;
-            if (id != other.id)
+            }
+            if (id != other.id) {
                 return false;
-            if (recognitionModes != other.recognitionModes)
+            }
+            if (recognitionModes != other.recognitionModes) {
                 return false;
-            if (coarseConfidenceLevel != other.coarseConfidenceLevel)
+            }
+            if (coarseConfidenceLevel != other.coarseConfidenceLevel) {
                 return false;
+            }
             return true;
         }
 
@@ -1715,7 +1790,7 @@
                     keyphraseExtras != null ? keyphraseExtras : new KeyphraseRecognitionExtra[0];
         }
 
-        public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseRecognitionEvent> CREATOR
+        public static final @NonNull Parcelable.Creator<KeyphraseRecognitionEvent> CREATOR
                 = new Parcelable.Creator<KeyphraseRecognitionEvent>() {
             public KeyphraseRecognitionEvent createFromParcel(Parcel in) {
                 return KeyphraseRecognitionEvent.fromParcelForKeyphrase(in);
diff --git a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
index 9772bde..2dd3aaa1 100644
--- a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
+++ b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java
@@ -24,6 +24,7 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
 import static android.net.NetworkStats.SET_DEFAULT;
 import static android.net.NetworkStats.TAG_NONE;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
@@ -108,6 +109,7 @@
         static final int VERSION_ADD_METERED = 4;
         static final int VERSION_ADD_DEFAULT_NETWORK = 5;
         static final int VERSION_ADD_OEM_MANAGED_NETWORK = 6;
+        static final int VERSION_ADD_SUB_ID = 7;
     }
 
     /**
@@ -448,6 +450,13 @@
                 oemNetCapabilities = NetworkTemplate.OEM_MANAGED_NO;
             }
 
+            final int subId;
+            if (version >= IdentitySetVersion.VERSION_ADD_SUB_ID) {
+                subId = in.readInt();
+            } else {
+                subId = INVALID_SUBSCRIPTION_ID;
+            }
+
             // Legacy files might contain TYPE_MOBILE_* types which were deprecated in later
             // releases. For backward compatibility, record them as TYPE_MOBILE instead.
             final int collapsedLegacyType = getCollapsedLegacyType(type);
@@ -457,7 +466,8 @@
                     .setWifiNetworkKey(networkId)
                     .setRoaming(roaming).setMetered(metered)
                     .setDefaultNetwork(defaultNetwork)
-                    .setOemManaged(oemNetCapabilities);
+                    .setOemManaged(oemNetCapabilities)
+                    .setSubId(subId);
             if (type == TYPE_MOBILE && ratType != NetworkTemplate.NETWORK_TYPE_ALL) {
                 builder.setRatType(ratType);
             }
@@ -501,10 +511,10 @@
      * This is copied from {@code NetworkStatsCollection#readLegacyUid}.
      * See {@code NetworkStatsService#maybeUpgradeLegacyStatsLocked}.
      *
-     * @param taggedData whether to read tagged data. For legacy uid files, the tagged
-     *                   data was stored in the same binary file with non-tagged data.
-     *                   But in later releases, these data should be kept in different
-     *                   recorders.
+     * @param taggedData whether to read only tagged data (true) or only non-tagged data
+     *                   (false). For legacy uid files, the tagged data was stored in
+     *                   the same binary file with non-tagged data. But in later releases,
+     *                   these data should be kept in different recorders.
      * @hide
      */
     @VisibleForTesting
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 9970641..1d39089 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -401,7 +401,12 @@
         /**
          * All known codenames starting from {@link VERSION_CODES.Q}.
          *
-         * <p>This includes in development codenames as well.
+         * <p>This includes in development codenames as well, i.e. if {@link #CODENAME} is not "REL"
+         * then the value of that is present in this set.
+         *
+         * <p>If a particular string is not present in this set, then it is either not a codename
+         * or a codename for a future release. For example, during Android R development, "Tiramisu"
+         * was not a known codename.
          *
          * @hide
          */
diff --git a/core/java/android/os/logcat/ILogcatManagerService.aidl b/core/java/android/os/logcat/ILogcatManagerService.aidl
index 68b5679..02db274 100644
--- a/core/java/android/os/logcat/ILogcatManagerService.aidl
+++ b/core/java/android/os/logcat/ILogcatManagerService.aidl
@@ -22,5 +22,7 @@
 interface ILogcatManagerService {
     void startThread(in int uid, in int gid, in int pid, in int fd);
     void finishThread(in int uid, in int gid, in int pid, in int fd);
+    void approve(in int uid, in int gid, in int pid, in int fd);
+    void decline(in int uid, in int gid, in int pid, in int fd);
 }
 
diff --git a/core/java/android/service/voice/AbstractHotwordDetector.java b/core/java/android/service/voice/AbstractHotwordDetector.java
index 1922607..01d5638 100644
--- a/core/java/android/service/voice/AbstractHotwordDetector.java
+++ b/core/java/android/service/voice/AbstractHotwordDetector.java
@@ -18,6 +18,7 @@
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
+import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityThread;
@@ -34,6 +35,9 @@
 import com.android.internal.app.IHotwordRecognitionStatusCallback;
 import com.android.internal.app.IVoiceInteractionManagerService;
 
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+
 /** Base implementation of {@link HotwordDetector}. */
 abstract class AbstractHotwordDetector implements HotwordDetector {
     private static final String TAG = AbstractHotwordDetector.class.getSimpleName();
@@ -45,6 +49,8 @@
     private final Handler mHandler;
     private final HotwordDetector.Callback mCallback;
     private final int mDetectorType;
+    private Consumer<AbstractHotwordDetector> mOnDestroyListener;
+    private final AtomicBoolean mIsDetectorActive;
 
     AbstractHotwordDetector(
             IVoiceInteractionManagerService managerService,
@@ -55,6 +61,7 @@
         mHandler = new Handler(Looper.getMainLooper());
         mCallback = callback;
         mDetectorType = detectorType;
+        mIsDetectorActive = new AtomicBoolean(true);
     }
 
     /**
@@ -70,6 +77,7 @@
         if (DEBUG) {
             Slog.i(TAG, "#recognizeHotword");
         }
+        throwIfDetectorIsNoLongerActive();
 
         // TODO: consider closing existing session.
 
@@ -106,6 +114,7 @@
         if (DEBUG) {
             Slog.d(TAG, "updateState()");
         }
+        throwIfDetectorIsNoLongerActive();
         synchronized (mLock) {
             updateStateLocked(options, sharedMemory, null /* callback */, mDetectorType);
         }
@@ -126,6 +135,35 @@
         }
     }
 
+    void registerOnDestroyListener(Consumer<AbstractHotwordDetector> onDestroyListener) {
+        synchronized (mLock) {
+            if (mOnDestroyListener != null) {
+                throw new IllegalStateException("only one destroy listener can be registered");
+            }
+            mOnDestroyListener = onDestroyListener;
+        }
+    }
+
+    @CallSuper
+    @Override
+    public void destroy() {
+        if (!mIsDetectorActive.get()) {
+            return;
+        }
+        mIsDetectorActive.set(false);
+        synchronized (mLock) {
+            mOnDestroyListener.accept(this);
+        }
+    }
+
+    protected void throwIfDetectorIsNoLongerActive() {
+        if (!mIsDetectorActive.get()) {
+            Slog.e(TAG, "attempting to use a destroyed detector which is no longer active");
+            throw new IllegalStateException(
+                    "attempting to use a destroyed detector which is no longer active");
+        }
+    }
+
     private static class BinderCallback
             extends IMicrophoneHotwordDetectionVoiceInteractionCallback.Stub {
         private final Handler mHandler;
@@ -146,7 +184,10 @@
             mHandler.sendMessage(obtainMessage(
                     HotwordDetector.Callback::onDetected,
                     mCallback,
-                    new AlwaysOnHotwordDetector.EventPayload(audioFormat, hotwordDetectedResult)));
+                    new AlwaysOnHotwordDetector.EventPayload.Builder()
+                            .setCaptureAudioFormat(audioFormat)
+                            .setHotwordDetectedResult(hotwordDetectedResult)
+                            .build()));
         }
     }
 }
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index c9daf52..bec5d1b 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -23,6 +23,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.app.ActivityThread;
@@ -59,6 +60,9 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.Locale;
 
 /**
@@ -336,7 +340,39 @@
      * Additional payload for {@link Callback#onDetected}.
      */
     public static class EventPayload {
-        private final boolean mTriggerAvailable;
+
+        /**
+         * Flags for describing the data format provided in the event payload.
+         *
+         * @hide
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(prefix = {"DATA_FORMAT_"}, value = {
+                DATA_FORMAT_RAW,
+                DATA_FORMAT_TRIGGER_AUDIO,
+        })
+        public @interface DataFormat {
+        }
+
+        /**
+         * Data format is not strictly defined by the framework, and the
+         * {@link android.hardware.soundtrigger.SoundTriggerModule} voice engine may populate this
+         * field in any format.
+         */
+        public static final int DATA_FORMAT_RAW = 0;
+
+        /**
+         * Data format is defined as trigger audio.
+         *
+         * <p>When this format is used, {@link #getCaptureAudioFormat()} can be used to understand
+         * further the audio format for reading the data.
+         *
+         * @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
+         */
+        public static final int DATA_FORMAT_TRIGGER_AUDIO = 1;
+
+        @DataFormat
+        private final int mDataFormat;
         // Indicates if {@code captureSession} can be used to continue capturing more audio
         // from the DSP hardware.
         private final boolean mCaptureAvailable;
@@ -348,40 +384,24 @@
         private final byte[] mData;
         private final HotwordDetectedResult mHotwordDetectedResult;
         private final ParcelFileDescriptor mAudioStream;
+        private final List<KeyphraseRecognitionExtra> mKephraseExtras;
 
-        EventPayload(boolean triggerAvailable, boolean captureAvailable,
-                AudioFormat audioFormat, int captureSession, byte[] data) {
-            this(triggerAvailable, captureAvailable, audioFormat, captureSession, data, null,
-                    null);
-        }
-
-        EventPayload(boolean triggerAvailable, boolean captureAvailable,
-                AudioFormat audioFormat, int captureSession, byte[] data,
-                HotwordDetectedResult hotwordDetectedResult) {
-            this(triggerAvailable, captureAvailable, audioFormat, captureSession, data,
-                    hotwordDetectedResult, null);
-        }
-
-        EventPayload(AudioFormat audioFormat, HotwordDetectedResult hotwordDetectedResult) {
-            this(false, false, audioFormat, -1, null, hotwordDetectedResult, null);
-        }
-
-        EventPayload(AudioFormat audioFormat,
-                HotwordDetectedResult hotwordDetectedResult,
-                ParcelFileDescriptor audioStream) {
-            this(false, false, audioFormat, -1, null, hotwordDetectedResult, audioStream);
-        }
-
-        private EventPayload(boolean triggerAvailable, boolean captureAvailable,
-                AudioFormat audioFormat, int captureSession, byte[] data,
-                HotwordDetectedResult hotwordDetectedResult, ParcelFileDescriptor audioStream) {
-            mTriggerAvailable = triggerAvailable;
+        private EventPayload(boolean captureAvailable,
+                @Nullable AudioFormat audioFormat,
+                int captureSession,
+                @DataFormat int dataFormat,
+                @Nullable byte[] data,
+                @Nullable HotwordDetectedResult hotwordDetectedResult,
+                @Nullable ParcelFileDescriptor audioStream,
+                @NonNull List<KeyphraseRecognitionExtra> keyphraseExtras) {
             mCaptureAvailable = captureAvailable;
             mCaptureSession = captureSession;
             mAudioFormat = audioFormat;
+            mDataFormat = dataFormat;
             mData = data;
             mHotwordDetectedResult = hotwordDetectedResult;
             mAudioStream = audioStream;
+            mKephraseExtras = keyphraseExtras;
         }
 
         /**
@@ -400,10 +420,12 @@
          * {@link #getCaptureAudioFormat()}.
          *
          * @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
+         * @deprecated Use {@link #getData()} instead.
          */
+        @Deprecated
         @Nullable
         public byte[] getTriggerAudio() {
-            if (mTriggerAvailable) {
+            if (mDataFormat == DATA_FORMAT_TRIGGER_AUDIO) {
                 return mData;
             } else {
                 return null;
@@ -411,6 +433,36 @@
         }
 
         /**
+         * Conveys the format of the additional data that is triggered with the keyphrase event.
+         *
+         * @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
+         * @see DataFormat
+         */
+        @DataFormat
+        public int getDataFormat() {
+            return mDataFormat;
+        }
+
+        /**
+         * Gets additional raw data that is triggered with the keyphrase event.
+         *
+         * <p>A {@link android.hardware.soundtrigger.SoundTriggerModule} may populate this
+         * field with opaque data for use by system applications who know about voice
+         * engine internals. Data may be null if the field is not populated by the
+         * {@link android.hardware.soundtrigger.SoundTriggerModule}.
+         *
+         * <p>If {@link #getDataFormat()} is {@link #DATA_FORMAT_TRIGGER_AUDIO}, then the
+         * entirety of this buffer is expected to be of the format from
+         * {@link #getCaptureAudioFormat()}.
+         *
+         * @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
+         */
+        @Nullable
+        public byte[] getData() {
+            return mData;
+        }
+
+        /**
          * Gets the session ID to start a capture from the DSP.
          * This may be null if streaming capture isn't possible.
          * If non-null, the format of the audio that can be captured can be
@@ -464,6 +516,166 @@
         public ParcelFileDescriptor getAudioStream() {
             return mAudioStream;
         }
+
+        /**
+         * Returns the keyphrases recognized by the voice engine with additional confidence
+         * information
+         *
+         * @return List of keyphrase extras describing additional data for each keyphrase the voice
+         * engine triggered on for this event. The ordering of the list is preserved based on what
+         * the ordering provided by {@link android.hardware.soundtrigger.SoundTriggerModule}.
+         */
+        @NonNull
+        public List<KeyphraseRecognitionExtra> getKeyphraseRecognitionExtras() {
+            return mKephraseExtras;
+        }
+
+        /**
+         * Builder class for {@link EventPayload} objects
+         *
+         * @hide
+         */
+        @TestApi
+        public static final class Builder {
+            private boolean mCaptureAvailable = false;
+            private int mCaptureSession = -1;
+            private AudioFormat mAudioFormat = null;
+            @DataFormat
+            private int mDataFormat = DATA_FORMAT_RAW;
+            private byte[] mData = null;
+            private HotwordDetectedResult mHotwordDetectedResult = null;
+            private ParcelFileDescriptor mAudioStream = null;
+            private List<KeyphraseRecognitionExtra> mKeyphraseExtras = Collections.emptyList();
+
+            public Builder() {}
+
+            Builder(SoundTrigger.KeyphraseRecognitionEvent keyphraseRecognitionEvent) {
+                setCaptureAvailable(keyphraseRecognitionEvent.isCaptureAvailable());
+                setCaptureSession(keyphraseRecognitionEvent.getCaptureSession());
+                if (keyphraseRecognitionEvent.getCaptureFormat() != null) {
+                    setCaptureAudioFormat(keyphraseRecognitionEvent.getCaptureFormat());
+                }
+                setDataFormat((keyphraseRecognitionEvent.triggerInData) ? DATA_FORMAT_TRIGGER_AUDIO
+                        : DATA_FORMAT_RAW);
+                if (keyphraseRecognitionEvent.getData() != null) {
+                    setData(keyphraseRecognitionEvent.getData());
+                }
+                if (keyphraseRecognitionEvent.keyphraseExtras != null) {
+                    setKeyphraseRecognitionExtras(
+                            Arrays.asList(keyphraseRecognitionEvent.keyphraseExtras));
+                }
+            }
+
+            /**
+             * Indicates if {@code captureSession} can be used to continue capturing more audio from
+             * the DSP hardware.
+             */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            public Builder setCaptureAvailable(boolean captureAvailable) {
+                mCaptureAvailable = captureAvailable;
+                return this;
+            }
+
+            /**
+             * Sets the session ID to start a capture from the DSP.
+             */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            public Builder setCaptureSession(int captureSession) {
+                mCaptureSession = captureSession;
+                return this;
+            }
+
+            /**
+             * Sets the format of the audio obtained using {@link #getTriggerAudio()}.
+             */
+            @NonNull
+            public Builder setCaptureAudioFormat(@NonNull AudioFormat audioFormat) {
+                mAudioFormat = audioFormat;
+                return this;
+            }
+
+            /**
+             * Conveys the format of the additional data that is triggered with the keyphrase event.
+             *
+             * @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
+             * @see DataFormat
+             */
+            @NonNull
+            public Builder setDataFormat(@DataFormat int dataFormat) {
+                mDataFormat = dataFormat;
+                return this;
+            }
+
+            /**
+             * Sets additional raw data that is triggered with the keyphrase event.
+             *
+             * <p>A {@link android.hardware.soundtrigger.SoundTriggerModule} may populate this
+             * field with opaque data for use by system applications who know about voice
+             * engine internals. Data may be null if the field is not populated by the
+             * {@link android.hardware.soundtrigger.SoundTriggerModule}.
+             *
+             * <p>If {@link #getDataFormat()} is {@link #DATA_FORMAT_TRIGGER_AUDIO}, then the
+             * entirety of this
+             * buffer is expected to be of the format from {@link #getCaptureAudioFormat()}.
+             *
+             * @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO
+             */
+            @NonNull
+            public Builder setData(@NonNull byte[] data) {
+                mData = data;
+                return this;
+            }
+
+            /**
+             * Sets {@link HotwordDetectedResult} associated with the hotword event, passed from
+             * {@link HotwordDetectionService}.
+             */
+            @NonNull
+            public Builder setHotwordDetectedResult(
+                    @NonNull HotwordDetectedResult hotwordDetectedResult) {
+                mHotwordDetectedResult = hotwordDetectedResult;
+                return this;
+            }
+
+            /**
+             * Sets a stream with bytes corresponding to the open audio stream with hotword data.
+             *
+             * <p>This data represents an audio stream in the format returned by
+             * {@link #getCaptureAudioFormat}.
+             *
+             * <p>Clients are expected to start consuming the stream within 1 second of receiving
+             * the
+             * event.
+             */
+            @NonNull
+            public Builder setAudioStream(@NonNull ParcelFileDescriptor audioStream) {
+                mAudioStream = audioStream;
+                return this;
+            }
+
+            /**
+             * Sets the keyphrases recognized by the voice engine with additional confidence
+             * information
+             */
+            @NonNull
+            public Builder setKeyphraseRecognitionExtras(
+                    @NonNull List<KeyphraseRecognitionExtra> keyphraseRecognitionExtras) {
+                mKeyphraseExtras = keyphraseRecognitionExtras;
+                return this;
+            }
+
+            /**
+             * Builds an {@link EventPayload} instance
+             */
+            @NonNull
+            public EventPayload build() {
+                return new EventPayload(mCaptureAvailable, mAudioFormat, mCaptureSession,
+                        mDataFormat, mData, mHotwordDetectedResult, mAudioStream,
+                        mKeyphraseExtras);
+            }
+        }
     }
 
     /**
@@ -993,11 +1205,14 @@
     /**
      * Invalidates this hotword detector so that any future calls to this result
      * in an IllegalStateException.
-     *
-     * @hide
      */
-    void invalidate() {
+    @Override
+    public void destroy() {
         synchronized (mLock) {
+            if (mAvailability == STATE_KEYPHRASE_ENROLLED) {
+                stopRecognition();
+            }
+
             mAvailability = STATE_INVALID;
             notifyStateChangedLocked();
 
@@ -1009,6 +1224,7 @@
                 }
             }
         }
+        super.destroy();
     }
 
     /**
@@ -1171,8 +1387,9 @@
                 Slog.i(TAG, "onDetected");
             }
             Message.obtain(mHandler, MSG_HOTWORD_DETECTED,
-                    new EventPayload(event.triggerInData, event.captureAvailable,
-                            event.captureFormat, event.captureSession, event.data, result))
+                    new EventPayload.Builder(event)
+                            .setHotwordDetectedResult(result)
+                            .build())
                     .sendToTarget();
         }
         @Override
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index e3bb589..dfe0f54 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -140,9 +140,7 @@
                 Log.d(TAG, "#detectFromDspSource");
             }
             HotwordDetectionService.this.onDetect(
-                    new AlwaysOnHotwordDetector.EventPayload(
-                            event.triggerInData, event.captureAvailable,
-                            event.captureFormat, event.captureSession, event.data),
+                    new AlwaysOnHotwordDetector.EventPayload.Builder(event).build(),
                     timeoutMillis,
                     new Callback(callback));
         }
diff --git a/core/java/android/service/voice/HotwordDetector.java b/core/java/android/service/voice/HotwordDetector.java
index 969ec22..96fd8bb 100644
--- a/core/java/android/service/voice/HotwordDetector.java
+++ b/core/java/android/service/voice/HotwordDetector.java
@@ -119,6 +119,17 @@
     void updateState(@Nullable PersistableBundle options, @Nullable SharedMemory sharedMemory);
 
     /**
+     * Invalidates this hotword detector so that any future calls to this result
+     * in an {@link IllegalStateException}.
+     *
+     * <p>If there are no other {@link HotwordDetector} instances linked to the
+     * {@link HotwordDetectionService}, the service will be shutdown.
+     */
+    default void destroy() {
+        throw new UnsupportedOperationException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
      * @hide
      */
     static String detectorTypeToString(int detectorType) {
diff --git a/core/java/android/service/voice/SoftwareHotwordDetector.java b/core/java/android/service/voice/SoftwareHotwordDetector.java
index 512a654..2d662ea 100644
--- a/core/java/android/service/voice/SoftwareHotwordDetector.java
+++ b/core/java/android/service/voice/SoftwareHotwordDetector.java
@@ -77,7 +77,7 @@
         if (DEBUG) {
             Slog.i(TAG, "#startRecognition");
         }
-
+        throwIfDetectorIsNoLongerActive();
         maybeCloseExistingSession();
 
         try {
@@ -100,6 +100,7 @@
         if (DEBUG) {
             Slog.i(TAG, "#stopRecognition");
         }
+        throwIfDetectorIsNoLongerActive();
 
         try {
             mManagerService.stopListeningFromMic();
@@ -110,6 +111,19 @@
         return true;
     }
 
+    @Override
+    public void destroy() {
+        stopRecognition();
+        maybeCloseExistingSession();
+
+        try {
+            mManagerService.shutdownHotwordDetectionService();
+        } catch (RemoteException ex) {
+            ex.rethrowFromSystemServer();
+        }
+        super.destroy();
+    }
+
     private void maybeCloseExistingSession() {
         // TODO: needs to be synchronized.
         // TODO: implement this
@@ -135,8 +149,11 @@
             mHandler.sendMessage(obtainMessage(
                     HotwordDetector.Callback::onDetected,
                     mCallback,
-                    new AlwaysOnHotwordDetector.EventPayload(
-                            audioFormat, hotwordDetectedResult, audioStream)));
+                    new AlwaysOnHotwordDetector.EventPayload.Builder()
+                            .setCaptureAudioFormat(audioFormat)
+                            .setAudioStream(audioStream)
+                            .setHotwordDetectedResult(hotwordDetectedResult)
+                            .build()));
         }
     }
 
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index f52c9ff..bf0cfbe 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -271,7 +271,7 @@
         // It's still guaranteed to have been stopped.
         // This helps with cases where the voice interaction implementation is changed
         // by the user.
-        safelyShutdownHotwordDetector();
+        safelyShutdownAllHotwordDetectors();
     }
 
     /**
@@ -380,11 +380,13 @@
         }
         synchronized (mLock) {
             // Allow only one concurrent recognition via the APIs.
-            safelyShutdownHotwordDetector();
+            safelyShutdownAllHotwordDetectors();
             mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback,
                     mKeyphraseEnrollmentInfo, mSystemService,
                     getApplicationContext().getApplicationInfo().targetSdkVersion,
                     supportHotwordDetectionService, options, sharedMemory);
+            mHotwordDetector.registerOnDestroyListener((detector) -> onDspHotwordDetectorDestroyed(
+                    (AlwaysOnHotwordDetector) detector));
         }
         return mHotwordDetector;
     }
@@ -433,10 +435,13 @@
         }
         synchronized (mLock) {
             // Allow only one concurrent recognition via the APIs.
-            safelyShutdownHotwordDetector();
+            safelyShutdownAllHotwordDetectors();
             mSoftwareHotwordDetector =
                     new SoftwareHotwordDetector(
                             mSystemService, null, options, sharedMemory, callback);
+            mSoftwareHotwordDetector.registerOnDestroyListener(
+                    (detector) -> onMicrophoneHotwordDetectorDestroyed(
+                            (SoftwareHotwordDetector) detector));
         }
         return mSoftwareHotwordDetector;
     }
@@ -482,51 +487,36 @@
         return mKeyphraseEnrollmentInfo.getKeyphraseMetadata(keyphrase, locale) != null;
     }
 
-    private void safelyShutdownHotwordDetector() {
+    private void safelyShutdownAllHotwordDetectors() {
         synchronized (mLock) {
-            shutdownDspHotwordDetectorLocked();
-            shutdownMicrophoneHotwordDetectorLocked();
+            if (mHotwordDetector != null) {
+                try {
+                    mHotwordDetector.destroy();
+                } catch (Exception ex) {
+                    Log.i(TAG, "exception destroying AlwaysOnHotwordDetector", ex);
+                }
+            }
+
+            if (mSoftwareHotwordDetector != null) {
+                try {
+                    mSoftwareHotwordDetector.destroy();
+                } catch (Exception ex) {
+                    Log.i(TAG, "exception destroying SoftwareHotwordDetector", ex);
+                }
+            }
         }
     }
 
-    private void shutdownDspHotwordDetectorLocked() {
-        if (mHotwordDetector == null) {
-            return;
+    private void onDspHotwordDetectorDestroyed(@NonNull AlwaysOnHotwordDetector detector) {
+        synchronized (mLock) {
+            mHotwordDetector = null;
         }
-
-        try {
-            mHotwordDetector.stopRecognition();
-        } catch (Exception ex) {
-            // Ignore.
-        }
-
-        try {
-            mHotwordDetector.invalidate();
-        } catch (Exception ex) {
-            // Ignore.
-        }
-
-        mHotwordDetector = null;
     }
 
-    private void shutdownMicrophoneHotwordDetectorLocked() {
-        if (mSoftwareHotwordDetector == null) {
-            return;
+    private void onMicrophoneHotwordDetectorDestroyed(@NonNull SoftwareHotwordDetector detector) {
+        synchronized (mLock) {
+            mSoftwareHotwordDetector = null;
         }
-
-        try {
-            mSoftwareHotwordDetector.stopRecognition();
-        } catch (Exception ex) {
-            // Ignore.
-        }
-
-        try {
-            mSystemService.shutdownHotwordDetectionService();
-        } catch (Exception ex) {
-            // Ignore.
-        }
-
-        mSoftwareHotwordDetector = null;
     }
 
     /**
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index c91851a..a2938a8 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -26,6 +26,8 @@
 import static android.view.View.SYSTEM_UI_FLAG_VISIBLE;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
@@ -890,8 +892,6 @@
          * @param dimAmount Float amount between [0.0, 1.0] to dim the wallpaper.
          */
         private void updateWallpaperDimming(float dimAmount) {
-            mPreviousWallpaperDimAmount = mWallpaperDimAmount;
-
             // Custom dim amount cannot be less than the default dim amount.
             mWallpaperDimAmount = Math.max(mDefaultDimAmount, dimAmount);
             // If dim amount is 0f (additional dimming is removed), then the wallpaper should dim
@@ -909,15 +909,15 @@
             SurfaceControl.Transaction surfaceControlTransaction = new SurfaceControl.Transaction();
             // TODO: apply the dimming to preview as well once surface transparency works in
             // preview mode.
-            if (!isPreview() && mShouldDim) {
+            if ((!isPreview() && mShouldDim)
+                    || mPreviousWallpaperDimAmount != mWallpaperDimAmount) {
                 Log.v(TAG, "Setting wallpaper dimming: " + mWallpaperDimAmount);
 
                 // Animate dimming to gradually change the wallpaper alpha from the previous
                 // dim amount to the new amount only if the dim amount changed.
                 ValueAnimator animator = ValueAnimator.ofFloat(
                         mPreviousWallpaperDimAmount, mWallpaperDimAmount);
-                animator.setDuration(mPreviousWallpaperDimAmount == mWallpaperDimAmount
-                        ? 0 : DIMMING_ANIMATION_DURATION_MS);
+                animator.setDuration(DIMMING_ANIMATION_DURATION_MS);
                 animator.addUpdateListener((ValueAnimator va) -> {
                     final float dimValue = (float) va.getAnimatedValue();
                     if (mBbqSurfaceControl != null) {
@@ -925,11 +925,19 @@
                                 .setAlpha(mBbqSurfaceControl, 1 - dimValue).apply();
                     }
                 });
+                animator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        updateSurface(false, false, true);
+                    }
+                });
                 animator.start();
             } else {
                 Log.v(TAG, "Setting wallpaper dimming: " + 0);
                 surfaceControlTransaction.setAlpha(mBbqSurfaceControl, 1.0f).apply();
             }
+
+            mPreviousWallpaperDimAmount = mWallpaperDimAmount;
         }
 
         /**
@@ -1332,6 +1340,7 @@
                             resetWindowPages();
                             mSession.finishDrawing(mWindow, null /* postDrawTransaction */);
                             processLocalColors(mPendingXOffset, mPendingXOffsetStep);
+                            notifyColorsChanged();
                         }
                         reposition();
                         reportEngineShown(shouldWaitForEngineShown());
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 3cd1e0f..ce54968 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -3700,21 +3700,32 @@
         /**
          * Sets the buffer transform that should be applied to the current buffer.
          *
+         * This can be used in combination with
+         * {@link AttachedSurfaceControl#addOnBufferTransformHintChangedListener(AttachedSurfaceControl.OnBufferTransformHintChangedListener)}
+         * to pre-rotate the buffer for the current display orientation. This can
+         * improve the performance of displaying the associated buffer.
+         *
          * @param sc The SurfaceControl to update
          * @param transform The transform to apply to the buffer.
          * @return this
          */
         public @NonNull Transaction setBufferTransform(@NonNull SurfaceControl sc,
-                /* TODO: Mark the intdef */ int transform) {
+                @SurfaceControl.BufferTransform int transform) {
             checkPreconditions(sc);
             nativeSetBufferTransform(mNativeObject, sc.mNativeObject, transform);
             return this;
         }
 
         /**
-         * Updates the region for the content on this surface updated in this transaction.
+         * Updates the region for the content on this surface updated in this transaction. The
+         * damage region is the area of the buffer that has changed since the previously
+         * sent buffer. This can be used to reduce the amount of recomposition that needs
+         * to happen when only a small region of the buffer is being updated, such as for
+         * a small blinking cursor or a loading indicator.
          *
-         * If unspecified, the complete surface is assumed to be damaged.
+         * @param sc The SurfaceControl on which to set the damage region
+         * @param region The region to set. If null, the entire buffer is assumed dirty. This is
+         *               equivalent to not setting a damage region at all.
          */
         public @NonNull Transaction setDamageRegion(@NonNull SurfaceControl sc,
                 @Nullable Region region) {
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 3fb0fe7..2edfda5 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -28,9 +28,9 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.util.Log;
-import android.view.accessibility.IAccessibilityEmbeddedConnection;
 import android.view.InsetsState;
 import android.view.WindowManagerGlobal;
+import android.view.accessibility.IAccessibilityEmbeddedConnection;
 
 import java.util.Objects;
 
@@ -428,4 +428,11 @@
         WindowManagerGlobal.getInstance().removeWindowlessRoot(mViewRoot);
         mReleased = true;
     }
+
+    /**
+     * @hide
+     */
+    public IBinder getFocusGrantToken() {
+        return mWm.getFocusGrantToken();
+    }
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3ba305b..8236fbb 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1794,10 +1794,16 @@
     }
 
     void pokeDrawLockIfNeeded() {
-        final int displayState = mAttachInfo.mDisplayState;
-        if (mView != null && mAdded && mTraversalScheduled
-                && (displayState == Display.STATE_DOZE
-                        || displayState == Display.STATE_DOZE_SUSPEND)) {
+        if (!Display.isDozeState(mAttachInfo.mDisplayState)) {
+            // Only need to acquire wake lock for DOZE state.
+            return;
+        }
+        if (mWindowAttributes.type != WindowManager.LayoutParams.TYPE_BASE_APPLICATION) {
+            // Non-activity windows should be responsible to hold wake lock by themself, because
+            // usually they are system windows.
+            return;
+        }
+        if (mAdded && mTraversalScheduled && mAttachInfo.mHasWindowFocus) {
             try {
                 mWindowSession.pokeDrawLock(mWindow);
             } catch (RemoteException ex) {
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 60ccf67..b7994db 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -3100,11 +3100,51 @@
     }
 
     /**
-     * Checks the id of autofill whether supported the fill dialog.
+     * If autofill suggestions for a dialog-style UI are available for {@code view}, shows a dialog
+     * allowing the user to select a suggestion and returns {@code true}.
+     * <p>
+     * The dialog may not be available if the autofill service does not support it, or if the
+     * autofill request has not returned a response yet.
+     * <p>
+     * It is recommended to call this method the first time a user focuses on an autofill-able form,
+     * and to avoid showing the input method if the dialog is shown. If this method returns
+     * {@code false}, you should then instead show the input method (assuming that is how the
+     * view normally handles the focus event). If the user re-focuses on the view, you should not
+     * call this method again so as to not disrupt usage of the input method.
      *
-     * @hide
+     * @param view the view for which to show autofill suggestions. This is typically a view
+     *             receiving a focus event. The autofill suggestions shown will include content for
+     *             related views as well.
+     * @return {@code true} if the autofill dialog is being shown
      */
-    public boolean isShowFillDialog(AutofillId id) {
+    // TODO(b/210926084): Consider whether to include the one-time show logic within this method.
+    public boolean showAutofillDialog(@NonNull View view) {
+        Objects.requireNonNull(view);
+        if (shouldShowAutofillDialog(view.getAutofillId())) {
+            // If the id matches a trigger id, this will trigger the fill dialog.
+            notifyViewEntered(view);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Like {@link #showAutofillDialog(View)} but for virtual views.
+     *
+     * @param virtualId id identifying the virtual child inside the parent view.
+     */
+    // TODO(b/210926084): Consider whether to include the one-time show logic within this method.
+    public boolean showAutofillDialog(@NonNull View view, int virtualId) {
+        Objects.requireNonNull(view);
+        if (shouldShowAutofillDialog(getAutofillId(view, virtualId))) {
+            // If the id matches a trigger id, this will trigger the fill dialog.
+            notifyViewEntered(view, virtualId, /* bounds= */ null, /* flags= */ 0);
+            return true;
+        }
+        return false;
+    }
+
+    private boolean shouldShowAutofillDialog(AutofillId id) {
         if (!hasFillDialogUiFeature() || mFillDialogTriggerIds == null) {
             return false;
         }
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 7e6e6fd..9a70667 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -278,6 +278,7 @@
                                     .InputMethod_Subtype_subtypeId, 0 /* use Arrays.hashCode */))
                             .setIsAsciiCapable(a.getBoolean(com.android.internal.R.styleable
                                     .InputMethod_Subtype_isAsciiCapable, false)).build();
+                    a.recycle();
                     if (!subtype.isAuxiliary()) {
                         isAuxIme = false;
                     }
diff --git a/core/java/com/android/internal/widget/SubtitleView.java b/core/java/com/android/internal/widget/SubtitleView.java
index 21e63c5..4d6151d 100644
--- a/core/java/com/android/internal/widget/SubtitleView.java
+++ b/core/java/com/android/internal/widget/SubtitleView.java
@@ -115,6 +115,7 @@
                     break;
             }
         }
+        a.recycle();
 
         // Set up density-dependent properties.
         // TODO: Move these to a default style.
diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto
index 5c6116a..279a5d0 100644
--- a/core/proto/android/content/package_item_info.proto
+++ b/core/proto/android/content/package_item_info.proto
@@ -115,4 +115,5 @@
     }
     optional Detail detail = 17;
     repeated string overlay_paths = 18;
+    repeated string known_activity_embedding_certs = 19;
 }
diff --git a/core/proto/android/internal/binder_latency.proto b/core/proto/android/internal/binder_latency.proto
index 8b11f5b..edd9b71 100644
--- a/core/proto/android/internal/binder_latency.proto
+++ b/core/proto/android/internal/binder_latency.proto
@@ -35,6 +35,7 @@
     SYSTEM_SERVER = 1;
     TELEPHONY = 2;
     BLUETOOTH = 3;
+    WIFI = 4;
   }
 
   enum ServiceClassName {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 19c1325..0c41f31 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1905,6 +1905,13 @@
     <permission android:name="android.permission.MANAGE_WIFI_AUTO_JOIN"
                 android:protectionLevel="signature|privileged" />
 
+    <!-- Allows applications to get notified when a Wi-Fi interface request cannot
+         be satisfied without tearing down one or more other interfaces, and provide a decision
+         whether to approve the request or reject it.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.MANAGE_WIFI_INTERFACES"
+                android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi @hide Allows apps to create and manage IPsec tunnels.
          <p>Only granted to applications that are currently bound by the
          system for creating and managing IPsec-based interfaces.
@@ -6629,6 +6636,14 @@
                   android:exported="false">
         </activity>
 
+        <activity android:name="com.android.server.logcat.LogAccessConfirmationActivity"
+                  android:theme="@style/Theme.Dialog.Confirmation"
+                  android:excludeFromRecents="true"
+                  android:process=":ui"
+                  android:label="@string/log_access_confirmation_title"
+                  android:exported="false">
+        </activity>
+
         <activity android:name="com.android.server.notification.NASLearnMoreActivity"
                   android:theme="@style/Theme.Dialog.Confirmation"
                   android:excludeFromRecents="true"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index cb40e86..6dc975b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1048,6 +1048,24 @@
          <p>The default value of this attribute is <code>false</code>. -->
     <attr name="allowEmbedded" format="boolean" />
 
+    <!-- A reference to an array resource containing the signing certificate digests, one of which a
+         client is required to be signed with in order to embed the activity. If the client is not
+         signed with one of the certificates in the set, and the activity does not allow embedding
+         by untrusted hosts via {@link android.R.attr#allowUntrustedActivityEmbedding} flag, the
+         embedding request will fail.
+         <p>The digest should be computed over the DER encoding of the trusted certificate using the
+         SHA-256 digest algorithm.
+         <p>If only a single signer is declared this can also be a string resource, or the digest
+         can be declared inline as the value for this attribute.
+         <p>If the attribute is declared both on the application and the activity level, the value
+         on the activity level takes precedence. -->
+    <attr name="knownActivityEmbeddingCerts" format="reference|string" />
+
+    <!-- Indicate that the activity can be embedded by untrusted hosts. In this case the
+         interactions and visibility of the embedded activity may be limited.
+         <p>The default value of this attribute is <code>false</code>. -->
+    <attr name="allowUntrustedActivityEmbedding" format="boolean" />
+
     <!-- Specifies whether this {@link android.app.Activity} should be shown on
          top of the lock screen whenever the lockscreen is up and this activity has another
          activity behind it with the {@link android.R.attr#showWhenLocked} attribute set. That
@@ -2011,6 +2029,7 @@
              when the application's user data is cleared. The default value is false.
         -->
         <attr name="resetEnabledSettingsOnAppDataCleared" format="boolean" />
+        <attr name="knownActivityEmbeddingCerts" />
     </declare-styleable>
 
     <!-- An attribution is a logical part of an app and is identified by a tag.
@@ -3033,6 +3052,8 @@
         <!-- Indicates whether the activity can be displayed on a remote device which may or
              may not be running Android. -->
         <attr name="canDisplayOnRemoteDevices" format="boolean"/>
+        <attr name="allowUntrustedActivityEmbedding" />
+        <attr name="knownActivityEmbeddingCerts" />
     </declare-styleable>
 
     <!-- The <code>activity-alias</code> tag declares a new
@@ -3073,6 +3094,8 @@
         <attr name="exported" />
         <attr name="parentActivityName" />
         <attr name="attributionTags" />
+        <attr name="allowUntrustedActivityEmbedding" />
+        <attr name="knownActivityEmbeddingCerts" />
     </declare-styleable>
 
     <!-- The <code>meta-data</code> tag is used to attach additional
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 775527d..6eb19e1 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4276,6 +4276,9 @@
     <!-- URI for in call notification sound -->
     <string translatable="false" name="config_inCallNotificationSound">/product/media/audio/ui/InCallNotification.ogg</string>
 
+    <!-- URI for camera shutter sound -->
+    <string translatable="false" name="config_cameraShutterSound">/product/media/audio/ui/camera_click.ogg</string>
+
     <!-- URI for default ringtone sound file to be used for silent ringer vibration -->
     <string translatable="false" name="config_defaultRingtoneVibrationSound"></string>
 
@@ -5697,4 +5700,75 @@
      -->
     <string-array name="config_dockExtconStateMapping">
     </string-array>
+
+    <!-- Whether or not the monitoring on the apps' background battery drain is enabled -->
+    <bool name="config_bg_current_drain_monitor_enabled">true</bool>
+
+    <!-- The threshold of the background current drain (in percentage) to the restricted
+         standby bucket.
+    -->
+    <array name="config_bg_current_drain_threshold_to_restricted_bucket">
+        <item>2.0</item> <!-- regular device -->
+        <item>4.0</item> <!-- low ram device -->
+    </array>
+
+    <!-- The threshold of the background current drain (in percentage) to the background
+         restricted level.
+    -->
+    <array name="config_bg_current_drain_threshold_to_bg_restricted">
+        <item>4.0</item> <!-- regular device -->
+        <item>8.0</item> <!-- low ram device -->
+    </array>
+
+    <!-- The background current drain monitoring window size. -->
+    <integer name="config_bg_current_drain_window">86400</integer>
+
+    <!-- The types of battery drain we're checking on each app; if the sum of the battery drain
+        exceeds the threshold, it'll be moved to restricted standby bucket. The value must be
+        one of or combination of the definitions in AppBatteryPolicy.
+    -->
+    <integer name="config_bg_current_drain_types_to_restricted_bucket">4</integer>
+
+    <!-- The types of battery drain we're checking on each app; if the sum of the battery drain
+        exceeds the threshold, it'll be moved to background restricted level. The value must be
+        one of or combination of the definitions in AppBatteryPolicy.
+    -->
+    <integer name="config_bg_current_drain_types_to_bg_restricted">12</integer>
+
+    <!-- The power usage components we're monitoring. Must one of the definition in BatteryConsumer.
+    -->
+    <integer name="config_bg_current_drain_power_components">-1</integer>
+
+    <!-- Whether or not enable the different threshold based on the durations of
+         certain event type.
+    -->
+    <bool name="config_bg_current_drain_event_duration_based_threshold_enabled">false</bool>
+
+    <!-- The threshold of the background current drain (in percentage) to the restricted
+         standby bucket for legitimate case with higher background current drain.
+    -->
+    <array name="config_bg_current_drain_high_threshold_to_restricted_bucket">
+        <item>30.0</item> <!-- regular device -->
+        <item>60.0</item> <!-- low ram device -->
+    </array>
+
+    <!-- The threshold of the background current drain (in percentage) to the background
+         restricted level for legitimate case with higher background current drain.
+    -->
+    <array name="config_bg_current_drain_high_threshold_to_bg_restricted">
+        <item>20.0</item> <!-- regular device -->
+        <item>40.0</item> <!-- low ram device -->
+    </array>
+
+    <!-- The threshold of minimal time of hosting a foreground service with type "mediaPlayback"
+         or a media session, over the given window, so it'd subject towards the higher background
+         current drain threshold.
+    -->
+    <integer name="config_bg_current_drain_media_playback_min_duration">1800</integer>
+
+    <!-- The threshold of minimal time of hosting a foreground service with type "location"
+         over the given window, so it'd subject towards the higher background
+         current drain threshold.
+    -->
+    <integer name="config_bg_current_drain_location_min_duration">1800</integer>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index cc63fd6..3d80ca8 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3276,6 +3276,8 @@
     <public name="toExtendBottom" />
     <public name="tileService" />
     <public name="windowSplashScreenBehavior" />
+    <public name="allowUntrustedActivityEmbedding" />
+    <public name="knownActivityEmbeddingCerts" />
   </staging-public-group>
 
   <staging-public-group type="id" first-id="0x01de0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cb209ab1..e41aa45 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5719,6 +5719,20 @@
     <!-- Title for the harmful app warning dialog. [CHAR LIMIT=40] -->
     <string name="harmful_app_warning_title">Harmful app detected</string>
 
+    <!-- Title for the log access confirmation dialog. [CHAR LIMIT=40] -->
+    <string name="log_access_confirmation_title">System log access request</string>
+    <!-- Label for the allow button on the log access confirmation dialog. [CHAR LIMIT=20] -->
+    <string name="log_access_confirmation_allow">Only this time</string>
+    <!-- Label for the deny button on the log access confirmation dialog. [CHAR LIMIT=20] -->
+    <string name="log_access_confirmation_deny">Don\u2019t allow</string>
+
+    <!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]-->
+    <string name="log_access_confirmation_body"><xliff:g id="log_access_app_name" example="Example App">%s</xliff:g> requests system logs for functional debugging.
+        These logs might contain information that apps and services on your device have written.</string>
+
+    <!-- Privacy notice do not show [CHAR LIMIT=20] -->
+    <string name="log_access_do_not_show_again">Don\u2019t show again</string>
+
     <!-- Text describing a permission request for one app to show another app's
          slices [CHAR LIMIT=NONE] -->
     <string name="slices_permission_request"><xliff:g id="app" example="Example App">%1$s</xliff:g> wants to show <xliff:g id="app_2" example="Other Example App">%2$s</xliff:g> slices</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e10ed24..c63e543 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3749,6 +3749,7 @@
   <java-symbol type="bool" name="config_handleVolumeAliasesUsingVolumeGroups" />
   <java-symbol type="dimen" name="config_inCallNotificationVolume" />
   <java-symbol type="string" name="config_inCallNotificationSound" />
+  <java-symbol type="string" name="config_cameraShutterSound" />
   <java-symbol type="integer" name="config_autoGroupAtCount" />
   <java-symbol type="bool" name="config_dozeAlwaysOnDisplayAvailable" />
   <java-symbol type="bool" name="config_dozeAlwaysOnEnabled" />
@@ -3868,6 +3869,11 @@
   <java-symbol type="string" name="harmful_app_warning_title" />
   <java-symbol type="layout" name="harmful_app_warning_dialog" />
 
+  <java-symbol type="string" name="log_access_confirmation_allow" />
+  <java-symbol type="string" name="log_access_confirmation_deny" />
+  <java-symbol type="string" name="log_access_confirmation_title" />
+  <java-symbol type="string" name="log_access_confirmation_body" />
+
   <java-symbol type="string" name="config_defaultAssistantAccessComponent" />
 
   <java-symbol type="string" name="slices_permission_request" />
@@ -4728,4 +4734,17 @@
   <java-symbol type="string" name="vdm_camera_access_denied" />
 
   <java-symbol type="color" name="camera_privacy_light"/>
+
+  <java-symbol type="bool" name="config_bg_current_drain_monitor_enabled" />
+  <java-symbol type="array" name="config_bg_current_drain_threshold_to_restricted_bucket" />
+  <java-symbol type="array" name="config_bg_current_drain_threshold_to_bg_restricted" />
+  <java-symbol type="integer" name="config_bg_current_drain_window" />
+  <java-symbol type="integer" name="config_bg_current_drain_types_to_restricted_bucket" />
+  <java-symbol type="integer" name="config_bg_current_drain_types_to_bg_restricted" />
+  <java-symbol type="integer" name="config_bg_current_drain_power_components" />
+  <java-symbol type="bool" name="config_bg_current_drain_event_duration_based_threshold_enabled" />
+  <java-symbol type="array" name="config_bg_current_drain_high_threshold_to_restricted_bucket" />
+  <java-symbol type="array" name="config_bg_current_drain_high_threshold_to_bg_restricted" />
+  <java-symbol type="integer" name="config_bg_current_drain_media_playback_min_duration" />
+  <java-symbol type="integer" name="config_bg_current_drain_location_min_duration" />
 </resources>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 8f368c2..d35ecbd 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -924,6 +924,7 @@
      * Checks if an activity is embedded and its presentation is customized by a
      * {@link android.window.TaskFragmentOrganizer} to only occupy a portion of Task bounds.
      */
+    @Override
     public boolean isActivityEmbedded(@NonNull Activity activity) {
         return mPresenter.isActivityEmbedded(activity.getActivityToken());
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 1039e2a..51067a4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -191,6 +191,19 @@
         return null;
     }
 
+    /**
+     * Gets a token associated with the view that can be used to grant the view focus.
+     */
+    public IBinder getFocusGrantToken(View view) {
+        SurfaceControlViewHost root = mViewRoots.get(view);
+        if (root == null) {
+            Slog.e(TAG, "Couldn't get focus grant token since view does not exist in "
+                    + "SystemWindow:" + view);
+            return null;
+        }
+        return root.getFocusGrantToken();
+    }
+
     private class PerDisplay {
         final int mDisplayId;
         private final SparseArray<SysUiWindowManager> mWwms = new SparseArray<>();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 6ec8f5b..71cff02 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -28,7 +28,6 @@
 import android.graphics.RectF;
 import android.os.Debug;
 import android.os.Handler;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Size;
@@ -126,7 +125,6 @@
     private int mMenuState;
 
     private PipMenuView mPipMenuView;
-    private IBinder mPipMenuInputToken;
 
     private ActionListener mMediaActionListener = new ActionListener() {
         @Override
@@ -206,7 +204,6 @@
         mApplier = null;
         mSystemWindows.removeView(mPipMenuView);
         mPipMenuView = null;
-        mPipMenuInputToken = null;
     }
 
     /**
@@ -392,7 +389,6 @@
 
         if (mApplier == null) {
             mApplier = new SyncRtSurfaceTransactionApplier(mPipMenuView);
-            mPipMenuInputToken = mPipMenuView.getViewRootImpl().getInputToken();
         }
 
         return mApplier != null;
@@ -539,7 +535,8 @@
 
             try {
                 WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
-                        mPipMenuInputToken, menuState != MENU_STATE_NONE /* grantFocus */);
+                        mSystemWindows.getFocusGrantToken(mPipMenuView),
+                        menuState != MENU_STATE_NONE /* grantFocus */);
             } catch (RemoteException e) {
                 Log.e(TAG, "Unable to update focus as menu appears/disappears", e);
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index 32861b6..f838a0b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -143,6 +143,7 @@
         mSystemWindows.addView(mPipMenuView,
                 getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
                 0, SHELL_ROOT_LAYER_PIP);
+        mPipMenuView.setFocusGrantToken(mSystemWindows.getFocusGrantToken(mPipMenuView));
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index 0141b6a..84eae9e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -28,6 +28,7 @@
 import android.app.RemoteAction;
 import android.content.Context;
 import android.os.Handler;
+import android.os.IBinder;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
@@ -69,6 +70,7 @@
     private final ImageView mArrowRight;
     private final ImageView mArrowDown;
     private final ImageView mArrowLeft;
+    private IBinder mFocusGrantToken = null;
 
     public TvPipMenuView(@NonNull Context context) {
         this(context, null);
@@ -108,6 +110,10 @@
         mListener = listener;
     }
 
+    void setFocusGrantToken(IBinder token) {
+        mFocusGrantToken = token;
+    }
+
     void show(boolean inMoveMode, int gravity) {
         if (DEBUG) Log.d(TAG, "show(), inMoveMode: " + inMoveMode);
         grantWindowFocus(true);
@@ -162,7 +168,7 @@
 
         try {
             WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
-                    getViewRootImpl().getInputToken(), grantFocus);
+                    mFocusGrantToken, grantFocus);
         } catch (Exception e) {
             Log.e(TAG, "Unable to update focus", e);
         }
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 219530b..029d073 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
@@ -163,6 +163,7 @@
     // and exit, since exit itself can trigger a number of changes that update the stages.
     private boolean mShouldUpdateRecents;
     private boolean mExitSplitScreenOnHide;
+    private boolean mIsDividerRemoteAnimating;
 
     /** The target stage to dismiss to when unlock after folded. */
     @StageType
@@ -389,6 +390,7 @@
                     RemoteAnimationTarget[] wallpapers,
                     RemoteAnimationTarget[] nonApps,
                     final IRemoteAnimationFinishedCallback finishedCallback) {
+                mIsDividerRemoteAnimating = true;
                 RemoteAnimationTarget[] augmentedNonApps =
                         new RemoteAnimationTarget[nonApps.length + 1];
                 for (int i = 0; i < nonApps.length; ++i) {
@@ -400,6 +402,7 @@
                         new IRemoteAnimationFinishedCallback.Stub() {
                             @Override
                             public void onAnimationFinished() throws RemoteException {
+                                mIsDividerRemoteAnimating = false;
                                 mShouldUpdateRecents = true;
                                 mSyncQueue.queue(evictWct);
                                 mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
@@ -423,6 +426,7 @@
 
             @Override
             public void onAnimationCancelled() {
+                mIsDividerRemoteAnimating = false;
                 mShouldUpdateRecents = true;
                 mSyncQueue.queue(evictWct);
                 mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
@@ -467,6 +471,7 @@
 
         // Using legacy transitions, so we can't use blast sync since it conflicts.
         mTaskOrganizer.applyTransaction(wct);
+        mSyncQueue.runInSync(t -> updateSurfaceBounds(mSplitLayout, t));
     }
 
     /** Start an intent and a task ordered by {@code intentFirst}. */
@@ -1055,7 +1060,7 @@
 
     private void applyDividerVisibility(SurfaceControl.Transaction t) {
         final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
-        if (dividerLeash == null) return;
+        if (mIsDividerRemoteAnimating || dividerLeash == null) return;
 
         if (mDividerVisible) {
             t.show(dividerLeash);
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
index 4ebaf2b..a48f94b 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
@@ -20,6 +20,7 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -86,6 +87,7 @@
 
     final int mType;
     final int mRatType;
+    final int mSubId;
     final String mSubscriberId;
     final String mWifiNetworkKey;
     final boolean mRoaming;
@@ -96,7 +98,7 @@
     /** @hide */
     public NetworkIdentity(
             int type, int ratType, @Nullable String subscriberId, @Nullable String wifiNetworkKey,
-            boolean roaming, boolean metered, boolean defaultNetwork, int oemManaged) {
+            boolean roaming, boolean metered, boolean defaultNetwork, int oemManaged, int subId) {
         mType = type;
         mRatType = ratType;
         mSubscriberId = subscriberId;
@@ -105,12 +107,13 @@
         mMetered = metered;
         mDefaultNetwork = defaultNetwork;
         mOemManaged = oemManaged;
+        mSubId = subId;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mType, mRatType, mSubscriberId, mWifiNetworkKey, mRoaming, mMetered,
-                mDefaultNetwork, mOemManaged);
+                mDefaultNetwork, mOemManaged, mSubId);
     }
 
     @Override
@@ -122,7 +125,8 @@
                     && Objects.equals(mWifiNetworkKey, ident.mWifiNetworkKey)
                     && mMetered == ident.mMetered
                     && mDefaultNetwork == ident.mDefaultNetwork
-                    && mOemManaged == ident.mOemManaged;
+                    && mOemManaged == ident.mOemManaged
+                    && mSubId == ident.mSubId;
         }
         return false;
     }
@@ -150,6 +154,7 @@
         builder.append(", metered=").append(mMetered);
         builder.append(", defaultNetwork=").append(mDefaultNetwork);
         builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
+        builder.append(", subId=").append(mSubId);
         return builder.append("}").toString();
     }
 
@@ -256,6 +261,11 @@
         return mOemManaged;
     }
 
+    /** Get the SubId of this instance. */
+    public int getSubId() {
+        return mSubId;
+    }
+
     /**
      * Assemble a {@link NetworkIdentity} from the passed arguments.
      *
@@ -276,7 +286,8 @@
     public static NetworkIdentity buildNetworkIdentity(Context context,
             @NonNull NetworkStateSnapshot snapshot, boolean defaultNetwork, int ratType) {
         final NetworkIdentity.Builder builder = new NetworkIdentity.Builder()
-                .setNetworkStateSnapshot(snapshot).setDefaultNetwork(defaultNetwork);
+                .setNetworkStateSnapshot(snapshot).setDefaultNetwork(defaultNetwork)
+                .setSubId(snapshot.getSubId());
         if (snapshot.getLegacyType() == TYPE_MOBILE && ratType != NETWORK_TYPE_ALL) {
             builder.setRatType(ratType);
         }
@@ -325,6 +336,9 @@
         if (res == 0) {
             res = Integer.compare(left.mOemManaged, right.mOemManaged);
         }
+        if (res == 0) {
+            res = Integer.compare(left.mSubId, right.mSubId);
+        }
         return res;
     }
 
@@ -345,6 +359,7 @@
         private boolean mMetered;
         private boolean mDefaultNetwork;
         private int mOemManaged;
+        private int mSubId;
 
         /**
          * Creates a new Builder.
@@ -359,6 +374,7 @@
             mMetered = false;
             mDefaultNetwork = false;
             mOemManaged = NetworkTemplate.OEM_MANAGED_NO;
+            mSubId = INVALID_SUBSCRIPTION_ID;
         }
 
         /**
@@ -537,6 +553,19 @@
             return this;
         }
 
+        /**
+         * Set the Subscription Id.
+         *
+         * @param subId the Subscription Id of the network. Or INVALID_SUBSCRIPTION_ID if not
+         *              applicable.
+         * @return this builder.
+         */
+        @NonNull
+        public Builder setSubId(int subId) {
+            mSubId = subId;
+            return this;
+        }
+
         private void ensureValidParameters() {
             // Assert non-mobile network cannot have a ratType.
             if (mType != TYPE_MOBILE && mRatType != NetworkTemplate.NETWORK_TYPE_ALL) {
@@ -559,7 +588,7 @@
         public NetworkIdentity build() {
             ensureValidParameters();
             return new NetworkIdentity(mType, mRatType, mSubscriberId, mWifiNetworkKey,
-                    mRoaming, mMetered, mDefaultNetwork, mOemManaged);
+                    mRoaming, mMetered, mDefaultNetwork, mOemManaged, mSubId);
         }
     }
 }
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
index 2236d70..56461ba 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import android.annotation.NonNull;
 import android.service.NetworkIdentitySetProto;
@@ -42,6 +43,7 @@
     private static final int VERSION_ADD_METERED = 4;
     private static final int VERSION_ADD_DEFAULT_NETWORK = 5;
     private static final int VERSION_ADD_OEM_MANAGED_NETWORK = 6;
+    private static final int VERSION_ADD_SUB_ID = 7;
 
     /**
      * Construct a {@link NetworkIdentitySet} object.
@@ -103,8 +105,15 @@
                 oemNetCapabilities = NetworkIdentity.OEM_NONE;
             }
 
+            final int subId;
+            if (version >= VERSION_ADD_SUB_ID) {
+                subId = in.readInt();
+            } else {
+                subId = INVALID_SUBSCRIPTION_ID;
+            }
+
             add(new NetworkIdentity(type, ratType, subscriberId, networkId, roaming, metered,
-                    defaultNetwork, oemNetCapabilities));
+                    defaultNetwork, oemNetCapabilities, subId));
         }
     }
 
@@ -113,7 +122,7 @@
      * @hide
      */
     public void writeToStream(DataOutput out) throws IOException {
-        out.writeInt(VERSION_ADD_OEM_MANAGED_NETWORK);
+        out.writeInt(VERSION_ADD_SUB_ID);
         out.writeInt(size());
         for (NetworkIdentity ident : this) {
             out.writeInt(ident.getType());
@@ -124,6 +133,7 @@
             out.writeBoolean(ident.isMetered());
             out.writeBoolean(ident.isDefaultNetwork());
             out.writeInt(ident.getOemManaged());
+            out.writeInt(ident.getSubId());
         }
     }
 
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java
index d577aa8..c018e91 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.java
@@ -17,6 +17,8 @@
 package android.net;
 
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -98,12 +100,29 @@
         return mLinkProperties;
     }
 
-    /** Get the Subscriber Id of the network associated with this snapshot. */
+    /**
+     * Get the Subscriber Id of the network associated with this snapshot.
+     * @deprecated Please use #getSubId, which doesn't return personally identifiable
+     * information.
+     */
+    @Deprecated
     @Nullable
     public String getSubscriberId() {
         return mSubscriberId;
     }
 
+    /** Get the subId of the network associated with this snapshot. */
+    public int getSubId() {
+        if (mNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+            final NetworkSpecifier spec = mNetworkCapabilities.getNetworkSpecifier();
+            if (spec instanceof TelephonyNetworkSpecifier) {
+                return ((TelephonyNetworkSpecifier) spec).getSubscriptionId();
+            }
+        }
+        return INVALID_SUBSCRIPTION_ID;
+    }
+
+
     /**
      * Get the legacy type of the network associated with this snapshot.
      * @return the legacy network type. See {@code ConnectivityManager#TYPE_*}.
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index 78ba218..e8b3d4c 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -1484,10 +1484,15 @@
                         NetworkCapabilities.NET_CAPABILITY_IMS) && !ident.isMetered()) {
 
                     // Copy the identify from IMS one but mark it as metered.
-                    NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
-                            ident.getRatType(), ident.getSubscriberId(), ident.getWifiNetworkKey(),
-                            ident.isRoaming(), true /* metered */,
-                            true /* onDefaultNetwork */, ident.getOemManaged());
+                    NetworkIdentity vtIdent = new NetworkIdentity.Builder()
+                            .setType(ident.getType())
+                            .setRatType(ident.getRatType())
+                            .setSubscriberId(ident.getSubscriberId())
+                            .setWifiNetworkKey(ident.getWifiNetworkKey())
+                            .setRoaming(ident.isRoaming()).setMetered(true)
+                            .setDefaultNetwork(true)
+                            .setOemManaged(ident.getOemManaged())
+                            .setSubId(ident.getSubId()).build();
                     final String ifaceVt = IFACE_VT + getSubIdForMobile(snapshot);
                     findOrCreateNetworkIdentitySet(mActiveIfaces, ifaceVt).add(vtIdent);
                     findOrCreateNetworkIdentitySet(mActiveUidIfaces, ifaceVt).add(vtIdent);
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index 0102a97..4e06ed7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -448,21 +448,26 @@
         uiContext.startActivity(intent);
     }
 
-    public void preview(DreamInfo dreamInfo) {
-        logd("preview(%s)", dreamInfo);
-        if (mDreamManager == null || dreamInfo == null || dreamInfo.componentName == null)
+    /**
+     * Preview a dream, given the component name.
+     */
+    public void preview(ComponentName componentName) {
+        logd("preview(%s)", componentName);
+        if (mDreamManager == null || componentName == null) {
             return;
+        }
         try {
-            mDreamManager.testDream(mContext.getUserId(), dreamInfo.componentName);
+            mDreamManager.testDream(mContext.getUserId(), componentName);
         } catch (RemoteException e) {
-            Log.w(TAG, "Failed to preview " + dreamInfo, e);
+            Log.w(TAG, "Failed to preview " + componentName, e);
         }
     }
 
     public void startDreaming() {
         logd("startDreaming()");
-        if (mDreamManager == null)
+        if (mDreamManager == null) {
             return;
+        }
         try {
             mDreamManager.dream();
         } catch (RemoteException e) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
index 484ca93..1f4cafce 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
@@ -221,8 +221,8 @@
             setSwitchEnabled(false);
         } else if (!mIsAllowedByOrganization) {
             EnforcedAdmin admin =
-                    RestrictedLockUtilsInternal.checkIfInputMethodDisallowed(getContext(),
-                            mImi.getPackageName(), UserHandle.myUserId());
+                    RestrictedLockUtilsInternal.checkIfInputMethodDisallowed(
+                            getContext(), mImi.getPackageName(), mUserId);
             setDisabledByAdmin(admin);
         } else {
             setEnabled(true);
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
index b4c95e6..2c2be03 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
@@ -39,6 +39,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
 import java.util.zip.GZIPInputStream;
 
 /**
@@ -54,6 +56,7 @@
     private static final String TAG_FILE_NAME = "file-name";
     private static final String TAG_FILE_CONTENT = "file-content";
     private static final String ATTR_CONTENT_ID = "contentId";
+    private static final String ATTR_LIBRARY_NAME = "lib";
 
     private static final String HTML_HEAD_STRING =
             "<html><head>\n"
@@ -67,8 +70,12 @@
             + "</style>\n"
             + "</head>"
             + "<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n"
-            + "<div class=\"toc\">\n"
-            + "<ul>";
+            + "<div class=\"toc\">\n";
+    private static final String LIBRARY_HEAD_STRING =
+            "<strong>Libraries</strong>\n<ul class=\"libraries\">";
+    private static final String LIBRARY_TAIL_STRING = "</ul>\n<strong>Files</strong>";
+
+    private static final String FILES_HEAD_STRING = "<ul class=\"files\">";
 
     private static final String HTML_MIDDLE_STRING =
             "</ul>\n"
@@ -81,12 +88,14 @@
     private final List<File> mXmlFiles;
 
     /*
-     * A map from a file name to a content id (MD5 sum of file content) for its license.
-     * For example, "/system/priv-app/TeleService/TeleService.apk" maps to
+     * A map from a file name to a library name (may be empty) to a content id (MD5 sum of file
+     * content) for its license.
+     * For example, "/system/priv-app/TeleService/TeleService.apk" maps to "service/Telephony" to
      * "9645f39e9db895a4aa6e02cb57294595". Here "9645f39e9db895a4aa6e02cb57294595" is a MD5 sum
      * of the content of packages/services/Telephony/MODULE_LICENSE_APACHE2.
      */
-    private final Map<String, Set<String>> mFileNameToContentIdMap = new HashMap();
+    private final Map<String, Map<String, Set<String>>> mFileNameToLibraryToContentIdMap =
+            new HashMap();
 
     /*
      * A map from a content id (MD5 sum of file content) to a license file content.
@@ -98,7 +107,7 @@
 
     static class ContentIdAndFileNames {
         final String mContentId;
-        final List<String> mFileNameList = new ArrayList();
+        final Map<String, List<String>> mLibraryToFileNameMap = new TreeMap();
 
         ContentIdAndFileNames(String contentId) {
             mContentId = contentId;
@@ -120,7 +129,7 @@
             parse(xmlFile);
         }
 
-        if (mFileNameToContentIdMap.isEmpty() || mContentIdToFileContentMap.isEmpty()) {
+        if (mFileNameToLibraryToContentIdMap.isEmpty() || mContentIdToFileContentMap.isEmpty()) {
             return false;
         }
 
@@ -128,7 +137,7 @@
         try {
             writer = new PrintWriter(outputFile);
 
-            generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer,
+            generateHtml(mFileNameToLibraryToContentIdMap, mContentIdToFileContentMap, writer,
                 noticeHeader);
 
             writer.flush();
@@ -157,7 +166,7 @@
                 in = new FileReader(xmlFile);
             }
 
-            parse(in, mFileNameToContentIdMap, mContentIdToFileContentMap);
+            parse(in, mFileNameToLibraryToContentIdMap, mContentIdToFileContentMap);
 
             in.close();
         } catch (XmlPullParserException | IOException e) {
@@ -180,7 +189,8 @@
      *
      *     <licenses>
      *     <file-name contentId="content_id_of_license1">file1</file-name>
-     *     <file-name contentId="content_id_of_license2">file2</file-name>
+     *     <file-name contentId="content_id_of_license2" lib="name of library">file2</file-name>
+     *     <file-name contentId="content_id_of_license2" lib="another library">file2</file-name>
      *     ...
      *     <file-content contentId="content_id_of_license1">license1 file contents</file-content>
      *     <file-content contentId="content_id_of_license2">license2 file contents</file-content>
@@ -188,10 +198,12 @@
      *     </licenses>
      */
     @VisibleForTesting
-    static void parse(InputStreamReader in, Map<String, Set<String>> outFileNameToContentIdMap,
+    static void parse(InputStreamReader in,
+            Map<String, Map<String, Set<String>>> outFileNameToLibraryToContentIdMap,
             Map<String, String> outContentIdToFileContentMap)
                     throws XmlPullParserException, IOException {
-        Map<String, Set<String>> fileNameToContentIdMap = new HashMap<String, Set<String>>();
+        Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap =
+                new HashMap<String, Map<String, Set<String>>>();
         Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
 
         XmlPullParser parser = Xml.newPullParser();
@@ -205,12 +217,15 @@
             if (state == XmlPullParser.START_TAG) {
                 if (TAG_FILE_NAME.equals(parser.getName())) {
                     String contentId = parser.getAttributeValue("", ATTR_CONTENT_ID);
+                    String libraryName = parser.getAttributeValue("", ATTR_LIBRARY_NAME);
                     if (!TextUtils.isEmpty(contentId)) {
                         String fileName = readText(parser).trim();
                         if (!TextUtils.isEmpty(fileName)) {
-                            Set<String> contentIds =
-                                    fileNameToContentIdMap.computeIfAbsent(
-                                            fileName, k -> new HashSet<>());
+                            Map<String, Set<String>> libs =
+                                    fileNameToLibraryToContentIdMap.computeIfAbsent(
+                                            fileName, k -> new HashMap<>());
+                            Set<String> contentIds = libs.computeIfAbsent(
+                                            libraryName, k -> new HashSet<>());
                             contentIds.add(contentId);
                         }
                     }
@@ -229,11 +244,17 @@
 
             state = parser.next();
         }
-        for (Map.Entry<String, Set<String>> entry : fileNameToContentIdMap.entrySet()) {
-            outFileNameToContentIdMap.merge(
-                    entry.getKey(), entry.getValue(), (s1, s2) -> {
-                        s1.addAll(s2);
-                        return s1;
+        for (Map.Entry<String, Map<String, Set<String>>> mapEntry :
+                fileNameToLibraryToContentIdMap.entrySet()) {
+            outFileNameToLibraryToContentIdMap.merge(
+                    mapEntry.getKey(), mapEntry.getValue(), (m1, m2) -> {
+                        for (Map.Entry<String, Set<String>> entry : m2.entrySet()) {
+                            m1.merge(entry.getKey(), entry.getValue(), (s1, s2) -> {
+                                s1.addAll(s2);
+                                return s1;
+                            });
+                        }
+                        return m1;
                     });
         }
         outContentIdToFileContentMap.putAll(contentIdToFileContentMap);
@@ -251,13 +272,28 @@
     }
 
     @VisibleForTesting
-    static void generateHtml(Map<String, Set<String>> fileNameToContentIdMap,
+    static void generateHtml(Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap,
             Map<String, String> contentIdToFileContentMap, PrintWriter writer,
             String noticeHeader) {
         List<String> fileNameList = new ArrayList();
-        fileNameList.addAll(fileNameToContentIdMap.keySet());
+        fileNameList.addAll(fileNameToLibraryToContentIdMap.keySet());
         Collections.sort(fileNameList);
 
+        SortedMap<String, Set<String>> libraryToContentIdMap = new TreeMap();
+        for (Map<String, Set<String>> libraryToContentValue :
+                fileNameToLibraryToContentIdMap.values()) {
+            for (Map.Entry<String, Set<String>> entry : libraryToContentValue.entrySet()) {
+                if (TextUtils.isEmpty(entry.getKey())) {
+                    continue;
+                }
+                libraryToContentIdMap.merge(
+                        entry.getKey(), entry.getValue(), (s1, s2) -> {
+                            s1.addAll(s2);
+                            return s1;
+                        });
+            }
+        }
+
         writer.println(HTML_HEAD_STRING);
 
         if (!TextUtils.isEmpty(noticeHeader)) {
@@ -268,21 +304,56 @@
         Map<String, Integer> contentIdToOrderMap = new HashMap();
         List<ContentIdAndFileNames> contentIdAndFileNamesList = new ArrayList();
 
+        if (!libraryToContentIdMap.isEmpty()) {
+            writer.println(LIBRARY_HEAD_STRING);
+            for (Map.Entry<String, Set<String>> entry: libraryToContentIdMap.entrySet()) {
+                String libraryName = entry.getKey();
+                for (String contentId : entry.getValue()) {
+                    // Assigns an id to a newly referred license file content.
+                    if (!contentIdToOrderMap.containsKey(contentId)) {
+                        contentIdToOrderMap.put(contentId, count);
+
+                        // An index in contentIdAndFileNamesList is the order of each element.
+                        contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
+                        count++;
+                    }
+                    int id = contentIdToOrderMap.get(contentId);
+                    writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, libraryName);
+                }
+            }
+            writer.println(LIBRARY_TAIL_STRING);
+        }
+
         // Prints all the file list with a link to its license file content.
         for (String fileName : fileNameList) {
-            for (String contentId : fileNameToContentIdMap.get(fileName)) {
-                // Assigns an id to a newly referred license file content.
-                if (!contentIdToOrderMap.containsKey(contentId)) {
-                    contentIdToOrderMap.put(contentId, count);
-
-                    // An index in contentIdAndFileNamesList is the order of each element.
-                    contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
-                    count++;
+            for (Map.Entry<String, Set<String>> libToContentId :
+                    fileNameToLibraryToContentIdMap.get(fileName).entrySet()) {
+                String libraryName = libToContentId.getKey();
+                if (libraryName == null) {
+                    libraryName = "";
                 }
+                for (String contentId : libToContentId.getValue()) {
+                    // Assigns an id to a newly referred license file content.
+                    if (!contentIdToOrderMap.containsKey(contentId)) {
+                        contentIdToOrderMap.put(contentId, count);
 
-                int id = contentIdToOrderMap.get(contentId);
-                contentIdAndFileNamesList.get(id).mFileNameList.add(fileName);
-                writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, fileName);
+                        // An index in contentIdAndFileNamesList is the order of each element.
+                        contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
+                        count++;
+                    }
+
+                    int id = contentIdToOrderMap.get(contentId);
+                    ContentIdAndFileNames elem = contentIdAndFileNamesList.get(id);
+                    List<String> files = elem.mLibraryToFileNameMap.computeIfAbsent(
+                            libraryName, k -> new ArrayList());
+                    files.add(fileName);
+                    if (TextUtils.isEmpty(libraryName)) {
+                        writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, fileName);
+                    } else {
+                        writer.format("<li><a href=\"#id%d\">%s - %s</a></li>\n",
+                                id, fileName, libraryName);
+                    }
+                }
             }
         }
 
@@ -292,19 +363,27 @@
         // Prints all contents of the license files in order of id.
         for (ContentIdAndFileNames contentIdAndFileNames : contentIdAndFileNamesList) {
             writer.format("<tr id=\"id%d\"><td class=\"same-license\">\n", count);
-            writer.println("<div class=\"label\">Notices for file(s):</div>");
-            writer.println("<div class=\"file-list\">");
-            for (String fileName : contentIdAndFileNames.mFileNameList) {
-                writer.format("%s <br/>\n", fileName);
+            for (Map.Entry<String, List<String>> libraryFiles :
+                    contentIdAndFileNames.mLibraryToFileNameMap.entrySet()) {
+                String libraryName = libraryFiles.getKey();
+                if (TextUtils.isEmpty(libraryName)) {
+                    writer.println("<div class=\"label\">Notices for file(s):</div>");
+                } else {
+                    writer.format("<div class=\"label\"><strong>%s</strong> used by:</div>\n",
+                            libraryName);
+                }
+                writer.println("<div class=\"file-list\">");
+                for (String fileName : libraryFiles.getValue()) {
+                    writer.format("%s <br/>\n", fileName);
+                }
+                writer.println("</div><!-- file-list -->");
+                count++;
             }
-            writer.println("</div><!-- file-list -->");
             writer.println("<pre class=\"license-text\">");
             writer.println(contentIdToFileContentMap.get(
                     contentIdAndFileNames.mContentId));
             writer.println("</pre><!-- license-text -->");
             writer.println("</td></tr><!-- same-license -->");
-
-            count++;
         }
 
         writer.println(HTML_REAR_STRING);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 360361b..dd7db21 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -19,6 +19,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
 
@@ -34,11 +35,13 @@
     private static final String TAG = "BluetoothMediaDevice";
 
     private CachedBluetoothDevice mCachedDevice;
+    private final AudioManager mAudioManager;
 
     BluetoothMediaDevice(Context context, CachedBluetoothDevice device,
             MediaRouter2Manager routerManager, MediaRoute2Info info, String packageName) {
         super(context, routerManager, info, packageName);
         mCachedDevice = device;
+        mAudioManager = context.getSystemService(AudioManager.class);
         initDeviceRecord();
     }
 
@@ -98,6 +101,12 @@
     }
 
     @Override
+    public boolean isMutingExpectedDevice() {
+        return mAudioManager.getMutingExpectedDevice() != null && mCachedDevice.getAddress().equals(
+                mAudioManager.getMutingExpectedDevice().getAddress());
+    }
+
+    @Override
     public boolean isConnected() {
         return mCachedDevice.getBondState() == BluetoothDevice.BOND_BONDED
                 && mCachedDevice.isConnected();
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 970abff..c759962 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -320,6 +320,13 @@
         }
 
         if (mType == another.mType) {
+            // Check device is muting expected device
+            if (isMutingExpectedDevice()) {
+                return -1;
+            } else if (another.isMutingExpectedDevice()) {
+                return 1;
+            }
+
             // Check fast pair device
             if (isFastPairDevice()) {
                 return -1;
@@ -392,6 +399,14 @@
         return false;
     }
 
+    /**
+     * Check if it is muting expected device
+     * @return {@code true} if it is muting expected device, otherwise return {@code false}
+     */
+    protected boolean isMutingExpectedDevice() {
+        return false;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (!(obj instanceof MediaDevice)) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
index e87461f..e348865 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
@@ -36,7 +36,7 @@
 
 @RunWith(RobolectricTestRunner.class)
 public class LicenseHtmlGeneratorFromXmlTest {
-    private static final String VALILD_XML_STRING =
+    private static final String VALID_OLD_XML_STRING =
             "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
             + "<licenses>\n"
             + "<file-name contentId=\"0\">/file0</file-name>\n"
@@ -44,7 +44,15 @@
             + "<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n"
             + "</licenses>";
 
-    private static final String INVALILD_XML_STRING =
+    private static final String VALID_NEW_XML_STRING =
+            "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+            + "<licenses>\n"
+            + "<file-name contentId=\"0\" lib=\"libA\">/file0</file-name>\n"
+            + "<file-name contentId=\"0\" lib=\"libB\">/file1</file-name>\n"
+            + "<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n"
+            + "</licenses>";
+
+    private static final String INVALID_XML_STRING =
             "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
             + "<licenses2>\n"
             + "<file-name contentId=\"0\">/file0</file-name>\n"
@@ -64,13 +72,13 @@
             + "</style>\n"
             + "</head>"
             + "<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n"
-            + "<div class=\"toc\">\n"
-            + "<ul>\n";
+            + "<div class=\"toc\">\n";
 
     private static final String HTML_CUSTOM_HEADING = "Custom heading";
 
-    private static final String HTML_BODY_STRING =
-            "<li><a href=\"#id0\">/file0</a></li>\n"
+    private static final String HTML_OLD_BODY_STRING =
+            "<ul class=\"files\">\n"
+            + "<li><a href=\"#id0\">/file0</a></li>\n"
             + "<li><a href=\"#id1\">/file0</a></li>\n"
             + "<li><a href=\"#id0\">/file1</a></li>\n"
             + "</ul>\n"
@@ -97,66 +105,181 @@
             + "</td></tr><!-- same-license -->\n"
             + "</table></body></html>\n";
 
-    private static final String EXPECTED_HTML_STRING = HTML_HEAD_STRING + HTML_BODY_STRING;
+    private static final String HTML_NEW_BODY_STRING =
+            "<strong>Libraries</strong>\n"
+            + "<ul class=\"libraries\">\n"
+            + "<li><a href=\"#id0\">libA</a></li>\n"
+            + "<li><a href=\"#id1\">libB</a></li>\n"
+            + "</ul>\n"
+            + "<strong>Files</strong>\n"
+            + "<ul class=\"files\">\n"
+            + "<li><a href=\"#id0\">/file0 - libA</a></li>\n"
+            + "<li><a href=\"#id1\">/file0 - libB</a></li>\n"
+            + "<li><a href=\"#id0\">/file1 - libA</a></li>\n"
+            + "</ul>\n"
+            + "</div><!-- table of contents -->\n"
+            + "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n"
+            + "<tr id=\"id0\"><td class=\"same-license\">\n"
+            + "<div class=\"label\">Notices for file(s):</div>\n"
+            + "<div class=\"file-list\">\n"
+            + "/file0 <br/>\n"
+            + "/file1 <br/>\n"
+            + "</div><!-- file-list -->\n"
+            + "<pre class=\"license-text\">\n"
+            + "license content #0\n"
+            + "</pre><!-- license-text -->\n"
+            + "</td></tr><!-- same-license -->\n"
+            + "<tr id=\"id1\"><td class=\"same-license\">\n"
+            + "<div class=\"label\">Notices for file(s):</div>\n"
+            + "<div class=\"file-list\">\n"
+            + "/file0 <br/>\n"
+            + "</div><!-- file-list -->\n"
+            + "<pre class=\"license-text\">\n"
+            + "license content #1\n"
+            + "</pre><!-- license-text -->\n"
+            + "</td></tr><!-- same-license -->\n"
+            + "</table></body></html>\n";
 
-    private static final String EXPECTED_HTML_STRING_WITH_CUSTOM_HEADING =
-            HTML_HEAD_STRING + HTML_CUSTOM_HEADING + "\n" + HTML_BODY_STRING;
+    private static final String EXPECTED_OLD_HTML_STRING = HTML_HEAD_STRING + HTML_OLD_BODY_STRING;
+
+    private static final String EXPECTED_NEW_HTML_STRING = HTML_HEAD_STRING + HTML_NEW_BODY_STRING;
+
+    private static final String EXPECTED_OLD_HTML_STRING_WITH_CUSTOM_HEADING =
+            HTML_HEAD_STRING + HTML_CUSTOM_HEADING + "\n" + HTML_OLD_BODY_STRING;
+
+    private static final String EXPECTED_NEW_HTML_STRING_WITH_CUSTOM_HEADING =
+            HTML_HEAD_STRING + HTML_CUSTOM_HEADING + "\n" + HTML_NEW_BODY_STRING;
 
     @Test
     public void testParseValidXmlStream() throws XmlPullParserException, IOException {
-        Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
+        Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
         Map<String, String> contentIdToFileContentMap = new HashMap<>();
 
         LicenseHtmlGeneratorFromXml.parse(
-                new InputStreamReader(new ByteArrayInputStream(VALILD_XML_STRING.getBytes())),
-                fileNameToContentIdMap, contentIdToFileContentMap);
-        assertThat(fileNameToContentIdMap.size()).isEqualTo(2);
-        assertThat(fileNameToContentIdMap.get("/file0")).containsExactly("0");
-        assertThat(fileNameToContentIdMap.get("/file1")).containsExactly("0");
+                new InputStreamReader(new ByteArrayInputStream(VALID_OLD_XML_STRING.getBytes())),
+                fileNameToLibraryToContentIdMap, contentIdToFileContentMap);
+        assertThat(fileNameToLibraryToContentIdMap.size()).isEqualTo(1);
+        assertThat(fileNameToLibraryToContentIdMap.get("").size()).isEqualTo(2);
+        assertThat(fileNameToLibraryToContentIdMap.get("").get("/file0")).containsExactly("0");
+        assertThat(fileNameToLibraryToContentIdMap.get("").get("/file1")).containsExactly("0");
+        assertThat(contentIdToFileContentMap.size()).isEqualTo(1);
+        assertThat(contentIdToFileContentMap.get("0")).isEqualTo("license content #0");
+    }
+
+    @Test
+    public void testParseNewValidXmlStream() throws XmlPullParserException, IOException {
+        Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
+        Map<String, String> contentIdToFileContentMap = new HashMap<>();
+
+        LicenseHtmlGeneratorFromXml.parse(
+                new InputStreamReader(new ByteArrayInputStream(VALID_NEW_XML_STRING.getBytes())),
+                fileNameToLibraryToContentIdMap, contentIdToFileContentMap);
+        assertThat(fileNameToLibraryToContentIdMap.size()).isEqualTo(2);
+        assertThat(fileNameToLibraryToContentIdMap.get("libA").size()).isEqualTo(1);
+        assertThat(fileNameToLibraryToContentIdMap.get("libB").size()).isEqualTo(1);
+        assertThat(fileNameToLibraryToContentIdMap.get("libA").get("/file0")).containsExactly("0");
+        assertThat(fileNameToLibraryToContentIdMap.get("libB").get("/file1")).containsExactly("0");
         assertThat(contentIdToFileContentMap.size()).isEqualTo(1);
         assertThat(contentIdToFileContentMap.get("0")).isEqualTo("license content #0");
     }
 
     @Test(expected = XmlPullParserException.class)
     public void testParseInvalidXmlStream() throws XmlPullParserException, IOException {
-        Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
+        Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
         Map<String, String> contentIdToFileContentMap = new HashMap<>();
 
         LicenseHtmlGeneratorFromXml.parse(
-                new InputStreamReader(new ByteArrayInputStream(INVALILD_XML_STRING.getBytes())),
-                fileNameToContentIdMap, contentIdToFileContentMap);
+                new InputStreamReader(new ByteArrayInputStream(INVALID_XML_STRING.getBytes())),
+                fileNameToLibraryToContentIdMap, contentIdToFileContentMap);
     }
 
     @Test
     public void testGenerateHtml() {
-        Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
+        Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
         Map<String, String> contentIdToFileContentMap = new HashMap<>();
+        Map<String, Set<String>> toBoth = new HashMap<>();
+        Map<String, Set<String>> toOne = new HashMap<>();
 
-        fileNameToContentIdMap.put("/file0", new HashSet<String>(Arrays.asList("0", "1")));
-        fileNameToContentIdMap.put("/file1", new HashSet<String>(Arrays.asList("0")));
+        toBoth.put("", new HashSet<String>(Arrays.asList("0", "1")));
+        toOne.put("", new HashSet<String>(Arrays.asList("0")));
+
+        fileNameToLibraryToContentIdMap.put("/file0", toBoth);
+        fileNameToLibraryToContentIdMap.put("/file1", toOne);
         contentIdToFileContentMap.put("0", "license content #0");
         contentIdToFileContentMap.put("1", "license content #1");
 
         StringWriter output = new StringWriter();
         LicenseHtmlGeneratorFromXml.generateHtml(
-                fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output), "");
-        assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING);
+                fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
+                new PrintWriter(output), "");
+        assertThat(output.toString()).isEqualTo(EXPECTED_OLD_HTML_STRING);
+    }
+
+    @Test
+    public void testGenerateNewHtml() {
+        Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
+        Map<String, String> contentIdToFileContentMap = new HashMap<>();
+        Map<String, Set<String>> toBoth = new HashMap<>();
+        Map<String, Set<String>> toOne = new HashMap<>();
+
+        toBoth.put("libA", new HashSet<String>(Arrays.asList("0")));
+        toBoth.put("libB", new HashSet<String>(Arrays.asList("1")));
+        toOne.put("libA", new HashSet<String>(Arrays.asList("0")));
+
+        fileNameToLibraryToContentIdMap.put("/file0", toBoth);
+        fileNameToLibraryToContentIdMap.put("/file1", toOne);
+        contentIdToFileContentMap.put("0", "license content #0");
+        contentIdToFileContentMap.put("1", "license content #1");
+
+        StringWriter output = new StringWriter();
+        LicenseHtmlGeneratorFromXml.generateHtml(
+                fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
+                new PrintWriter(output), "");
+        assertThat(output.toString()).isEqualTo(EXPECTED_NEW_HTML_STRING);
     }
 
     @Test
     public void testGenerateHtmlWithCustomHeading() {
-        Map<String, Set<String>> fileNameToContentIdMap = new HashMap<>();
+        Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
         Map<String, String> contentIdToFileContentMap = new HashMap<>();
+        Map<String, Set<String>> toBoth = new HashMap<>();
+        Map<String, Set<String>> toOne = new HashMap<>();
 
-        fileNameToContentIdMap.put("/file0", new HashSet<String>(Arrays.asList("0", "1")));
-        fileNameToContentIdMap.put("/file1", new HashSet<String>(Arrays.asList("0")));
+        toBoth.put("", new HashSet<String>(Arrays.asList("0", "1")));
+        toOne.put("", new HashSet<String>(Arrays.asList("0")));
+
+        fileNameToLibraryToContentIdMap.put("/file0", toBoth);
+        fileNameToLibraryToContentIdMap.put("/file1", toOne);
         contentIdToFileContentMap.put("0", "license content #0");
         contentIdToFileContentMap.put("1", "license content #1");
 
         StringWriter output = new StringWriter();
         LicenseHtmlGeneratorFromXml.generateHtml(
-                fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output),
-                HTML_CUSTOM_HEADING);
-        assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING_WITH_CUSTOM_HEADING);
+                fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
+                new PrintWriter(output), HTML_CUSTOM_HEADING);
+        assertThat(output.toString()).isEqualTo(EXPECTED_OLD_HTML_STRING_WITH_CUSTOM_HEADING);
+    }
+
+    @Test
+    public void testGenerateNewHtmlWithCustomHeading() {
+        Map<String, Map<String, Set<String>>> fileNameToLibraryToContentIdMap = new HashMap<>();
+        Map<String, String> contentIdToFileContentMap = new HashMap<>();
+        Map<String, Set<String>> toBoth = new HashMap<>();
+        Map<String, Set<String>> toOne = new HashMap<>();
+
+        toBoth.put("libA", new HashSet<String>(Arrays.asList("0")));
+        toBoth.put("libB", new HashSet<String>(Arrays.asList("1")));
+        toOne.put("libA", new HashSet<String>(Arrays.asList("0")));
+
+        fileNameToLibraryToContentIdMap.put("/file0", toBoth);
+        fileNameToLibraryToContentIdMap.put("/file1", toOne);
+        contentIdToFileContentMap.put("0", "license content #0");
+        contentIdToFileContentMap.put("1", "license content #1");
+
+        StringWriter output = new StringWriter();
+        LicenseHtmlGeneratorFromXml.generateHtml(
+                fileNameToLibraryToContentIdMap, contentIdToFileContentMap,
+                new PrintWriter(output), HTML_CUSTOM_HEADING);
+        assertThat(output.toString()).isEqualTo(EXPECTED_NEW_HTML_STRING_WITH_CUSTOM_HEADING);
     }
 }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java
deleted file mode 100644
index 6d1408d..0000000
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/DetailAdapter.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.plugins.qs;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-
-@ProvidesInterface(version = DetailAdapter.VERSION)
-public interface DetailAdapter {
-    public static final int VERSION = 1;
-
-    CharSequence getTitle();
-    Boolean getToggleState();
-
-    default boolean getToggleEnabled() {
-        return true;
-    }
-
-    View createDetailView(Context context, View convertView, ViewGroup parent);
-
-    /**
-     * @return intent for opening more settings related to this detail panel. If null, the more
-     * settings button will not be shown
-     */
-    Intent getSettingsIntent();
-
-    /**
-     * @return resource id of the string to use for opening the settings intent. If
-     * {@code Resources.ID_NULL}, then use the default string:
-     * {@code com.android.systemui.R.string.quick_settings_more_settings}
-     */
-    default int getSettingsText() {
-        return Resources.ID_NULL;
-    }
-
-    /**
-     * @return resource id of the string to use for closing the detail panel. If
-     * {@code Resources.ID_NULL}, then use the default string:
-     * {@code com.android.systemui.R.string.quick_settings_done}
-     */
-    default int getDoneText() {
-        return Resources.ID_NULL;
-    }
-
-    void setToggleState(boolean state);
-    int getMetricsCategory();
-
-    /**
-     * Indicates whether the detail view wants to have its header (back button, title and
-     * toggle) shown.
-     */
-    default boolean hasHeader() {
-        return true;
-    }
-
-    /**
-     * Indicates whether the detail view wants to animate when shown. This has no affect over the
-     * closing animation. Detail panels will always animate when closed.
-     */
-    default boolean shouldAnimate() {
-        return true;
-    }
-
-    /**
-     * @return true if the callback handled the event and wants to keep the detail panel open, false
-     * otherwise. Returning false will close the panel.
-     */
-    default boolean onDoneButtonClicked() {
-        return false;
-    }
-
-    default UiEventLogger.UiEventEnum openDetailEvent() {
-        return INVALID;
-    }
-
-    default UiEventLogger.UiEventEnum closeDetailEvent() {
-        return INVALID;
-    }
-
-    default UiEventLogger.UiEventEnum moreSettingsEvent() {
-        return INVALID;
-    }
-
-    UiEventLogger.UiEventEnum INVALID = () -> 0;
-}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index 1ef5324..669d6a3 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -35,14 +35,12 @@
 
 @ProvidesInterface(version = QSTile.VERSION)
 @DependsOn(target = QSIconView.class)
-@DependsOn(target = DetailAdapter.class)
 @DependsOn(target = Callback.class)
 @DependsOn(target = Icon.class)
 @DependsOn(target = State.class)
 public interface QSTile {
-    int VERSION = 2;
+    int VERSION = 3;
 
-    DetailAdapter getDetailAdapter();
     String getTileSpec();
 
     boolean isAvailable();
@@ -117,12 +115,9 @@
     }
 
     @ProvidesInterface(version = Callback.VERSION)
-    public interface Callback {
-        public static final int VERSION = 1;
+    interface Callback {
+        static final int VERSION = 2;
         void onStateChanged(State state);
-        void onShowDetail(boolean show);
-        void onToggleStateChanged(boolean state);
-        void onScanStateChanged(boolean state);
     }
 
     @ProvidesInterface(version = Icon.VERSION)
diff --git a/packages/SystemUI/res/color-night/qs_detail_progress_track.xml b/packages/SystemUI/res/color-night/qs_detail_progress_track.xml
deleted file mode 100644
index c56382e..0000000
--- a/packages/SystemUI/res/color-night/qs_detail_progress_track.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <!-- I really don't want to define this, but the View that uses this asset uses both the
-         light and dark accent colors. -->
-    <item android:alpha="0.6" android:drawable="@*android:color/accent_device_default_light" />
-</selector>
diff --git a/packages/SystemUI/res/color/qs_detail_progress_track.xml b/packages/SystemUI/res/color/qs_detail_progress_track.xml
deleted file mode 100644
index d86119f..0000000
--- a/packages/SystemUI/res/color/qs_detail_progress_track.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <!-- I really don't want to define this, but the View that uses this asset uses both the
-         light and dark accent colors. -->
-    <item android:alpha="0.6" android:drawable="@*android:color/accent_device_default_dark" />
-</selector>
diff --git a/packages/SystemUI/res/drawable/qs_detail_background.xml b/packages/SystemUI/res/drawable/qs_detail_background.xml
deleted file mode 100644
index c23649d..0000000
--- a/packages/SystemUI/res/drawable/qs_detail_background.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <inset>
-            <shape>
-                <solid android:color="@android:color/transparent"/>
-                <corners android:radius="@dimen/qs_footer_action_corner_radius" />
-            </shape>
-        </inset>
-    </item>
-    <item>
-        <inset>
-            <shape>
-                <solid android:color="?android:attr/colorBackgroundFloating"/>
-                <corners android:radius="@dimen/qs_footer_action_corner_radius" />
-            </shape>
-        </inset>
-    </item>
-</transition>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
deleted file mode 100644
index 78655c0..0000000
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<!-- Extends LinearLayout -->
-<com.android.systemui.qs.QSDetail
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="@drawable/qs_detail_background"
-    android:layout_marginTop="@dimen/qs_detail_margin_top"
-    android:clickable="true"
-    android:orientation="vertical"
-    android:paddingBottom="8dp"
-    android:visibility="invisible"
-    android:elevation="4dp"
-    android:importantForAccessibility="no" >
-
-    <include
-        android:id="@+id/qs_detail_header"
-        layout="@layout/qs_detail_header"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        />
-
-    <com.android.systemui.statusbar.AlphaOptimizedImageView
-        android:id="@+id/qs_detail_header_progress"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:alpha="0"
-        android:background="@color/qs_detail_progress_track"
-        android:src="@drawable/indeterminate_anim"
-        android:scaleType="fitXY"
-        />
-
-    <ScrollView
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        android:fillViewport="true">
-
-        <FrameLayout
-            android:id="@android:id/content"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
-    </ScrollView>
-
-    <include layout="@layout/qs_detail_buttons" />
-
-</com.android.systemui.qs.QSDetail>
diff --git a/packages/SystemUI/res/layout/qs_detail_buttons.xml b/packages/SystemUI/res/layout/qs_detail_buttons.xml
deleted file mode 100644
index 75f43f9..0000000
--- a/packages/SystemUI/res/layout/qs_detail_buttons.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:paddingEnd="8dp"
-    android:gravity="end">
-
-    <TextView
-        android:id="@android:id/button2"
-        style="@style/QSBorderlessButton"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginEnd="8dp"
-        android:minHeight="48dp"
-        android:minWidth="132dp"
-        android:textAppearance="@style/TextAppearance.QS.DetailButton"
-        android:focusable="true" />
-
-    <TextView
-        android:id="@android:id/button1"
-        style="@style/QSBorderlessButton"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:minHeight="48dp"
-        android:minWidth="88dp"
-        android:textAppearance="@style/TextAppearance.QS.DetailButton"
-        android:focusable="true"/>
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_detail_header.xml b/packages/SystemUI/res/layout/qs_detail_header.xml
deleted file mode 100644
index d1ab054..0000000
--- a/packages/SystemUI/res/layout/qs_detail_header.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.keyguard.AlphaOptimizedLinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:paddingLeft="@dimen/qs_detail_header_padding"
-    android:paddingTop="@dimen/qs_detail_header_padding"
-    android:paddingBottom="@dimen/qs_detail_items_padding_top"
-    android:paddingEnd="@dimen/qs_panel_padding"
-    android:background="@drawable/btn_borderless_rect"
-    android:orientation="vertical"
-    android:gravity="center">
-
-    <com.android.systemui.ResizingSpace
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/qs_detail_header_margin_top" />
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal">
-
-        <TextView
-            android:id="@android:id/title"
-            android:paddingStart="@dimen/qs_detail_header_text_padding"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:textDirection="locale"
-            android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
-
-        <ImageView
-            android:id="@+id/settings"
-            android:layout_width="@dimen/qs_detail_image_width"
-            android:layout_height="@dimen/qs_detail_image_height"
-            android:background="?android:attr/selectableItemBackground"
-            android:padding="@dimen/qs_detail_image_padding"
-            android:src="@drawable/ic_settings"
-            android:visibility="gone"/>
-
-        <ViewStub
-            android:id="@+id/toggle_stub"
-            android:inflatedId="@+id/toggle"
-            android:layout="@layout/qs_detail_switch"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"/>
-
-    </LinearLayout>
-
-</com.android.keyguard.AlphaOptimizedLinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_detail_item.xml b/packages/SystemUI/res/layout/qs_detail_item.xml
deleted file mode 100644
index 0844bb4..0000000
--- a/packages/SystemUI/res/layout/qs_detail_item.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:minHeight="@dimen/qs_detail_item_height"
-    android:background="@drawable/btn_borderless_rect"
-    android:clickable="true"
-    android:focusable="true"
-    android:gravity="center_vertical"
-    android:orientation="horizontal" >
-
-    <ImageView
-        android:id="@android:id/icon"
-        android:layout_width="@dimen/qs_detail_item_icon_width"
-        android:layout_height="@dimen/qs_detail_item_icon_size"
-        android:layout_marginStart="@dimen/qs_detail_item_icon_marginStart"
-        android:layout_marginEnd="@dimen/qs_detail_item_icon_marginEnd"
-        android:tint="?android:attr/textColorPrimary"/>
-
-    <LinearLayout
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="12dp"
-        android:layout_weight="1"
-        android:orientation="vertical" >
-
-        <TextView
-            android:id="@android:id/title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:textDirection="locale"
-            android:ellipsize="end"
-            android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
-
-        <TextView
-            android:id="@android:id/summary"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:textDirection="locale"
-            android:layout_marginTop="2dp"
-            android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary" />
-    </LinearLayout>
-
-    <ImageView
-        android:id="@android:id/icon2"
-        style="@style/QSBorderlessButton"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:clickable="true"
-        android:focusable="true"
-        android:scaleType="center"
-        android:contentDescription="@*android:string/media_route_controller_disconnect"
-        android:tint="?android:attr/textColorPrimary" />
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_detail_items.xml b/packages/SystemUI/res/layout/qs_detail_items.xml
deleted file mode 100644
index 60cba67..0000000
--- a/packages/SystemUI/res/layout/qs_detail_items.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<!-- extends FrameLayout -->
-<com.android.systemui.qs.QSDetailItems
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:sysui="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:paddingStart="@dimen/qs_detail_padding_start"
-    android:paddingEnd="16dp">
-
-    <com.android.systemui.qs.AutoSizingList
-        android:id="@android:id/list"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="vertical"
-        sysui:itemHeight="@dimen/qs_detail_item_height"
-        style="@style/AutoSizingList"/>
-
-    <LinearLayout
-        android:id="@android:id/empty"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_gravity="center"
-        android:gravity="center"
-        android:orientation="vertical">
-
-        <ImageView
-            android:id="@android:id/icon"
-            android:layout_width="56dp"
-            android:layout_height="56dp"
-            android:tint="?android:attr/textColorSecondary" />
-
-        <TextView
-            android:id="@android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="20dp"
-            android:textAppearance="@style/TextAppearance.QS.DetailEmpty"/>
-    </LinearLayout>
-</com.android.systemui.qs.QSDetailItems>
diff --git a/packages/SystemUI/res/layout/qs_detail_switch.xml b/packages/SystemUI/res/layout/qs_detail_switch.xml
deleted file mode 100644
index abb2497..0000000
--- a/packages/SystemUI/res/layout/qs_detail_switch.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
-  ~ Copyright (C) 2019 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.
-  -->
-
-<Switch
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@android:id/toggle"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:clickable="false"
-    android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 85b33cc..2040051 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -47,10 +47,6 @@
 
     <include layout="@layout/quick_status_bar_expanded_header" />
 
-    <include
-        android:id="@+id/qs_detail"
-        layout="@layout/qs_detail" />
-
     <ViewStub
         android:id="@+id/container_stub"
         android:inflatedId="@+id/qs_footer_actions"
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 3a0df28..0c847ed 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -52,7 +52,7 @@
                 android:gravity="center_horizontal" />
         <ImageView
                 android:id="@+id/restricted_padlock"
-                android:layout_width="@dimen/qs_detail_item_secondary_text_size"
+                android:layout_width="@dimen/qs_tile_text_size"
                 android:layout_height="match_parent"
                 android:gravity="center_vertical"
                 android:src="@drawable/ic_info"
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index 4f4bae4..816dfd3 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -19,6 +19,7 @@
     android:id="@+id/system_icons"
     android:layout_width="wrap_content"
     android:layout_height="match_parent"
+    android:layout_gravity="center_vertical|end"
     android:gravity="center_vertical">
 
     <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons"
diff --git a/packages/SystemUI/res/layout/tuner_zen_mode_panel.xml b/packages/SystemUI/res/layout/tuner_zen_mode_panel.xml
deleted file mode 100644
index efe63d7..0000000
--- a/packages/SystemUI/res/layout/tuner_zen_mode_panel.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<!-- extends LinearLayout -->
-<com.android.systemui.tuner.TunerZenModePanel
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/tuner_zen_mode_panel"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:clipChildren="false"
-    android:visibility="gone"
-    android:orientation="vertical" >
-
-    <View
-        android:id="@+id/zen_embedded_divider"
-        android:layout_width="match_parent"
-        android:layout_height="1dp"
-        android:layout_marginBottom="12dp"
-        android:layout_marginTop="8dp"
-        android:background="@color/qs_tile_divider" />
-
-    <include
-        android:layout_width="match_parent"
-        android:layout_height="48dp"
-        android:layout_marginStart="16dp"
-        android:id="@+id/tuner_zen_switch"
-        layout="@layout/qs_detail_header" />
-
-    <include layout="@layout/zen_mode_panel" />
-
-    <include
-        android:id="@+id/tuner_zen_buttons"
-        layout="@layout/qs_detail_buttons" />
-
-</com.android.systemui.tuner.TunerZenModePanel>
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
deleted file mode 100644
index 5862413..0000000
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<!-- extends LinearLayout -->
-<com.android.systemui.volume.ZenModePanel xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/zen_mode_panel"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:clipChildren="false" >
-
-    <LinearLayout
-        android:id="@+id/edit_container"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="?android:attr/colorPrimary"
-        android:clipChildren="false"
-        android:orientation="vertical">
-
-        <com.android.systemui.volume.SegmentedButtons
-            android:id="@+id/zen_buttons"
-            android:background="@drawable/segmented_buttons_background"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="16dp"
-            android:layout_marginEnd="16dp"
-            android:layout_marginBottom="8dp" />
-
-        <RelativeLayout
-            android:id="@+id/zen_introduction"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="16dp"
-            android:layout_marginEnd="16dp"
-            android:paddingTop="8dp"
-            android:paddingBottom="8dp"
-            android:background="@drawable/zen_introduction_message_background"
-            android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent.Light">
-
-            <ImageView
-                android:id="@+id/zen_introduction_confirm"
-                android:layout_width="48dp"
-                android:layout_height="48dp"
-                android:layout_marginEnd="8dp"
-                android:layout_alignParentEnd="true"
-                android:background="@drawable/btn_borderless_rect"
-                android:clickable="true"
-                android:contentDescription="@string/accessibility_desc_close"
-                android:scaleType="center"
-                android:src="@drawable/ic_close_white_rounded" />
-
-            <TextView
-                android:id="@+id/zen_introduction_message"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="12dp"
-                android:layout_marginStart="24dp"
-                android:textDirection="locale"
-                android:lineSpacingMultiplier="1.20029"
-                android:layout_toStartOf="@id/zen_introduction_confirm"
-                android:textAppearance="@style/TextAppearance.QS.Introduction" />
-
-            <TextView
-                android:id="@+id/zen_introduction_customize"
-                style="@style/QSBorderlessButton"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_alignParentEnd="true"
-                android:layout_marginEnd="12dp"
-                android:layout_below="@id/zen_introduction_message"
-                android:clickable="true"
-                android:focusable="true"
-                android:text="@string/zen_priority_customize_button"
-                android:textAppearance="@style/TextAppearance.QS.DetailButton.White" />
-
-            <View
-                android:layout_width="0dp"
-                android:layout_height="16dp"
-                android:layout_below="@id/zen_introduction_message"
-                android:layout_alignParentEnd="true" />
-
-        </RelativeLayout>
-
-        <com.android.settingslib.notification.ZenRadioLayout
-            android:id="@+id/zen_conditions"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="8dp"
-            android:layout_marginEnd="4dp"
-            android:layout_marginStart="4dp"
-            android:paddingBottom="@dimen/zen_mode_condition_detail_bottom_padding"
-            android:orientation="horizontal" >
-            <RadioGroup
-                android:id="@+id/zen_radio_buttons"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content" />
-            <LinearLayout
-                android:id="@+id/zen_radio_buttons_content"
-                android:layout_width="fill_parent"
-                android:layout_height="fill_parent"
-                android:orientation="vertical"/>
-        </com.android.settingslib.notification.ZenRadioLayout>
-
-        <TextView
-            android:id="@+id/zen_alarm_warning"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="18dp"
-            android:layout_marginEnd="16dp"
-            android:textDirection="locale"
-            android:lineSpacingMultiplier="1.20029"
-            android:textAppearance="@style/TextAppearance.QS.Warning" />
-    </LinearLayout>
-
-    <LinearLayout
-        android:id="@android:id/empty"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_gravity="center"
-        android:background="?android:attr/colorPrimary"
-        android:gravity="center"
-        android:orientation="vertical">
-
-        <ImageView
-            android:id="@android:id/icon"
-            android:layout_width="56dp"
-            android:layout_height="56dp"
-            android:alpha="?android:attr/disabledAlpha"
-            android:tint="?android:attr/colorForeground" />
-
-        <TextView
-            android:id="@android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="20dp"
-            android:textAppearance="@style/TextAppearance.QS.DetailEmpty"/>
-    </LinearLayout>
-
-    <LinearLayout
-        android:id="@+id/auto_rule"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="?android:attr/colorPrimary"
-        android:layout_marginStart="16dp"
-        android:layout_marginEnd="16dp"
-        android:layout_marginTop="16dp"
-        android:layout_marginBottom="8dp"
-        android:orientation="vertical">
-
-        <TextView
-            android:id="@android:id/title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary"/>
-
-    </LinearLayout>
-
-</com.android.systemui.volume.ZenModePanel>
diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml
index 8c5006d..062e33c 100644
--- a/packages/SystemUI/res/values-land/config.xml
+++ b/packages/SystemUI/res/values-land/config.xml
@@ -33,4 +33,10 @@
 
     <!-- Max number of columns for power menu -->
     <integer name="power_menu_max_columns">4</integer>
+
+    <!-- Max number of columns for power menu lite -->
+    <integer name="power_menu_lite_max_columns">4</integer>
+    <!-- Max number of rows for power menu lite -->
+    <integer name="power_menu_lite_max_rows">2</integer>
+
 </resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 9d24e9b..01eb09b 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -38,11 +38,6 @@
     <dimen name="qs_footer_padding">14dp</dimen>
     <dimen name="qs_security_footer_background_inset">12dp</dimen>
 
-    <dimen name="battery_detail_graph_space_top">9dp</dimen>
-    <dimen name="battery_detail_graph_space_bottom">9dp</dimen>
-
-    <dimen name="qs_detail_header_margin_top">14dp</dimen>
-
     <dimen name="volume_tool_tip_top_margin">12dp</dimen>
     <dimen name="volume_row_slider_height">128dp</dimen>
 
diff --git a/packages/SystemUI/res/values-sw410dp/dimens.xml b/packages/SystemUI/res/values-sw410dp/dimens.xml
index d33ee99..7da47e5 100644
--- a/packages/SystemUI/res/values-sw410dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw410dp/dimens.xml
@@ -20,8 +20,6 @@
 <!-- These resources are around just to allow their values to be customized
      for different hardware and product builds. -->
 <resources>
-    <dimen name="qs_detail_items_padding_top">16dp</dimen>
-
     <!-- Global actions grid -->
     <dimen name="global_actions_grid_vertical_padding">8dp</dimen>
     <dimen name="global_actions_grid_horizontal_padding">4dp</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index fe546f6..588638f 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -33,4 +33,7 @@
     <!-- Notifications are sized to match the width of two (of 4) qs tiles in landscape. -->
     <bool name="config_skinnyNotifsInLandscape">false</bool>
 
+    <integer name="power_menu_lite_max_columns">3</integer>
+    <integer name="power_menu_lite_max_rows">2</integer>
+
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp-port/config.xml b/packages/SystemUI/res/values-sw600dp-port/config.xml
index 3c6a81e..857e162 100644
--- a/packages/SystemUI/res/values-sw600dp-port/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/config.xml
@@ -23,4 +23,8 @@
 
     <!-- The number of columns in the QuickSettings -->
     <integer name="quick_settings_num_columns">3</integer>
+
+    <integer name="power_menu_lite_max_columns">2</integer>
+    <integer name="power_menu_lite_max_rows">3</integer>
+
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index a66ed15..8f6bde5 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -65,9 +65,6 @@
     <dimen name="qs_security_footer_single_line_height">48dp</dimen>
     <dimen name="qs_security_footer_background_inset">0dp</dimen>
 
-    <!-- When split shade is used, this panel should be aligned to the top -->
-    <dimen name="qs_detail_margin_top">0dp</dimen>
-
     <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
     <dimen name="large_dialog_width">472dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index e6ab0ff..2992859 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -114,13 +114,6 @@
         <attr name="hybridNotificationTextStyle" format="reference" />
     </declare-styleable>
 
-    <declare-styleable name="AutoSizingList">
-        <!-- Whether AutoSizingList will show only as many items as fit on screen and
-             remove extra items instead of scrolling. -->
-        <attr name="enableAutoSizing" format="boolean" />
-        <attr name="itemHeight" format="dimension" />
-    </declare-styleable>
-
     <declare-styleable name="PluginInflateContainer">
         <attr name="viewType" format="string" />
     </declare-styleable>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index fe79f27..d1f4f19 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -500,26 +500,10 @@
     <dimen name="qs_panel_elevation">4dp</dimen>
     <dimen name="qs_panel_padding_bottom">@dimen/new_footer_height</dimen>
     <dimen name="qs_panel_padding_top">48dp</dimen>
-    <dimen name="qs_detail_header_padding">0dp</dimen>
-    <dimen name="qs_detail_image_width">56dp</dimen>
-    <dimen name="qs_detail_image_height">56dp</dimen>
-    <dimen name="qs_detail_image_padding">16dp</dimen>
-    <dimen name="qs_detail_item_height">48dp</dimen>
-    <dimen name="qs_detail_header_text_size">20sp</dimen>
-    <dimen name="qs_detail_button_text_size">14sp</dimen>
-    <dimen name="qs_detail_item_primary_text_size">16sp</dimen>
-    <dimen name="qs_detail_item_secondary_text_size">14sp</dimen>
-    <dimen name="qs_detail_empty_text_size">14sp</dimen>
-    <dimen name="qs_detail_header_margin_top">28dp</dimen>
-    <dimen name="qs_detail_header_text_padding">16dp</dimen>
+
     <dimen name="qs_data_usage_text_size">14sp</dimen>
     <dimen name="qs_data_usage_usage_text_size">36sp</dimen>
-    <dimen name="qs_detail_padding_start">16dp</dimen>
-    <dimen name="qs_detail_items_padding_top">4dp</dimen>
-    <dimen name="qs_detail_item_icon_size">24dp</dimen>
-    <dimen name="qs_detail_item_icon_width">32dp</dimen>
-    <dimen name="qs_detail_item_icon_marginStart">0dp</dimen>
-    <dimen name="qs_detail_item_icon_marginEnd">20dp</dimen>
+
     <dimen name="qs_header_mobile_icon_size">@dimen/status_bar_icon_drawing_size</dimen>
     <dimen name="qs_header_carrier_separator_width">6dp</dimen>
     <dimen name="qs_carrier_margin_width">4dp</dimen>
@@ -533,9 +517,6 @@
     <dimen name="qs_security_footer_background_inset">0dp</dimen>
     <dimen name="qs_security_footer_corner_radius">28dp</dimen>
 
-    <!-- Desired qs icon overlay size. -->
-    <dimen name="qs_detail_icon_overlay_size">24dp</dimen>
-
     <dimen name="segmented_button_spacing">0dp</dimen>
     <dimen name="borderless_button_radius">2dp</dimen>
 
@@ -547,8 +528,6 @@
     <!-- Padding between subtitles and the following text in the QSFooter dialog -->
     <dimen name="qs_footer_dialog_subtitle_padding">20dp</dimen>
 
-    <dimen name="qs_detail_margin_top">@*android:dimen/quick_qs_offset_height</dimen>
-
     <!-- Zen mode panel: spacing between two-line condition upper and lower lines -->
     <dimen name="zen_mode_condition_detail_item_interline_spacing">4dp</dimen>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6a34ada..3b7e9d4 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -609,8 +609,6 @@
     <string name="quick_settings_inversion_label">Color inversion</string>
     <!-- QuickSettings: Label for the toggle that controls whether display color correction is enabled. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_color_correction_label">Color correction</string>
-    <!-- QuickSettings: Control panel: Label for button that navigates to settings. [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_more_settings">More settings</string>
     <!-- QuickSettings: Control panel: Label for button that navigates to user settings. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_more_user_settings">User settings</string>
     <!-- QuickSettings: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] -->
@@ -1213,9 +1211,6 @@
     <!-- Alarm template for far alarms [CHAR LIMIT=25] -->
     <string name="alarm_template_far">on <xliff:g id="when" example="Fri 7:00 AM">%1$s</xliff:g></string>
 
-    <!-- Accessibility label for Quick Settings detail screens [CHAR LIMIT=NONE] -->
-    <string name="accessibility_quick_settings_detail">Quick Settings, <xliff:g id="title" example="Wi-Fi">%s</xliff:g>.</string>
-
     <!-- Accessibility label for hotspot icon [CHAR LIMIT=NONE] -->
     <string name="accessibility_status_bar_hotspot">Hotspot</string>
 
@@ -1731,11 +1726,6 @@
     <string name="data_connection_no_internet">No internet</string>
 
     <!-- accessibility label for quick settings items that open a details page [CHAR LIMIT=NONE] -->
-    <string name="accessibility_quick_settings_open_details">Open details.</string>
-
-    <!-- accessibility label for quick settings items that are currently disabled. Must have a reason [CHAR LIMIT=NONE] -->
-
-    <!-- accessibility label for quick settings items that open a details page [CHAR LIMIT=NONE] -->
     <string name="accessibility_quick_settings_open_settings">Open <xliff:g name="page" example="Bluetooth">%s</xliff:g> settings.</string>
 
     <!-- accessibility label for button to edit quick settings [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 9448d3f..5d252fd 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -96,17 +96,12 @@
         <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
     </style>
 
-    <style name="TextAppearance.QS.DetailHeader">
-        <item name="android:textSize">@dimen/qs_detail_header_text_size</item>
-        <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
-    </style>
-
     <style name="TextAppearance.QS.DetailItemPrimary">
-        <item name="android:textSize">@dimen/qs_detail_item_primary_text_size</item>
+        <item name="android:textSize">@dimen/qs_tile_text_size</item>
     </style>
 
     <style name="TextAppearance.QS.DetailItemSecondary">
-        <item name="android:textSize">@dimen/qs_detail_item_secondary_text_size</item>
+        <item name="android:textSize">@dimen/qs_tile_text_size</item>
         <item name="android:textColor">?android:attr/colorAccent</item>
     </style>
 
@@ -120,23 +115,6 @@
         <item name="android:textColor">?android:attr/colorError</item>
     </style>
 
-    <style name="TextAppearance.QS.DetailButton">
-        <item name="android:textSize">@dimen/qs_detail_button_text_size</item>
-        <item name="android:textColor">?android:attr/textColorSecondary</item>
-        <item name="android:textAllCaps">true</item>
-        <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
-        <item name="android:gravity">center</item>
-    </style>
-
-    <style name="TextAppearance.QS.DetailButton.White">
-        <item name="android:textColor">@color/zen_introduction</item>
-    </style>
-
-    <style name="TextAppearance.QS.DetailEmpty">
-        <item name="android:textSize">@dimen/qs_detail_empty_text_size</item>
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
-    </style>
-
     <style name="TextAppearance.QS.SegmentedButton">
         <item name="android:textSize">16sp</item>
         <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
@@ -167,7 +145,7 @@
     </style>
 
     <style name="TextAppearance.QS.UserSwitcher">
-        <item name="android:textSize">@dimen/qs_detail_item_secondary_text_size</item>
+        <item name="android:textSize">@dimen/qs_tile_text_size</item>
         <item name="android:textColor">?android:attr/textColorSecondary</item>
     </style>
 
@@ -433,9 +411,6 @@
         <item name="numColumns">3</item>
     </style>
 
-    <style name="AutoSizingList">
-        <item name="enableAutoSizing">true</item>
-    </style>
     <style name="Theme.SystemUI.MediaProjectionAlertDialog">
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowBackground">@android:color/transparent</item>
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index 72b40d4..54664f2 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -48,7 +48,7 @@
     @Override
     public void start() {
         if (DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED, false)) {
+                DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED, true)) {
             mClipboardManager = requireNonNull(mContext.getSystemService(ClipboardManager.class));
             mClipboardManager.addPrimaryClipChangedListener(this);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 63d4d6b..a9e310d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -105,17 +105,7 @@
 
         updateUdfpsController();
         if (mUdfpsController == null) {
-            mAuthController.addCallback(new AuthController.Callback() {
-                @Override
-                public void onAllAuthenticatorsRegistered() {
-                    updateUdfpsController();
-                }
-
-                @Override
-                public void onEnrollmentsChanged() {
-                    updateUdfpsController();
-                }
-            });
+            mAuthController.addCallback(mAuthControllerCallback);
         }
     }
 
@@ -128,6 +118,11 @@
     }
 
     @Override
+    public void destroy() {
+        mAuthController.removeCallback(mAuthControllerCallback);
+    }
+
+    @Override
     public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
         int screenState = newState.screenState(mParameters);
         mDozeHost.cancelGentleSleep();
@@ -234,4 +229,16 @@
             mWakeLock.setAcquired(false);
         }
     }
+
+    private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
+        @Override
+        public void onAllAuthenticatorsRegistered() {
+            updateUdfpsController();
+        }
+
+        @Override
+        public void onEnrollmentsChanged() {
+            updateUdfpsController();
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index dd91f6f..1ba6e34 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -95,6 +95,10 @@
 
     /***************************************/
     // 500 - quick settings
+    /**
+     * @deprecated Not needed anymore
+     */
+    @Deprecated
     public static final BooleanFlag NEW_USER_SWITCHER =
             new BooleanFlag(500, true);
 
@@ -144,6 +148,11 @@
     public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, false);
     public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true);
 
+    // 1000 - dock
+    public static final BooleanFlag SIMULATE_DOCK_THROUGH_CHARGING =
+            new BooleanFlag(1000, true);
+
+
     // Pay no attention to the reflection behind the curtain.
     // ========================== Curtain ==========================
     // |                                                           |
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayoutLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayoutLite.java
index f1e5b08..42230ae 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayoutLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayoutLite.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.globalactions;
 
-import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
-
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
@@ -33,15 +31,9 @@
  * ConstraintLayout implementation of the button layout created by the global actions dialog.
  */
 public class GlobalActionsLayoutLite extends GlobalActionsLayout {
-    private final int mMaxColumns;
-    private final int mMaxRows;
 
     public GlobalActionsLayoutLite(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mMaxColumns = getResources().getInteger(
-                com.android.systemui.R.integer.power_menu_lite_max_columns);
-        mMaxRows = getResources().getInteger(
-                com.android.systemui.R.integer.power_menu_lite_max_rows);
         setOnClickListener(v -> { }); // Prevent parent onClickListener from triggering
     }
 
@@ -60,10 +52,13 @@
     @Override
     public void onUpdateList() {
         super.onUpdateList();
-        int nElementsWrap = (getCurrentRotation() == ROTATION_NONE) ? mMaxColumns : mMaxRows;
+        int nElementsWrap = getResources().getInteger(
+                com.android.systemui.R.integer.power_menu_lite_max_columns);
         int nChildren = getListView().getChildCount() - 1; // don't count flow element
-        if (getCurrentRotation() != ROTATION_NONE && nChildren > mMaxRows) {
-            // up to 4 elements can fit in a row in landscape, otherwise limit for balance
+
+        // Avoid having just one action on the last row if there are more than 2 columns because
+        // it looks unbalanced. Instead, bring the column size down to balance better.
+        if (nChildren == nElementsWrap + 1 && nElementsWrap > 2) {
             nElementsWrap -= 1;
         }
         Flow flow = findViewById(R.id.list_flow);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index b3e6682..ce7a697 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -371,13 +371,6 @@
         // Output switcher chip
         ViewGroup seamlessView = mMediaViewHolder.getSeamless();
         seamlessView.setVisibility(View.VISIBLE);
-        seamlessView.setOnClickListener(
-                v -> {
-                    if (!mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
-                        mMediaOutputDialogFactory.create(data.getPackageName(), true,
-                                mMediaViewHolder.getSeamlessButton());
-                    }
-                });
         ImageView iconView = mMediaViewHolder.getSeamlessIcon();
         TextView deviceName = mMediaViewHolder.getSeamlessText();
         final MediaDeviceData device = data.getDevice();
@@ -387,8 +380,8 @@
         final float seamlessAlpha = seamlessDisabled ? DISABLED_ALPHA : 1.0f;
         mMediaViewHolder.getSeamlessButton().setAlpha(seamlessAlpha);
         seamlessView.setEnabled(!seamlessDisabled);
-        String deviceString = null;
-        if (device != null && device.getEnabled()) {
+        CharSequence deviceString = mContext.getString(R.string.media_seamless_other_device);
+        if (device != null) {
             Drawable icon = device.getIcon();
             if (icon instanceof AdaptiveIcon) {
                 AdaptiveIcon aIcon = (AdaptiveIcon) icon;
@@ -399,13 +392,32 @@
             }
             deviceString = device.getName();
         } else {
-            // Reset to default
-            Log.w(TAG, "Device is null or not enabled: " + device + ", not binding output chip.");
+            // Set to default icon
             iconView.setImageResource(R.drawable.ic_media_home_devices);
-            deviceString =  mContext.getString(R.string.media_seamless_other_device);
         }
         deviceName.setText(deviceString);
         seamlessView.setContentDescription(deviceString);
+        seamlessView.setOnClickListener(
+                v -> {
+                    if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+                        return;
+                    }
+                    if (device.getIntent() != null) {
+                        if (device.getIntent().isActivity()) {
+                            mActivityStarter.startActivity(
+                                    device.getIntent().getIntent(), true);
+                        } else {
+                            try {
+                                device.getIntent().send();
+                            } catch (PendingIntent.CanceledException e) {
+                                Log.e(TAG, "Device pending intent was canceled");
+                            }
+                        }
+                    } else {
+                        mMediaOutputDialogFactory.create(data.getPackageName(), true,
+                                mMediaViewHolder.getSeamlessButton());
+                    }
+            });
 
         // Dismiss
         mMediaViewHolder.getDismissText().setAlpha(isDismissible ? 1 : DISABLED_ALPHA);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 4b8dfde..500e82e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -164,8 +164,17 @@
 )
 
 /** State of the media device. */
-data class MediaDeviceData(
+data class MediaDeviceData
+@JvmOverloads constructor(
+    /** Whether or not to enable the chip */
     val enabled: Boolean,
+
+    /** Device icon to show in the chip */
     val icon: Drawable?,
-    val name: String?
+
+    /** Device display name */
+    val name: CharSequence?,
+
+    /** Optional intent to override the default output switcher for this control */
+    val intent: PendingIntent? = null
 )
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 240ca36..e1ff110 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -27,8 +27,6 @@
 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.graphics.ImageDecoder
 import android.graphics.drawable.Icon
@@ -129,7 +127,7 @@
     private val useQsMediaPlayer: Boolean,
     private val systemClock: SystemClock,
     private val tunerService: TunerService,
-    private val mediaFlags: MediaFlags,
+    private val mediaFlags: MediaFlags
 ) : Dumpable, BcSmartspaceDataPlugin.SmartspaceTargetListener {
 
     companion object {
@@ -170,20 +168,9 @@
 
     /**
      * Check whether this notification is an RCN
-     * TODO(b/204910409) implement new API for explicitly declaring this
      */
     private fun isRemoteCastNotification(sbn: StatusBarNotification): Boolean {
-        val pm = context.packageManager
-        try {
-            val info = pm.getApplicationInfo(sbn.packageName, PackageManager.MATCH_SYSTEM_ONLY)
-            if (info.privateFlags and ApplicationInfo.PRIVATE_FLAG_PRIVILEGED != 0) {
-                val extras = sbn.notification.extras
-                if (extras.containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME)) {
-                    return true
-                }
-            }
-        } catch (e: PackageManager.NameNotFoundException) { }
-        return false
+        return sbn.notification.extras.containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)
     }
 
     @Inject
@@ -597,6 +584,25 @@
             artist = HybridGroupManager.resolveText(notif)
         }
 
+        // Device name (used for remote cast notifications)
+        var device: MediaDeviceData? = null
+        if (isRemoteCastNotification(sbn)) {
+            val extras = sbn.notification.extras
+            val deviceName = extras.getCharSequence(Notification.EXTRA_MEDIA_REMOTE_DEVICE, null)
+            val deviceIcon = extras.getInt(Notification.EXTRA_MEDIA_REMOTE_ICON, -1)
+            val deviceIntent = extras.getParcelable(Notification.EXTRA_MEDIA_REMOTE_INTENT)
+                    as PendingIntent?
+            Log.d(TAG, "$key is RCN for $deviceName")
+
+            if (deviceName != null && deviceIcon > -1) {
+                // Name and icon must be present, but intent may be null
+                val enabled = deviceIntent != null && deviceIntent.isActivity
+                val deviceDrawable = Icon.createWithResource(sbn.packageName, deviceIcon)
+                        .loadDrawable(sbn.getPackageContext(context))
+                device = MediaDeviceData(enabled, deviceDrawable, deviceName, deviceIntent)
+            }
+        }
+
         // Control buttons
         // If flag is enabled and controller has a PlaybackState, create actions from session info
         // Otherwise, use the notification actions
@@ -624,7 +630,7 @@
             val active = mediaEntries[key]?.active ?: true
             onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, bgColor, app,
                     smallIcon, artist, song, artWorkIcon, actionIcons, actionsToShowCollapsed,
-                    semanticActions, sbn.packageName, token, notif.contentIntent, null,
+                    semanticActions, sbn.packageName, token, notif.contentIntent, device,
                     active, resumeAction = resumeAction, playbackLocation = playbackLocation,
                     notificationKey = key, hasCheckedForResume = hasCheckedForResume,
                     isPlaying = isPlaying, isClearable = sbn.isClearable(),
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index f972da5..ffae898 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -81,6 +81,12 @@
         var entry = entries[key]
         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
+                // listen for updates, but process once to push updates to listeners
+                processDevice(key, oldKey, data.device)
+                return
+            }
             val controller = data.token?.let {
                 controllerFactory.create(it)
             }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index a9e9f0f..8731a2c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -153,6 +153,10 @@
 
     @VisibleForTesting
     void refresh() {
+        refresh(false);
+    }
+
+    void refresh(boolean deviceSetChanged) {
         // Update header icon
         final int iconRes = getHeaderIconRes();
         final IconCompat iconCompat = getHeaderIcon();
@@ -190,7 +194,8 @@
         }
         if (!mAdapter.isDragging() && !mAdapter.isAnimating()) {
             int currentActivePosition = mAdapter.getCurrentActivePosition();
-            if (currentActivePosition >= 0 && currentActivePosition < mAdapter.getItemCount()) {
+            if (!deviceSetChanged && currentActivePosition >= 0
+                    && currentActivePosition < mAdapter.getItemCount()) {
                 mAdapter.notifyItemChanged(currentActivePosition);
             } else {
                 mAdapter.notifyDataSetChanged();
@@ -232,6 +237,11 @@
     }
 
     @Override
+    public void onDeviceListChanged() {
+        mMainThreadHandler.post(() -> refresh(true));
+    }
+
+    @Override
     public void dismissDialog() {
         dismiss();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 2caecf2..4961711 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -171,7 +171,7 @@
     @Override
     public void onDeviceListUpdate(List<MediaDevice> devices) {
         buildMediaDevices(devices);
-        mCallback.onRouteChanged();
+        mCallback.onDeviceListChanged();
     }
 
     @Override
@@ -570,11 +570,16 @@
         void onMediaStoppedOrPaused();
 
         /**
-         * Override to handle the device updating.
+         * Override to handle the device status or attributes updating.
          */
         void onRouteChanged();
 
         /**
+         * Override to handle the devices set updating.
+         */
+        void onDeviceListChanged();
+
+        /**
          * Override to dismiss dialog.
          */
         void dismissDialog();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 98b49b1..aa1117c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -31,6 +31,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
@@ -215,9 +216,11 @@
     /** @return {@code true} if taskbar is enabled, false otherwise */
     private boolean initializeTaskbarIfNecessary() {
         if (mIsTablet) {
+            Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary");
             // Remove navigation bar when taskbar is showing
             removeNavigationBar(mContext.getDisplayId());
             mTaskbarDelegate.init(mContext.getDisplayId());
+            Trace.endSection();
         } else {
             mTaskbarDelegate.destroy();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java b/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
deleted file mode 100644
index 18d28bf..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.database.DataSetObserver;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.ListAdapter;
-
-import com.android.systemui.R;
-
-/**
- * Similar to a ListView, but it will show only as many items as fit on screen and
- * bind those instead of scrolling.
- */
-public class AutoSizingList extends LinearLayout {
-
-    private static final String TAG = "AutoSizingList";
-    private final int mItemSize;
-    private final Handler mHandler;
-
-    @Nullable
-    private ListAdapter mAdapter;
-    private int mCount;
-    private boolean mEnableAutoSizing;
-
-    public AutoSizingList(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-
-        mHandler = new Handler();
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AutoSizingList);
-        mItemSize = a.getDimensionPixelSize(R.styleable.AutoSizingList_itemHeight, 0);
-        mEnableAutoSizing = a.getBoolean(R.styleable.AutoSizingList_enableAutoSizing, true);
-        a.recycle();
-    }
-
-    public void setAdapter(ListAdapter adapter) {
-        if (mAdapter != null) {
-            mAdapter.unregisterDataSetObserver(mDataObserver);
-        }
-        mAdapter = adapter;
-        if (adapter != null) {
-            adapter.registerDataSetObserver(mDataObserver);
-        }
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int requestedHeight = MeasureSpec.getSize(heightMeasureSpec);
-        if (requestedHeight != 0) {
-            int count = getItemCount(requestedHeight);
-            if (mCount != count) {
-                postRebindChildren();
-                mCount = count;
-            }
-        }
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    private int getItemCount(int requestedHeight) {
-        int desiredCount = getDesiredCount();
-        return mEnableAutoSizing ? Math.min(requestedHeight / mItemSize, desiredCount)
-                : desiredCount;
-    }
-
-    private int getDesiredCount() {
-        return mAdapter != null ? mAdapter.getCount() : 0;
-    }
-
-    private void postRebindChildren() {
-        mHandler.post(mBindChildren);
-    }
-
-    private void rebindChildren() {
-        if (mAdapter == null) {
-            return;
-        }
-        for (int i = 0; i < mCount; i++) {
-            View v = i < getChildCount() ? getChildAt(i) : null;
-            View newView = mAdapter.getView(i, v, this);
-            if (newView != v) {
-                if (v != null) {
-                    removeView(v);
-                }
-                addView(newView, i);
-            }
-        }
-        // Ditch extra views.
-        while (getChildCount() > mCount) {
-            removeViewAt(getChildCount() - 1);
-        }
-    }
-
-    private final Runnable mBindChildren = new Runnable() {
-        @Override
-        public void run() {
-            rebindChildren();
-        }
-    };
-
-    private final DataSetObserver mDataObserver = new DataSetObserver() {
-        @Override
-        public void onChanged() {
-            if (mCount > getDesiredCount()) {
-                mCount = getDesiredCount();
-            }
-            postRebindChildren();
-        }
-
-        @Override
-        public void onInvalidated() {
-            postRebindChildren();
-        }
-    };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 707313f..f868055 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -44,7 +44,6 @@
     private final float[] mFancyClippingRadii = new float[] {0, 0, 0, 0, 0, 0, 0, 0};
     private  final Path mFancyClippingPath = new Path();
     private int mHeightOverride = -1;
-    private View mQSDetail;
     private QuickStatusBarHeader mHeader;
     private float mQsExpansion;
     private QSCustomizer mQSCustomizer;
@@ -63,7 +62,6 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mQSPanelContainer = findViewById(R.id.expanded_qs_scroll_view);
-        mQSDetail = findViewById(R.id.qs_detail);
         mHeader = findViewById(R.id.header);
         mQSCustomizer = findViewById(R.id.qs_customize);
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
@@ -175,9 +173,6 @@
         int height = calculateContainerHeight();
         int scrollBottom = calculateContainerBottom();
         setBottom(getTop() + height);
-        mQSDetail.setBottom(getTop() + scrollBottom);
-        int qsDetailBottomMargin = ((MarginLayoutParams) mQSDetail.getLayoutParams()).bottomMargin;
-        mQSDetail.setBottom(getTop() + scrollBottom - qsDetailBottomMargin);
     }
 
     protected int calculateContainerHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
deleted file mode 100644
index 04e2252..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_MORE_SETTINGS;
-
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorListenerAdapter;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.drawable.Animatable;
-import android.util.AttributeSet;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.view.WindowInsets;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.Switch;
-import android.widget.TextView;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.policy.SystemBarUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
-import com.android.systemui.plugins.qs.QSContainerController;
-import com.android.systemui.statusbar.CommandQueue;
-
-public class QSDetail extends LinearLayout {
-
-    private static final String TAG = "QSDetail";
-    private static final long FADE_DURATION = 300;
-
-    private final SparseArray<View> mDetailViews = new SparseArray<>();
-    private final UiEventLogger mUiEventLogger = QSEvents.INSTANCE.getQsUiEventsLogger();
-
-    private ViewGroup mDetailContent;
-    private FalsingManager mFalsingManager;
-    protected TextView mDetailSettingsButton;
-    protected TextView mDetailDoneButton;
-    @VisibleForTesting
-    QSDetailClipper mClipper;
-    @Nullable
-    private DetailAdapter mDetailAdapter;
-    private QSPanelController mQsPanelController;
-
-    protected View mQsDetailHeader;
-    protected TextView mQsDetailHeaderTitle;
-    private ViewStub mQsDetailHeaderSwitchStub;
-    @Nullable
-    private Switch mQsDetailHeaderSwitch;
-    protected ImageView mQsDetailHeaderProgress;
-
-    @Nullable
-    protected QSTileHost mHost;
-
-    private boolean mScanState;
-    private boolean mClosingDetail;
-    private boolean mFullyExpanded;
-    private QuickStatusBarHeader mHeader;
-    private boolean mTriggeredExpand;
-    private boolean mShouldAnimate;
-    private int mOpenX;
-    private int mOpenY;
-    private boolean mAnimatingOpen;
-    private boolean mSwitchState;
-    private QSFooter mFooter;
-
-    @Nullable
-    private QSContainerController mQsContainerController;
-
-    public QSDetail(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        FontSizeUtils.updateFontSize(mDetailDoneButton, R.dimen.qs_detail_button_text_size);
-        FontSizeUtils.updateFontSize(mDetailSettingsButton, R.dimen.qs_detail_button_text_size);
-
-        for (int i = 0; i < mDetailViews.size(); i++) {
-            mDetailViews.valueAt(i).dispatchConfigurationChanged(newConfig);
-        }
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mDetailContent = findViewById(android.R.id.content);
-        mDetailSettingsButton = findViewById(android.R.id.button2);
-        mDetailDoneButton = findViewById(android.R.id.button1);
-
-        mQsDetailHeader = findViewById(R.id.qs_detail_header);
-        mQsDetailHeaderTitle = (TextView) mQsDetailHeader.findViewById(android.R.id.title);
-        mQsDetailHeaderSwitchStub = mQsDetailHeader.findViewById(R.id.toggle_stub);
-        mQsDetailHeaderProgress = findViewById(R.id.qs_detail_header_progress);
-
-        updateDetailText();
-
-        mClipper = new QSDetailClipper(this);
-    }
-
-    public void setContainerController(QSContainerController controller) {
-        mQsContainerController = controller;
-    }
-
-    /** */
-    public void setQsPanel(QSPanelController panelController, QuickStatusBarHeader header,
-            QSFooter footer, FalsingManager falsingManager) {
-        mQsPanelController = panelController;
-        mHeader = header;
-        mFooter = footer;
-        mHeader.setCallback(mQsPanelCallback);
-        mQsPanelController.setCallback(mQsPanelCallback);
-        mFalsingManager = falsingManager;
-    }
-
-    public void setHost(QSTileHost host) {
-        mHost = host;
-    }
-    public boolean isShowingDetail() {
-        return mDetailAdapter != null;
-    }
-
-    public void setFullyExpanded(boolean fullyExpanded) {
-        mFullyExpanded = fullyExpanded;
-    }
-
-    public void setExpanded(boolean qsExpanded) {
-        if (!qsExpanded) {
-            mTriggeredExpand = false;
-        }
-    }
-
-    private void updateDetailText() {
-        int resId = mDetailAdapter != null ? mDetailAdapter.getDoneText() : Resources.ID_NULL;
-        mDetailDoneButton.setText(
-                (resId != Resources.ID_NULL) ? resId : R.string.quick_settings_done);
-        resId = mDetailAdapter != null ? mDetailAdapter.getSettingsText() : Resources.ID_NULL;
-        mDetailSettingsButton.setText(
-                (resId != Resources.ID_NULL) ? resId : R.string.quick_settings_more_settings);
-    }
-
-    public void updateResources() {
-        updateDetailText();
-        MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
-        lp.topMargin = SystemBarUtils.getQuickQsOffsetHeight(mContext);
-        setLayoutParams(lp);
-    }
-
-    @Override
-    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        int bottomNavBar = insets.getInsets(WindowInsets.Type.navigationBars()).bottom;
-        MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
-        lp.bottomMargin = bottomNavBar;
-        setLayoutParams(lp);
-        return super.onApplyWindowInsets(insets);
-    }
-
-    public boolean isClosingDetail() {
-        return mClosingDetail;
-    }
-
-    public interface Callback {
-        /** Handle an event of showing detail. */
-        void onShowingDetail(@Nullable DetailAdapter detail, int x, int y);
-        void onToggleStateChanged(boolean state);
-        void onScanStateChanged(boolean state);
-    }
-
-    /** Handle an event of showing detail. */
-    public void handleShowingDetail(final @Nullable DetailAdapter adapter, int x, int y,
-            boolean toggleQs) {
-        final boolean showingDetail = adapter != null;
-        final boolean wasShowingDetail = mDetailAdapter != null;
-        setClickable(showingDetail);
-        if (showingDetail) {
-            setupDetailHeader(adapter);
-            if (toggleQs && !mFullyExpanded) {
-                mTriggeredExpand = true;
-                Dependency.get(CommandQueue.class).animateExpandSettingsPanel(null);
-            } else {
-                mTriggeredExpand = false;
-            }
-            mShouldAnimate = adapter.shouldAnimate();
-            mOpenX = x;
-            mOpenY = y;
-        } else {
-            // Ensure we collapse into the same point we opened from.
-            x = mOpenX;
-            y = mOpenY;
-            if (toggleQs && mTriggeredExpand) {
-                Dependency.get(CommandQueue.class).animateCollapsePanels();
-                mTriggeredExpand = false;
-            }
-            // Always animate on close, even if the last opened detail adapter had shouldAnimate()
-            // return false. This is necessary to avoid a race condition which could leave the
-            // keyguard in a bad state where QS remains visible underneath the notifications, clock,
-            // and status area.
-            mShouldAnimate = true;
-        }
-
-        boolean visibleDiff = wasShowingDetail != showingDetail;
-        if (!visibleDiff && !wasShowingDetail) return;  // already in right state
-        AnimatorListener listener;
-        if (showingDetail) {
-            int viewCacheIndex = adapter.getMetricsCategory();
-            View detailView = adapter.createDetailView(mContext, mDetailViews.get(viewCacheIndex),
-                    mDetailContent);
-            if (detailView == null) throw new IllegalStateException("Must return detail view");
-
-            setupDetailFooter(adapter);
-
-            mDetailContent.removeAllViews();
-            mDetailContent.addView(detailView);
-            mDetailViews.put(viewCacheIndex, detailView);
-            Dependency.get(MetricsLogger.class).visible(adapter.getMetricsCategory());
-            mUiEventLogger.log(adapter.openDetailEvent());
-            announceForAccessibility(mContext.getString(
-                    R.string.accessibility_quick_settings_detail,
-                    adapter.getTitle()));
-            mDetailAdapter = adapter;
-            listener = mHideGridContentWhenDone;
-            setVisibility(View.VISIBLE);
-            updateDetailText();
-        } else {
-            if (wasShowingDetail) {
-                Dependency.get(MetricsLogger.class).hidden(mDetailAdapter.getMetricsCategory());
-                mUiEventLogger.log(mDetailAdapter.closeDetailEvent());
-            }
-            mClosingDetail = true;
-            mDetailAdapter = null;
-            listener = mTeardownDetailWhenDone;
-            // Only update visibility if already expanded. Otherwise, a race condition can cause the
-            // keyguard to enter a bad state where the QS tiles are displayed underneath the
-            // notifications, clock, and status area.
-            if (mQsPanelController.isExpanded()) {
-                mHeader.setVisibility(View.VISIBLE);
-                mFooter.setVisibility(View.VISIBLE);
-                mQsPanelController.setGridContentVisibility(true);
-                mQsPanelCallback.onScanStateChanged(false);
-            }
-        }
-        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
-        animateDetailVisibleDiff(x, y, visibleDiff, listener);
-        if (mQsContainerController != null) {
-            mQsContainerController.setDetailShowing(showingDetail);
-        }
-    }
-
-    protected void animateDetailVisibleDiff(int x, int y, boolean visibleDiff, AnimatorListener listener) {
-        if (visibleDiff) {
-            mAnimatingOpen = mDetailAdapter != null;
-            if (mFullyExpanded || mDetailAdapter != null) {
-                setAlpha(1);
-                mClipper.updateCircularClip(mShouldAnimate, x, y, mDetailAdapter != null, listener);
-            } else {
-                animate().alpha(0)
-                        .setDuration(mShouldAnimate ? FADE_DURATION : 0)
-                        .setListener(listener)
-                        .start();
-            }
-        }
-    }
-
-    protected void setupDetailFooter(DetailAdapter adapter) {
-        final Intent settingsIntent = adapter.getSettingsIntent();
-        mDetailSettingsButton.setVisibility(settingsIntent != null ? VISIBLE : GONE);
-        mDetailSettingsButton.setOnClickListener(v -> {
-            if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
-                return;
-            }
-            Dependency.get(MetricsLogger.class).action(ACTION_QS_MORE_SETTINGS,
-                    adapter.getMetricsCategory());
-            mUiEventLogger.log(adapter.moreSettingsEvent());
-            Dependency.get(ActivityStarter.class)
-                    .postStartActivityDismissingKeyguard(settingsIntent, 0);
-        });
-        mDetailDoneButton.setOnClickListener(v -> {
-            if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
-                return;
-            }
-            announceForAccessibility(
-                    mContext.getString(R.string.accessibility_desc_quick_settings));
-            if (!adapter.onDoneButtonClicked()) {
-                mQsPanelController.closeDetail();
-            }
-        });
-    }
-
-    protected void setupDetailHeader(final DetailAdapter adapter) {
-        mQsDetailHeaderTitle.setText(adapter.getTitle());
-        final Boolean toggleState = adapter.getToggleState();
-        if (toggleState == null) {
-            if (mQsDetailHeaderSwitch != null) mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
-            mQsDetailHeader.setClickable(false);
-        } else {
-            if (mQsDetailHeaderSwitch == null) {
-                mQsDetailHeaderSwitch = (Switch) mQsDetailHeaderSwitchStub.inflate();
-            }
-            mQsDetailHeaderSwitch.setVisibility(VISIBLE);
-            handleToggleStateChanged(toggleState, adapter.getToggleEnabled());
-            mQsDetailHeader.setClickable(true);
-            mQsDetailHeader.setOnClickListener(v -> {
-                if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
-                    return;
-                }
-                boolean checked = !mQsDetailHeaderSwitch.isChecked();
-                mQsDetailHeaderSwitch.setChecked(checked);
-                adapter.setToggleState(checked);
-            });
-        }
-    }
-
-    private void handleToggleStateChanged(boolean state, boolean toggleEnabled) {
-        mSwitchState = state;
-        if (mAnimatingOpen) {
-            return;
-        }
-        if (mQsDetailHeaderSwitch != null) mQsDetailHeaderSwitch.setChecked(state);
-        mQsDetailHeader.setEnabled(toggleEnabled);
-        if (mQsDetailHeaderSwitch != null) mQsDetailHeaderSwitch.setEnabled(toggleEnabled);
-    }
-
-    private void handleScanStateChanged(boolean state) {
-        if (mScanState == state) return;
-        mScanState = state;
-        final Animatable anim = (Animatable) mQsDetailHeaderProgress.getDrawable();
-        if (state) {
-            mQsDetailHeaderProgress.animate().cancel();
-            mQsDetailHeaderProgress.animate()
-                    .alpha(1)
-                    .withEndAction(anim::start)
-                    .start();
-        } else {
-            mQsDetailHeaderProgress.animate().cancel();
-            mQsDetailHeaderProgress.animate()
-                    .alpha(0f)
-                    .withEndAction(anim::stop)
-                    .start();
-        }
-    }
-
-    private void checkPendingAnimations() {
-        handleToggleStateChanged(mSwitchState,
-                            mDetailAdapter != null && mDetailAdapter.getToggleEnabled());
-    }
-
-    protected Callback mQsPanelCallback = new Callback() {
-        @Override
-        public void onToggleStateChanged(final boolean state) {
-            post(new Runnable() {
-                @Override
-                public void run() {
-                    handleToggleStateChanged(state,
-                            mDetailAdapter != null && mDetailAdapter.getToggleEnabled());
-                }
-            });
-        }
-
-        @Override
-        public void onShowingDetail(
-                final @Nullable DetailAdapter detail, final int x, final int y) {
-            post(new Runnable() {
-                @Override
-                public void run() {
-                    if (isAttachedToWindow()) {
-                        handleShowingDetail(detail, x, y, false /* toggleQs */);
-                    }
-                }
-            });
-        }
-
-        @Override
-        public void onScanStateChanged(final boolean state) {
-            post(new Runnable() {
-                @Override
-                public void run() {
-                    handleScanStateChanged(state);
-                }
-            });
-        }
-    };
-
-    private final AnimatorListenerAdapter mHideGridContentWhenDone = new AnimatorListenerAdapter() {
-        public void onAnimationCancel(Animator animation) {
-            // If we have been cancelled, remove the listener so that onAnimationEnd doesn't get
-            // called, this will avoid accidentally turning off the grid when we don't want to.
-            animation.removeListener(this);
-            mAnimatingOpen = false;
-            checkPendingAnimations();
-        };
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            // Only hide content if still in detail state.
-            if (mDetailAdapter != null) {
-                mQsPanelController.setGridContentVisibility(false);
-                mHeader.setVisibility(View.INVISIBLE);
-                mFooter.setVisibility(View.INVISIBLE);
-            }
-            mAnimatingOpen = false;
-            checkPendingAnimations();
-        }
-    };
-
-    private final AnimatorListenerAdapter mTeardownDetailWhenDone = new AnimatorListenerAdapter() {
-        public void onAnimationEnd(Animator animation) {
-            mDetailContent.removeAllViews();
-            setVisibility(View.INVISIBLE);
-            mClosingDetail = false;
-        };
-    };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
index 43136d3..b02efba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailClipper.java
@@ -25,7 +25,7 @@
 
 import androidx.annotation.Nullable;
 
-/** Helper for quick settings detail panel clip animations. **/
+/** Helper for quick settings detail panel clip animations. Currently used by the customizer **/
 public class QSDetailClipper {
 
     private final View mDetail;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java
deleted file mode 100644
index afd4f0f..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailDisplayer.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import androidx.annotation.Nullable;
-
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.plugins.qs.DetailAdapter;
-
-import javax.inject.Inject;
-
-/**
- * Proxy class for talking with the QSPanel and showing custom content within it.
- */
-@SysUISingleton
-public class QSDetailDisplayer {
-    @Nullable
-    private QSPanelController mQsPanelController;
-
-    @Inject
-    public QSDetailDisplayer() {
-    }
-
-    public void setQsPanelController(@Nullable QSPanelController qsPanelController) {
-        mQsPanelController = qsPanelController;
-    }
-
-    /** Show the supplied DetailAdapter in the Quick Settings. */
-    public void showDetailAdapter(DetailAdapter detailAdapter, int x, int y) {
-        if (mQsPanelController != null) {
-            mQsPanelController.showDetailAdapter(detailAdapter, x, y);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
deleted file mode 100644
index eb3247b..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.Nullable;
-
-import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSTile;
-
-/**
- * Quick settings common detail view with line items.
- */
-public class QSDetailItems extends FrameLayout {
-    private static final String TAG = "QSDetailItems";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private final int mQsDetailIconOverlaySize;
-    private final Context mContext;
-    private final H mHandler = new H();
-    private final Adapter mAdapter = new Adapter();
-
-    private String mTag;
-    @Nullable
-    private Callback mCallback;
-    private boolean mItemsVisible = true;
-    private AutoSizingList mItemList;
-    private View mEmpty;
-    private TextView mEmptyText;
-    private ImageView mEmptyIcon;
-
-    private Item[] mItems;
-
-    public QSDetailItems(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mContext = context;
-        mTag = TAG;
-        mQsDetailIconOverlaySize = (int) getResources().getDimension(
-                R.dimen.qs_detail_icon_overlay_size);
-    }
-
-    public static QSDetailItems convertOrInflate(Context context, View convert, ViewGroup parent) {
-        if (convert instanceof QSDetailItems) {
-            return (QSDetailItems) convert;
-        }
-        return (QSDetailItems) LayoutInflater.from(context).inflate(R.layout.qs_detail_items,
-                parent, false);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mItemList = findViewById(android.R.id.list);
-        mItemList.setVisibility(GONE);
-        mItemList.setAdapter(mAdapter);
-        mEmpty = findViewById(android.R.id.empty);
-        mEmpty.setVisibility(GONE);
-        mEmptyText = mEmpty.findViewById(android.R.id.title);
-        mEmptyIcon = mEmpty.findViewById(android.R.id.icon);
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        FontSizeUtils.updateFontSize(mEmptyText, R.dimen.qs_detail_empty_text_size);
-        int count = mItemList.getChildCount();
-        for (int i = 0; i < count; i++) {
-            View item = mItemList.getChildAt(i);
-            FontSizeUtils.updateFontSize(item, android.R.id.title,
-                    R.dimen.qs_detail_item_primary_text_size);
-            FontSizeUtils.updateFontSize(item, android.R.id.summary,
-                    R.dimen.qs_detail_item_secondary_text_size);
-        }
-    }
-
-    public void setTagSuffix(String suffix) {
-        mTag = TAG + "." + suffix;
-    }
-
-    public void setEmptyState(int icon, int text) {
-        mEmptyIcon.post(() -> {
-            mEmptyIcon.setImageResource(icon);
-            mEmptyText.setText(text);
-        });
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        if (DEBUG) Log.d(mTag, "onAttachedToWindow");
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
-        mCallback = null;
-    }
-
-    public void setCallback(Callback callback) {
-        mHandler.removeMessages(H.SET_CALLBACK);
-        mHandler.obtainMessage(H.SET_CALLBACK, callback).sendToTarget();
-    }
-
-    /** Set items. */
-    public void setItems(@Nullable Item[] items) {
-        mHandler.removeMessages(H.SET_ITEMS);
-        mHandler.obtainMessage(H.SET_ITEMS, items).sendToTarget();
-    }
-
-    public void setItemsVisible(boolean visible) {
-        mHandler.removeMessages(H.SET_ITEMS_VISIBLE);
-        mHandler.obtainMessage(H.SET_ITEMS_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
-    }
-
-    private void handleSetCallback(Callback callback) {
-        mCallback = callback;
-    }
-
-    private void handleSetItems(Item[] items) {
-        final int itemCount = items != null ? items.length : 0;
-        mEmpty.setVisibility(itemCount == 0 ? VISIBLE : GONE);
-        mItemList.setVisibility(itemCount == 0 ? GONE : VISIBLE);
-        mItems = items;
-        mAdapter.notifyDataSetChanged();
-    }
-
-    private void handleSetItemsVisible(boolean visible) {
-        if (mItemsVisible == visible) return;
-        mItemsVisible = visible;
-        for (int i = 0; i < mItemList.getChildCount(); i++) {
-            mItemList.getChildAt(i).setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
-        }
-    }
-
-    private class Adapter extends BaseAdapter {
-
-        @Override
-        public int getCount() {
-            return mItems != null ? mItems.length : 0;
-        }
-
-        @Override
-        public Object getItem(int position) {
-            return mItems[position];
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return 0;
-        }
-
-        @Override
-        public View getView(int position, View view, ViewGroup parent) {
-            final Item item = mItems[position];
-            if (view == null) {
-                view = LayoutInflater.from(mContext).inflate(R.layout.qs_detail_item, parent,
-                        false);
-            }
-            view.setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
-            final ImageView iv = (ImageView) view.findViewById(android.R.id.icon);
-            if (item.icon != null) {
-                iv.setImageDrawable(item.icon.getDrawable(iv.getContext()));
-            } else {
-                iv.setImageResource(item.iconResId);
-            }
-            iv.getOverlay().clear();
-            if (item.overlay != null) {
-                item.overlay.setBounds(0, 0, mQsDetailIconOverlaySize, mQsDetailIconOverlaySize);
-                iv.getOverlay().add(item.overlay);
-            }
-            final TextView title = (TextView) view.findViewById(android.R.id.title);
-            title.setText(item.line1);
-            final TextView summary = (TextView) view.findViewById(android.R.id.summary);
-            final boolean twoLines = !TextUtils.isEmpty(item.line2);
-            title.setMaxLines(twoLines ? 1 : 2);
-            summary.setVisibility(twoLines ? VISIBLE : GONE);
-            summary.setText(twoLines ? item.line2 : null);
-            view.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    if (mCallback != null) {
-                        mCallback.onDetailItemClick(item);
-                    }
-                }
-            });
-
-            final ImageView icon2 = (ImageView) view.findViewById(android.R.id.icon2);
-            if (item.canDisconnect) {
-                icon2.setImageResource(R.drawable.ic_qs_cancel);
-                icon2.setVisibility(VISIBLE);
-                icon2.setClickable(true);
-                icon2.setOnClickListener(new OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        if (mCallback != null) {
-                            mCallback.onDetailItemDisconnect(item);
-                        }
-                    }
-                });
-            } else if (item.icon2 != -1) {
-                icon2.setVisibility(VISIBLE);
-                icon2.setImageResource(item.icon2);
-                icon2.setClickable(false);
-            } else {
-                icon2.setVisibility(GONE);
-            }
-
-            return view;
-        }
-    };
-
-    private class H extends Handler {
-        private static final int SET_ITEMS = 1;
-        private static final int SET_CALLBACK = 2;
-        private static final int SET_ITEMS_VISIBLE = 3;
-
-        public H() {
-            super(Looper.getMainLooper());
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            if (msg.what == SET_ITEMS) {
-                handleSetItems((Item[]) msg.obj);
-            } else if (msg.what == SET_CALLBACK) {
-                handleSetCallback((QSDetailItems.Callback) msg.obj);
-            } else if (msg.what == SET_ITEMS_VISIBLE) {
-                handleSetItemsVisible(msg.arg1 != 0);
-            }
-        }
-    }
-
-    public static class Item {
-        public Item(int iconResId, CharSequence line1, Object tag) {
-            this.iconResId = iconResId;
-            this.line1 = line1;
-            this.tag = tag;
-        }
-
-        public int iconResId;
-        @Nullable
-        public QSTile.Icon icon;
-        @Nullable
-        public Drawable overlay;
-        public CharSequence line1;
-        @Nullable
-        public CharSequence line2;
-        public Object tag;
-        public boolean canDisconnect;
-        public int icon2 = -1;
-    }
-
-    public interface Callback {
-        void onDetailItemClick(Item item);
-        void onDetailItemDisconnect(Item item);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt b/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
index 26adf46..24bb16a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
@@ -124,13 +124,13 @@
     @UiEvent(doc = "The current user has been switched in the detail panel")
     QS_USER_SWITCH(424),
 
-    @UiEvent(doc = "User switcher QS detail panel open")
+    @UiEvent(doc = "User switcher QS dialog open")
     QS_USER_DETAIL_OPEN(425),
 
-    @UiEvent(doc = "User switcher QS detail panel closed")
+    @UiEvent(doc = "User switcher QS dialog closed")
     QS_USER_DETAIL_CLOSE(426),
 
-    @UiEvent(doc = "User switcher QS detail panel more settings pressed")
+    @UiEvent(doc = "User switcher QS dialog more settings pressed")
     QS_USER_MORE_SETTINGS(427),
 
     @UiEvent(doc = "The user has added a guest in the detail panel")
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 50952bd..ea68c40 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -33,7 +33,6 @@
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
-import android.widget.FrameLayout.LayoutParams;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
@@ -85,7 +84,6 @@
     private QSSquishinessController mQSSquishinessController;
     protected QuickStatusBarHeader mHeader;
     protected NonInterceptingScrollView mQSPanelScrollView;
-    private QSDetail mQSDetail;
     private boolean mListening;
     private QSContainerImpl mContainer;
     private int mLayoutDirection;
@@ -96,8 +94,6 @@
     private boolean mQsDisabled;
 
     private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
-    private final CommandQueue mCommandQueue;
-    private final QSDetailDisplayer mQsDetailDisplayer;
     private final MediaHost mQsMediaHost;
     private final MediaHost mQqsMediaHost;
     private final QSFragmentComponent.Factory mQsComponentFactory;
@@ -126,7 +122,6 @@
      * otherwise.
      */
     private boolean mInSplitShade;
-    private boolean mPulseExpanding;
 
     /**
      * Are we currently transitioning from lockscreen to the full shade?
@@ -150,15 +145,13 @@
     public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
             QSTileHost qsTileHost,
             StatusBarStateController statusBarStateController, CommandQueue commandQueue,
-            QSDetailDisplayer qsDetailDisplayer, @Named(QS_PANEL) MediaHost qsMediaHost,
+            @Named(QS_PANEL) MediaHost qsMediaHost,
             @Named(QUICK_QS_PANEL) MediaHost qqsMediaHost,
             KeyguardBypassController keyguardBypassController,
             QSFragmentComponent.Factory qsComponentFactory,
             QSFragmentDisableFlagsLogger qsFragmentDisableFlagsLogger,
             FalsingManager falsingManager, DumpManager dumpManager) {
         mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
-        mCommandQueue = commandQueue;
-        mQsDetailDisplayer = qsDetailDisplayer;
         mQsMediaHost = qsMediaHost;
         mQqsMediaHost = qqsMediaHost;
         mQsComponentFactory = qsComponentFactory;
@@ -209,19 +202,15 @@
                         mScrollListener.onQsPanelScrollChanged(scrollY);
                     }
         });
-        mQSDetail = view.findViewById(R.id.qs_detail);
         mHeader = view.findViewById(R.id.header);
         mQSPanelController.setHeaderContainer(view.findViewById(R.id.header_text_container));
         mFooter = qsFragmentComponent.getQSFooter();
 
-        mQsDetailDisplayer.setQsPanelController(mQSPanelController);
-
         mQSContainerImplController = qsFragmentComponent.getQSContainerImplController();
         mQSContainerImplController.init();
         mContainer = mQSContainerImplController.getView();
         mDumpManager.registerDumpable(mContainer.getClass().getName(), mContainer);
 
-        mQSDetail.setQsPanel(mQSPanelController, mHeader, mFooter, mFalsingManager);
         mQSAnimator = qsFragmentComponent.getQSAnimator();
         mQSSquishinessController = qsFragmentComponent.getQSSquishinessController();
 
@@ -237,7 +226,6 @@
                 mQSPanelController.getTileLayout().restoreInstanceState(savedInstanceState);
             }
         }
-        setHost(mHost);
         mStatusBarStateController.addCallback(this);
         onStateChanged(mStatusBarStateController.getState());
         view.addOnLayoutChangeListener(
@@ -271,7 +259,6 @@
             setListening(false);
         }
         mQSCustomizerController.setQs(null);
-        mQsDetailDisplayer.setQsPanelController(null);
         mScrollListener = null;
         mDumpManager.unregisterDumpable(mContainer.getClass().getName());
     }
@@ -352,7 +339,6 @@
     @Override
     public void setContainerController(QSContainerController controller) {
         mQSCustomizerController.setContainerController(controller);
-        mQSDetail.setContainerController(controller);
     }
 
     @Override
@@ -360,10 +346,6 @@
         return mQSCustomizerController.isCustomizing();
     }
 
-    public void setHost(QSTileHost qsh) {
-        mQSDetail.setHost(qsh);
-    }
-
     @Override
     public void disable(int displayId, int state1, int state2, boolean animate) {
         if (displayId != getContext().getDisplayId()) {
@@ -392,7 +374,6 @@
         final boolean expandVisually = expanded || mStackScrollerOverscrolling
                 || mHeaderAnimating;
         mQSPanelController.setExpanded(expanded);
-        mQSDetail.setExpanded(expanded);
         boolean keyguardShowing = isKeyguardState();
         mHeader.setVisibility((expanded || !keyguardShowing || mHeaderAnimating
                 || mShowCollapsedOnKeyguard)
@@ -444,7 +425,7 @@
 
     @Override
     public boolean isShowingDetail() {
-        return mQSCustomizerController.isCustomizing() || mQSDetail.isShowingDetail();
+        return mQSCustomizerController.isCustomizing();
     }
 
     @Override
@@ -573,7 +554,6 @@
         if (fullyCollapsed) {
             mQSPanelScrollView.setScrollY(0);
         }
-        mQSDetail.setFullyExpanded(fullyExpanded);
 
         if (!fullyExpanded) {
             // Set bounds on the QS panel so it doesn't run over the header when animating.
@@ -732,14 +712,7 @@
         if (mQSCustomizerController.isCustomizing()) {
             return getView().getHeight();
         }
-        if (mQSDetail.isClosingDetail()) {
-            LayoutParams layoutParams = (LayoutParams) mQSPanelScrollView.getLayoutParams();
-            int panelHeight = layoutParams.topMargin + layoutParams.bottomMargin +
-                    + mQSPanelScrollView.getMeasuredHeight();
-            return panelHeight + getView().getPaddingBottom();
-        } else {
-            return getView().getMeasuredHeight();
-        }
+        return getView().getMeasuredHeight();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 7c04cd4..0c854df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -41,10 +41,8 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.widget.RemeasuringLinearLayout;
 import com.android.systemui.R;
-import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.settings.brightness.BrightnessSliderController;
-import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 
@@ -80,7 +78,6 @@
     protected boolean mExpanded;
     protected boolean mListening;
 
-    private QSDetail.Callback mCallback;
     protected QSTileHost mHost;
     private final List<OnConfigurationChangedListener> mOnConfigurationChangedListeners =
             new ArrayList<>();
@@ -100,9 +97,6 @@
     private int mContentMarginEnd;
     private boolean mUsingHorizontalLayout;
 
-    private Record mDetailRecord;
-
-    private BrightnessMirrorController mBrightnessMirrorController;
     private LinearLayout mHorizontalLinearLayout;
     protected LinearLayout mHorizontalContentContainer;
 
@@ -315,24 +309,12 @@
         view.setVisibility(TunerService.parseIntegerSwitch(newValue, true) ? VISIBLE : GONE);
     }
 
-    /** */
-    public void openDetails(QSTile tile) {
-        // If there's no tile with that name (as defined in QSFactoryImpl or other QSFactory),
-        // QSFactory will not be able to create a tile and getTile will return null
-        if (tile != null) {
-            showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0});
-        }
-    }
 
     @Nullable
     View getBrightnessView() {
         return mBrightnessView;
     }
 
-    public void setCallback(QSDetail.Callback callback) {
-        mCallback = callback;
-    }
-
     /**
      * Links the footer's page indicator, which is used in landscape orientation to save space.
      *
@@ -519,25 +501,6 @@
         mListening = listening;
     }
 
-    public void showDetailAdapter(boolean show, DetailAdapter adapter, int[] locationInWindow) {
-        int xInWindow = locationInWindow[0];
-        int yInWindow = locationInWindow[1];
-        ((View) getParent()).getLocationInWindow(locationInWindow);
-
-        Record r = new Record();
-        r.detailAdapter = adapter;
-        r.x = xInWindow - locationInWindow[0];
-        r.y = yInWindow - locationInWindow[1];
-
-        locationInWindow[0] = xInWindow;
-        locationInWindow[1] = yInWindow;
-
-        showDetail(show, r);
-    }
-
-    protected void showDetail(boolean show, Record r) {
-        mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget();
-    }
 
     protected void drawTile(QSPanelControllerBase.TileRecord r, QSTile.State state) {
         r.tileView.onStateChanged(state);
@@ -565,30 +528,6 @@
             public void onStateChanged(QSTile.State state) {
                 drawTile(tileRecord, state);
             }
-
-            @Override
-            public void onShowDetail(boolean show) {
-                // Both the collapsed and full QS panels get this callback, this check determines
-                // which one should handle showing the detail.
-                if (shouldShowDetail()) {
-                    QSPanel.this.showDetail(show, tileRecord);
-                }
-            }
-
-            @Override
-            public void onToggleStateChanged(boolean state) {
-                if (mDetailRecord == tileRecord) {
-                    fireToggleStateChanged(state);
-                }
-            }
-
-            @Override
-            public void onScanStateChanged(boolean state) {
-                tileRecord.scanState = state;
-                if (mDetailRecord == tileRecord) {
-                    fireScanStateChanged(tileRecord.scanState);
-                }
-            }
         };
 
         tileRecord.tile.addCallback(callback);
@@ -605,72 +544,10 @@
         mTileLayout.removeTile(tileRecord);
     }
 
-    void closeDetail() {
-        showDetail(false, mDetailRecord);
-    }
-
     public int getGridHeight() {
         return getMeasuredHeight();
     }
 
-    protected void handleShowDetail(Record r, boolean show) {
-        if (r instanceof QSPanelControllerBase.TileRecord) {
-            handleShowDetailTile((QSPanelControllerBase.TileRecord) r, show);
-        } else {
-            int x = 0;
-            int y = 0;
-            if (r != null) {
-                x = r.x;
-                y = r.y;
-            }
-            handleShowDetailImpl(r, show, x, y);
-        }
-    }
-
-    private void handleShowDetailTile(QSPanelControllerBase.TileRecord r, boolean show) {
-        if ((mDetailRecord != null) == show && mDetailRecord == r) return;
-
-        if (show) {
-            r.detailAdapter = r.tile.getDetailAdapter();
-            if (r.detailAdapter == null) return;
-        }
-        r.tile.setDetailListening(show);
-        int x = r.tileView.getLeft() + r.tileView.getWidth() / 2;
-        int y = r.tileView.getDetailY() + mTileLayout.getOffsetTop(r) + getTop();
-        handleShowDetailImpl(r, show, x, y);
-    }
-
-    private void handleShowDetailImpl(Record r, boolean show, int x, int y) {
-        setDetailRecord(show ? r : null);
-        fireShowingDetail(show ? r.detailAdapter : null, x, y);
-    }
-
-    protected void setDetailRecord(Record r) {
-        if (r == mDetailRecord) return;
-        mDetailRecord = r;
-        final boolean scanState = mDetailRecord instanceof QSPanelControllerBase.TileRecord
-                && ((QSPanelControllerBase.TileRecord) mDetailRecord).scanState;
-        fireScanStateChanged(scanState);
-    }
-
-    private void fireShowingDetail(DetailAdapter detail, int x, int y) {
-        if (mCallback != null) {
-            mCallback.onShowingDetail(detail, x, y);
-        }
-    }
-
-    private void fireToggleStateChanged(boolean state) {
-        if (mCallback != null) {
-            mCallback.onToggleStateChanged(state);
-        }
-    }
-
-    private void fireScanStateChanged(boolean state) {
-        if (mCallback != null) {
-            mCallback.onScanStateChanged(state);
-        }
-    }
-
     QSTileLayout getTileLayout() {
         return mTileLayout;
     }
@@ -774,26 +651,16 @@
     }
 
     private class H extends Handler {
-        private static final int SHOW_DETAIL = 1;
-        private static final int SET_TILE_VISIBILITY = 2;
-        private static final int ANNOUNCE_FOR_ACCESSIBILITY = 3;
+        private static final int ANNOUNCE_FOR_ACCESSIBILITY = 1;
 
         @Override
         public void handleMessage(Message msg) {
-            if (msg.what == SHOW_DETAIL) {
-                handleShowDetail((Record) msg.obj, msg.arg1 != 0);
-            } else if (msg.what == ANNOUNCE_FOR_ACCESSIBILITY) {
+            if (msg.what == ANNOUNCE_FOR_ACCESSIBILITY) {
                 announceForAccessibility((CharSequence) msg.obj);
             }
         }
     }
 
-    protected static class Record {
-        DetailAdapter detailAdapter;
-        int x;
-        int y;
-    }
-
     public interface QSTileLayout {
         /** */
         default void saveInstanceState(Bundle outState) {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 418c4ae..6a7f3c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -36,15 +36,12 @@
 import com.android.systemui.media.MediaHierarchyManager;
 import com.android.systemui.media.MediaHost;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
-import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.settings.brightness.BrightnessController;
 import com.android.systemui.settings.brightness.BrightnessMirrorHandler;
 import com.android.systemui.settings.brightness.BrightnessSliderController;
-import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.Utils;
@@ -57,7 +54,6 @@
  */
 @QSScope
 public class QSPanelController extends QSPanelControllerBase<QSPanel> {
-    public static final String QS_REMOVE_LABELS = "sysui_remove_labels";
 
     private final QSFgsManagerFooter mQSFgsManagerFooter;
     private final QSSecurityFooter mQsSecurityFooter;
@@ -65,7 +61,6 @@
     private final QSCustomizerController mQsCustomizerController;
     private final QSTileRevealController.Factory mQsTileRevealControllerFactory;
     private final FalsingManager mFalsingManager;
-    private final CommandQueue mCommandQueue;
     private final BrightnessController mBrightnessController;
     private final BrightnessSliderController mBrightnessSliderController;
     private final BrightnessMirrorHandler mBrightnessMirrorHandler;
@@ -107,7 +102,7 @@
             DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
             QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
             BrightnessSliderController.Factory brightnessSliderFactory,
-            FalsingManager falsingManager, CommandQueue commandQueue, FeatureFlags featureFlags) {
+            FalsingManager falsingManager, FeatureFlags featureFlags) {
         super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
                 metricsLogger, uiEventLogger, qsLogger, dumpManager);
         mQSFgsManagerFooter = qsFgsManagerFooter;
@@ -116,7 +111,6 @@
         mQsCustomizerController = qsCustomizerController;
         mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
         mFalsingManager = falsingManager;
-        mCommandQueue = commandQueue;
 
         mBrightnessSliderController = brightnessSliderFactory.create(getContext(), mView);
         mView.setBrightnessView(mBrightnessSliderController.getRootView());
@@ -221,15 +215,6 @@
         return mHost;
     }
 
-
-    /** Open the details for a specific tile.. */
-    public void openDetails(String subPanel) {
-        QSTile tile = getTile(subPanel);
-        if (tile != null) {
-            mView.openDetails(tile);
-        }
-    }
-
     /** Show the device monitoring dialog. */
     public void showDeviceMonitoringDialog() {
         mQsSecurityFooter.showDeviceMonitoringDialog();
@@ -261,11 +246,6 @@
     }
 
     /** */
-    public void setCallback(QSDetail.Callback qsPanelCallback) {
-        mView.setCallback(qsPanelCallback);
-    }
-
-    /** */
     public void setGridContentVisibility(boolean visible) {
         int newVis = visible ? View.VISIBLE : View.INVISIBLE;
         setVisibility(newVis);
@@ -294,19 +274,6 @@
     }
 
     /** */
-    public void showDetailAdapter(DetailAdapter detailAdapter, int x, int y) {
-        // TODO(b/199296365)
-        // Workaround for opening detail from QQS, when there might not be enough space to
-        // display e.g. in case of multiuser detail from split shade. Currently showing detail works
-        // only for QS (mView below) and that's why expanding panel (thus showing QS instead of QQS)
-        // makes it displayed correctly.
-        if (!isExpanded()) {
-            mCommandQueue.animateExpandSettingsPanel(null);
-        }
-        mView.showDetailAdapter(true, detailAdapter, new int[]{x, y});
-    }
-
-    /** */
     public void setFooterPageIndicator(PageIndicator pageIndicator) {
         mView.setFooterPageIndicator(pageIndicator);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index d4d6da8..0bff722 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -298,21 +298,8 @@
             mQsCustomizerController.hide();
             return;
         }
-        mView.closeDetail();
     }
 
-    /** */
-    public void openDetails(String subPanel) {
-        QSTile tile = getTile(subPanel);
-        // If there's no tile with that name (as defined in QSFactoryImpl or other QSFactory),
-        // QSFactory will not be able to create a tile and getTile will return null
-        if (tile != null) {
-            mView.showDetailAdapter(
-                    true, tile.getDetailAdapter(), new int[]{mView.getWidth() / 2, 0});
-        }
-    }
-
-
     void setListening(boolean listening) {
         mView.setListening(listening);
 
@@ -429,7 +416,7 @@
     }
 
     /** */
-    public static final class TileRecord extends QSPanel.Record {
+    public static final class TileRecord {
         public TileRecord(QSTile tile, com.android.systemui.plugins.qs.QSTileView tileView) {
             this.tile = tile;
             this.tileView = tileView;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index d3f8db38..8c08873 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -39,7 +39,6 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.battery.BatteryMeterView;
-import com.android.systemui.qs.QSDetail.Callback;
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
 import com.android.systemui.statusbar.phone.StatusIconContainer;
@@ -548,10 +547,6 @@
         post(() -> setClickable(!mExpanded));
     }
 
-    public void setCallback(Callback qsPanelCallback) {
-        mHeaderQsPanel.setCallback(qsPanelCallback);
-    }
-
     private void setContentMargins(View view, int marginStart, int marginEnd) {
         MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
         lp.setMarginStart(marginStart);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index b29687f..32a7916 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -205,15 +205,6 @@
                 finished();
             }
         }
-
-        @Override
-        public void onShowDetail(boolean show) {}
-
-        @Override
-        public void onToggleStateChanged(boolean state) {}
-
-        @Override
-        public void onScanStateChanged(boolean state) {}
     }
 
     private void addPackageTiles(final QSTileHost host) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 86fc4de..1488231 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -53,7 +53,6 @@
 import com.android.systemui.qs.tiles.RotationLockTile;
 import com.android.systemui.qs.tiles.ScreenRecordTile;
 import com.android.systemui.qs.tiles.UiModeNightTile;
-import com.android.systemui.qs.tiles.UserTile;
 import com.android.systemui.qs.tiles.WifiTile;
 import com.android.systemui.qs.tiles.WorkModeTile;
 import com.android.systemui.util.leak.GarbageMonitor;
@@ -82,7 +81,6 @@
     private final Provider<LocationTile> mLocationTileProvider;
     private final Provider<CastTile> mCastTileProvider;
     private final Provider<HotspotTile> mHotspotTileProvider;
-    private final Provider<UserTile> mUserTileProvider;
     private final Provider<BatterySaverTile> mBatterySaverTileProvider;
     private final Provider<DataSaverTile> mDataSaverTileProvider;
     private final Provider<NightDisplayTile> mNightDisplayTileProvider;
@@ -119,7 +117,6 @@
             Provider<LocationTile> locationTileProvider,
             Provider<CastTile> castTileProvider,
             Provider<HotspotTile> hotspotTileProvider,
-            Provider<UserTile> userTileProvider,
             Provider<BatterySaverTile> batterySaverTileProvider,
             Provider<DataSaverTile> dataSaverTileProvider,
             Provider<NightDisplayTile> nightDisplayTileProvider,
@@ -152,7 +149,6 @@
         mLocationTileProvider = locationTileProvider;
         mCastTileProvider = castTileProvider;
         mHotspotTileProvider = hotspotTileProvider;
-        mUserTileProvider = userTileProvider;
         mBatterySaverTileProvider = batterySaverTileProvider;
         mDataSaverTileProvider = dataSaverTileProvider;
         mNightDisplayTileProvider = nightDisplayTileProvider;
@@ -212,8 +208,6 @@
                 return mCastTileProvider.get();
             case "hotspot":
                 return mHotspotTileProvider.get();
-            case "user":
-                return mUserTileProvider.get();
             case "battery":
                 return mBatterySaverTileProvider.get();
             case "saver":
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 6d9d5b1..131589f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -60,7 +60,6 @@
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.State;
@@ -261,16 +260,6 @@
         return new QSIconViewImpl(context);
     }
 
-    /** Returns corresponding DetailAdapter. */
-    @Nullable
-    public DetailAdapter getDetailAdapter() {
-        return null; // optional
-    }
-
-    protected DetailAdapter createDetailAdapter() {
-        throw new UnsupportedOperationException();
-    }
-
     /**
      * Is a startup check whether this device currently supports this tile.
      * Should not be used to conditionally hide tiles.  Only checked on tile
@@ -337,10 +326,6 @@
                 .addTaggedData(FIELD_QS_POSITION, mHost.indexOf(mTileSpec));
     }
 
-    public void showDetail(boolean show) {
-        mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0).sendToTarget();
-    }
-
     public void refreshState() {
         refreshState(null);
     }
@@ -353,14 +338,6 @@
         mHandler.obtainMessage(H.USER_SWITCH, newUserId, 0).sendToTarget();
     }
 
-    public void fireToggleStateChanged(boolean state) {
-        mHandler.obtainMessage(H.TOGGLE_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget();
-    }
-
-    public void fireScanStateChanged(boolean state) {
-        mHandler.obtainMessage(H.SCAN_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget();
-    }
-
     public void destroy() {
         mHandler.sendEmptyMessage(H.DESTROY);
     }
@@ -462,29 +439,6 @@
         }
     }
 
-    private void handleShowDetail(boolean show) {
-        mShowingDetail = show;
-        for (int i = 0; i < mCallbacks.size(); i++) {
-            mCallbacks.get(i).onShowDetail(show);
-        }
-    }
-
-    protected boolean isShowingDetail() {
-        return mShowingDetail;
-    }
-
-    private void handleToggleStateChanged(boolean state) {
-        for (int i = 0; i < mCallbacks.size(); i++) {
-            mCallbacks.get(i).onToggleStateChanged(state);
-        }
-    }
-
-    private void handleScanStateChanged(boolean state) {
-        for (int i = 0; i < mCallbacks.size(); i++) {
-            mCallbacks.get(i).onScanStateChanged(state);
-        }
-    }
-
     protected void handleUserSwitch(int newUserId) {
         handleRefreshState(null);
     }
@@ -591,17 +545,14 @@
         private static final int SECONDARY_CLICK = 3;
         private static final int LONG_CLICK = 4;
         private static final int REFRESH_STATE = 5;
-        private static final int SHOW_DETAIL = 6;
-        private static final int USER_SWITCH = 7;
-        private static final int TOGGLE_STATE_CHANGED = 8;
-        private static final int SCAN_STATE_CHANGED = 9;
-        private static final int DESTROY = 10;
-        private static final int REMOVE_CALLBACKS = 11;
-        private static final int REMOVE_CALLBACK = 12;
-        private static final int SET_LISTENING = 13;
+        private static final int USER_SWITCH = 6;
+        private static final int DESTROY = 7;
+        private static final int REMOVE_CALLBACKS = 8;
+        private static final int REMOVE_CALLBACK = 9;
+        private static final int SET_LISTENING = 10;
         @VisibleForTesting
-        protected static final int STALE = 14;
-        private static final int INITIALIZE = 15;
+        protected static final int STALE = 11;
+        private static final int INITIALIZE = 12;
 
         @VisibleForTesting
         protected H(Looper looper) {
@@ -639,18 +590,9 @@
                 } else if (msg.what == REFRESH_STATE) {
                     name = "handleRefreshState";
                     handleRefreshState(msg.obj);
-                } else if (msg.what == SHOW_DETAIL) {
-                    name = "handleShowDetail";
-                    handleShowDetail(msg.arg1 != 0);
                 } else if (msg.what == USER_SWITCH) {
                     name = "handleUserSwitch";
                     handleUserSwitch(msg.arg1);
-                } else if (msg.what == TOGGLE_STATE_CHANGED) {
-                    name = "handleToggleStateChanged";
-                    handleToggleStateChanged(msg.arg1 != 0);
-                } else if (msg.what == SCAN_STATE_CHANGED) {
-                    name = "handleScanStateChanged";
-                    handleScanStateChanged(msg.arg1 != 0);
                 } else if (msg.what == DESTROY) {
                     name = "handleDestroy";
                     handleDestroy();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 754f8e2..c61c18a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -19,7 +19,6 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
@@ -29,7 +28,6 @@
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
@@ -38,24 +36,18 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.Utils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSDetailItems;
-import com.android.systemui.qs.QSDetailItems.Item;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.policy.BluetoothController;
 
-import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 
 import javax.inject.Inject;
@@ -65,7 +57,6 @@
     private static final Intent BLUETOOTH_SETTINGS = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
 
     private final BluetoothController mController;
-    private final BluetoothDetailAdapter mDetailAdapter;
 
     @Inject
     public BluetoothTile(
@@ -82,16 +73,10 @@
         super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                 statusBarStateController, activityStarter, qsLogger);
         mController = bluetoothController;
-        mDetailAdapter = (BluetoothDetailAdapter) createDetailAdapter();
         mController.observe(getLifecycle(), mCallback);
     }
 
     @Override
-    public DetailAdapter getDetailAdapter() {
-        return mDetailAdapter;
-    }
-
-    @Override
     public BooleanState newTileState() {
         return new BooleanState();
     }
@@ -117,7 +102,6 @@
                     new Intent(Settings.ACTION_BLUETOOTH_SETTINGS), 0);
             return;
         }
-        showDetail(true);
         if (!mState.value) {
             mController.setBluetoothEnabled(true);
         }
@@ -251,53 +235,14 @@
         @Override
         public void onBluetoothStateChange(boolean enabled) {
             refreshState();
-            if (isShowingDetail()) {
-                mDetailAdapter.updateItems();
-                fireToggleStateChanged(mDetailAdapter.getToggleState());
-            }
         }
 
         @Override
         public void onBluetoothDevicesChanged() {
             refreshState();
-            if (isShowingDetail()) {
-                mDetailAdapter.updateItems();
-            }
         }
     };
 
-    @Override
-    protected DetailAdapter createDetailAdapter() {
-        return new BluetoothDetailAdapter();
-    }
-
-    /**
-     * Bluetooth icon wrapper for Quick Settings with a battery indicator that reflects the
-     * connected device's battery level. This is used instead of
-     * {@link com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon} in order to use a context
-     * that reflects dark/light theme attributes.
-     */
-    private class BluetoothBatteryTileIcon extends Icon {
-        private int mBatteryLevel;
-        private float mIconScale;
-
-        BluetoothBatteryTileIcon(int batteryLevel, float iconScale) {
-            mBatteryLevel = batteryLevel;
-            mIconScale = iconScale;
-        }
-
-        @Override
-        public Drawable getDrawable(Context context) {
-            // This method returns Pair<Drawable, String> while first value is the drawable
-            return BluetoothDeviceLayerDrawable.createLayerDrawable(
-                    context,
-                    R.drawable.ic_bluetooth_connected,
-                    mBatteryLevel,
-                    mIconScale);
-        }
-    }
-
-
     /**
      * Bluetooth icon wrapper (when connected with no battery indicator) for Quick Settings. This is
      * used instead of {@link com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon} in order to
@@ -315,129 +260,4 @@
             return context.getDrawable(R.drawable.ic_bluetooth_connected);
         }
     }
-
-    protected class BluetoothDetailAdapter implements DetailAdapter, QSDetailItems.Callback {
-        // We probably won't ever have space in the UI for more than 20 devices, so don't
-        // get info for them.
-        private static final int MAX_DEVICES = 20;
-        @Nullable
-        private QSDetailItems mItems;
-
-        @Override
-        public CharSequence getTitle() {
-            return mContext.getString(R.string.quick_settings_bluetooth_label);
-        }
-
-        @Override
-        public Boolean getToggleState() {
-            return mState.value;
-        }
-
-        @Override
-        public boolean getToggleEnabled() {
-            return mController.getBluetoothState() == BluetoothAdapter.STATE_OFF
-                    || mController.getBluetoothState() == BluetoothAdapter.STATE_ON;
-        }
-
-        @Override
-        public Intent getSettingsIntent() {
-            return BLUETOOTH_SETTINGS;
-        }
-
-        @Override
-        public void setToggleState(boolean state) {
-            MetricsLogger.action(mContext, MetricsEvent.QS_BLUETOOTH_TOGGLE, state);
-            mController.setBluetoothEnabled(state);
-        }
-
-        @Override
-        public int getMetricsCategory() {
-            return MetricsEvent.QS_BLUETOOTH_DETAILS;
-        }
-
-        @Override
-        public View createDetailView(Context context, View convertView, ViewGroup parent) {
-            mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
-            mItems.setTagSuffix("Bluetooth");
-            mItems.setCallback(this);
-            updateItems();
-            setItemsVisible(mState.value);
-            return mItems;
-        }
-
-        public void setItemsVisible(boolean visible) {
-            if (mItems == null) return;
-            mItems.setItemsVisible(visible);
-        }
-
-        private void updateItems() {
-            if (mItems == null) return;
-            if (mController.isBluetoothEnabled()) {
-                mItems.setEmptyState(R.drawable.ic_qs_bluetooth_detail_empty,
-                        R.string.quick_settings_bluetooth_detail_empty_text);
-            } else {
-                mItems.setEmptyState(R.drawable.ic_qs_bluetooth_detail_empty,
-                        R.string.bt_is_off);
-            }
-            ArrayList<Item> items = new ArrayList<Item>();
-            final Collection<CachedBluetoothDevice> devices = mController.getDevices();
-            if (devices != null) {
-                int connectedDevices = 0;
-                int count = 0;
-                for (CachedBluetoothDevice device : devices) {
-                    if (mController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
-                    final Item item =
-                            new Item(
-                                    com.android.internal.R.drawable.ic_qs_bluetooth,
-                                    device.getName(),
-                                    device);
-                    int state = device.getMaxConnectionState();
-                    if (state == BluetoothProfile.STATE_CONNECTED) {
-                        item.iconResId = R.drawable.ic_bluetooth_connected;
-                        int batteryLevel = device.getBatteryLevel();
-                        if (batteryLevel > BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
-                            item.icon = new BluetoothBatteryTileIcon(batteryLevel,1 /* iconScale */);
-                            item.line2 = mContext.getString(
-                                    R.string.quick_settings_connected_battery_level,
-                                    Utils.formatPercentage(batteryLevel));
-                        } else {
-                            item.line2 = mContext.getString(R.string.quick_settings_connected);
-                        }
-                        item.canDisconnect = true;
-                        items.add(connectedDevices, item);
-                        connectedDevices++;
-                    } else if (state == BluetoothProfile.STATE_CONNECTING) {
-                        item.iconResId = R.drawable.ic_qs_bluetooth_connecting;
-                        item.line2 = mContext.getString(R.string.quick_settings_connecting);
-                        items.add(connectedDevices, item);
-                    } else {
-                        items.add(item);
-                    }
-                    if (++count == MAX_DEVICES) {
-                        break;
-                    }
-                }
-            }
-            mItems.setItems(items.toArray(new Item[items.size()]));
-        }
-
-        @Override
-        public void onDetailItemClick(Item item) {
-            if (item == null || item.tag == null) return;
-            final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
-            if (device != null && device.getMaxConnectionState()
-                    == BluetoothProfile.STATE_DISCONNECTED) {
-                mController.connect(device);
-            }
-        }
-
-        @Override
-        public void onDetailItemDisconnect(Item item) {
-            if (item == null || item.tag == null) return;
-            final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
-            if (device != null) {
-                mController.disconnect(device);
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 76c84f9..4d9c4c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -20,7 +20,6 @@
 
 import android.annotation.NonNull;
 import android.app.Dialog;
-import android.content.Context;
 import android.content.Intent;
 import android.media.MediaRouter.RouteInfo;
 import android.os.Handler;
@@ -29,8 +28,6 @@
 import android.service.quicksettings.Tile;
 import android.util.Log;
 import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
-import android.view.ViewGroup;
 import android.widget.Button;
 
 import androidx.annotation.Nullable;
@@ -44,11 +41,8 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSDetailItems;
-import com.android.systemui.qs.QSDetailItems.Item;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
@@ -62,7 +56,6 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import java.util.ArrayList;
-import java.util.LinkedHashMap;
 import java.util.List;
 
 import javax.inject.Inject;
@@ -73,7 +66,6 @@
             new Intent(Settings.ACTION_CAST_SETTINGS);
 
     private final CastController mController;
-    private final CastDetailAdapter mDetailAdapter;
     private final KeyguardStateController mKeyguard;
     private final NetworkController mNetworkController;
     private final DialogLaunchAnimator mDialogLaunchAnimator;
@@ -100,7 +92,6 @@
         super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                 statusBarStateController, activityStarter, qsLogger);
         mController = castController;
-        mDetailAdapter = new CastDetailAdapter();
         mKeyguard = keyguardStateController;
         mNetworkController = networkController;
         mDialogLaunchAnimator = dialogLaunchAnimator;
@@ -111,11 +102,6 @@
     }
 
     @Override
-    public DetailAdapter getDetailAdapter() {
-        return mDetailAdapter;
-    }
-
-    @Override
     public BooleanState newTileState() {
         BooleanState state = new BooleanState();
         state.handlesLongClick = false;
@@ -154,14 +140,14 @@
         }
 
         List<CastDevice> activeDevices = getActiveDevices();
-        if (willPopDetail()) {
+        if (willPopDialog()) {
             if (!mKeyguard.isShowing()) {
-                showDetail(view);
+                showDialog(view);
             } else {
                 mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
                     // Dismissing the keyguard will collapse the shade, so we don't animate from the
                     // view here as it would not look good.
-                    showDetail(null /* view */);
+                    showDialog(null /* view */);
                 });
             }
         } else {
@@ -173,7 +159,7 @@
     // (neither routes nor projection), or if we have an active route. In other cases, we assume
     // that a projection is active. This is messy, but this tile never correctly handled the
     // case where multiple devices were active :-/.
-    private boolean willPopDetail() {
+    private boolean willPopDialog() {
         List<CastDevice> activeDevices = getActiveDevices();
         return activeDevices.isEmpty() || (activeDevices.get(0).tag instanceof RouteInfo);
     }
@@ -190,11 +176,6 @@
         return activeDevices;
     }
 
-    @Override
-    public void showDetail(boolean show) {
-        showDetail(null /* view */);
-    }
-
     private static class DialogHolder {
         private Dialog mDialog;
 
@@ -203,7 +184,7 @@
         }
     }
 
-    private void showDetail(@Nullable View view) {
+    private void showDialog(@Nullable View view) {
         mUiHandler.post(() -> {
             final DialogHolder holder = new DialogHolder();
             final Dialog dialog = MediaRouteDialogPresenter.createDialog(
@@ -268,10 +249,8 @@
             if (!state.value) {
                 state.secondaryLabel = "";
             }
-            state.contentDescription = state.contentDescription + ","
-                    + mContext.getString(R.string.accessibility_quick_settings_open_details);
             state.expandedAccessibilityClassName = Button.class.getName();
-            state.forceExpandIcon = willPopDetail();
+            state.forceExpandIcon = willPopDialog();
         } else {
             state.state = Tile.STATE_UNAVAILABLE;
             String noWifi = mContext.getString(R.string.quick_settings_cast_no_wifi);
@@ -279,7 +258,6 @@
             state.forceExpandIcon = false;
         }
         state.stateDescription = state.stateDescription + ", " + state.secondaryLabel;
-        mDetailAdapter.updateItems(devices);
     }
 
     @Override
@@ -339,118 +317,4 @@
             refreshState();
         }
     };
-
-    private final class CastDetailAdapter implements DetailAdapter, QSDetailItems.Callback {
-        private final LinkedHashMap<String, CastDevice> mVisibleOrder = new LinkedHashMap<>();
-
-        private QSDetailItems mItems;
-
-        @Override
-        public CharSequence getTitle() {
-            return mContext.getString(R.string.quick_settings_cast_title);
-        }
-
-        @Override
-        public Boolean getToggleState() {
-            return null;
-        }
-
-        @Override
-        public Intent getSettingsIntent() {
-            return CAST_SETTINGS;
-        }
-
-        @Override
-        public void setToggleState(boolean state) {
-            // noop
-        }
-
-        @Override
-        public int getMetricsCategory() {
-            return MetricsEvent.QS_CAST_DETAILS;
-        }
-
-        @Override
-        public View createDetailView(Context context, View convertView, ViewGroup parent) {
-            mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
-            mItems.setTagSuffix("Cast");
-            if (convertView == null) {
-                if (DEBUG) Log.d(TAG, "addOnAttachStateChangeListener");
-                mItems.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
-                    @Override
-                    public void onViewAttachedToWindow(View v) {
-                        if (DEBUG) Log.d(TAG, "onViewAttachedToWindow");
-                    }
-
-                    @Override
-                    public void onViewDetachedFromWindow(View v) {
-                        if (DEBUG) Log.d(TAG, "onViewDetachedFromWindow");
-                        mVisibleOrder.clear();
-                    }
-                });
-            }
-            mItems.setEmptyState(R.drawable.ic_qs_cast_detail_empty,
-                    R.string.quick_settings_cast_detail_empty_text);
-            mItems.setCallback(this);
-            updateItems(mController.getCastDevices());
-            mController.setDiscovering(true);
-            return mItems;
-        }
-
-        private void updateItems(List<CastDevice> devices) {
-            if (mItems == null) return;
-            Item[] items = null;
-            if (devices != null && !devices.isEmpty()) {
-                // if we are connected, simply show that device
-                for (CastDevice device : devices) {
-                    if (device.state == CastDevice.STATE_CONNECTED) {
-                        final Item item =
-                                new Item(
-                                        R.drawable.ic_cast_connected,
-                                        getDeviceName(device),
-                                        device);
-                        item.line2 = mContext.getString(R.string.quick_settings_connected);
-                        item.canDisconnect = true;
-                        items = new Item[] { item };
-                        break;
-                    }
-                }
-                // otherwise list all available devices, and don't move them around
-                if (items == null) {
-                    for (CastDevice device : devices) {
-                        mVisibleOrder.put(device.id, device);
-                    }
-                    items = new Item[devices.size()];
-                    int i = 0;
-                    for (String id : mVisibleOrder.keySet()) {
-                        final CastDevice device = mVisibleOrder.get(id);
-                        if (!devices.contains(device)) continue;
-                        final Item item =
-                                new Item(R.drawable.ic_cast, getDeviceName(device), device);
-                        if (device.state == CastDevice.STATE_CONNECTING) {
-                            item.line2 = mContext.getString(R.string.quick_settings_connecting);
-                        }
-                        items[i++] = item;
-                    }
-                }
-            }
-            mItems.setItems(items);
-        }
-
-        @Override
-        public void onDetailItemClick(Item item) {
-            if (item == null || item.tag == null) return;
-            MetricsLogger.action(mContext, MetricsEvent.QS_CAST_SELECT);
-            final CastDevice device = (CastDevice) item.tag;
-            mController.startCasting(device);
-        }
-
-        @Override
-        public void onDetailItemDisconnect(Item item) {
-            if (item == null || item.tag == null) return;
-            MetricsLogger.action(mContext, MetricsEvent.QS_CAST_DISCONNECT);
-            final CastDevice device = (CastDevice) item.tag;
-            mController.stopCasting(device);
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 698a253..e116f75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -32,9 +32,7 @@
 import android.telephony.SubscriptionManager;
 import android.text.Html;
 import android.text.TextUtils;
-import android.view.LayoutInflater;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.WindowManager.LayoutParams;
 import android.widget.Switch;
 
@@ -49,7 +47,6 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile.SignalState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -71,7 +68,6 @@
 
     private final NetworkController mController;
     private final DataUsageController mDataController;
-    private final CellularDetailAdapter mDetailAdapter;
 
     private final CellSignalCallback mSignalCallback = new CellSignalCallback();
 
@@ -91,7 +87,6 @@
                 statusBarStateController, activityStarter, qsLogger);
         mController = networkController;
         mDataController = mController.getMobileDataController();
-        mDetailAdapter = new CellularDetailAdapter();
         mController.observe(getLifecycle(), mSignalCallback);
     }
 
@@ -106,11 +101,6 @@
     }
 
     @Override
-    public DetailAdapter getDetailAdapter() {
-        return mDetailAdapter;
-    }
-
-    @Override
     public Intent getLongClickIntent() {
         if (getState().state == Tile.STATE_UNAVAILABLE) {
             return new Intent(Settings.ACTION_WIRELESS_SETTINGS);
@@ -161,12 +151,7 @@
 
     @Override
     protected void handleSecondaryClick(@Nullable View view) {
-        if (mDataController.isMobileDataSupported()) {
-            showDetail(true);
-        } else {
-            mActivityStarter
-                    .postStartActivityDismissingKeyguard(getCellularSettingIntent(),0 /* delay */);
-        }
+        handleLongClick(view);
     }
 
     @Override
@@ -298,11 +283,6 @@
             mInfo.airplaneModeEnabled = icon.visible;
             refreshState(mInfo);
         }
-
-        @Override
-        public void setMobileDataEnabled(boolean enabled) {
-            mDetailAdapter.setMobileDataEnabled(enabled);
-        }
     }
 
     static Intent getCellularSettingIntent() {
@@ -314,53 +294,4 @@
         }
         return intent;
     }
-
-    private final class CellularDetailAdapter implements DetailAdapter {
-
-        @Override
-        public CharSequence getTitle() {
-            return mContext.getString(R.string.quick_settings_cellular_detail_title);
-        }
-
-        @Nullable
-        @Override
-        public Boolean getToggleState() {
-            return mDataController.isMobileDataSupported()
-                    ? mDataController.isMobileDataEnabled()
-                    : null;
-        }
-
-        @Override
-        public Intent getSettingsIntent() {
-            return getCellularSettingIntent();
-        }
-
-        @Override
-        public void setToggleState(boolean state) {
-            MetricsLogger.action(mContext, MetricsEvent.QS_CELLULAR_TOGGLE, state);
-            mDataController.setMobileDataEnabled(state);
-        }
-
-        @Override
-        public int getMetricsCategory() {
-            return MetricsEvent.QS_DATAUSAGEDETAIL;
-        }
-
-        @Override
-        public View createDetailView(Context context, View convertView, ViewGroup parent) {
-            final DataUsageDetailView v = (DataUsageDetailView) (convertView != null
-                    ? convertView
-                    : LayoutInflater.from(mContext).inflate(R.layout.data_usage, parent, false));
-            final DataUsageController.DataUsageInfo info = mDataController.getDataUsageInfo();
-            if (info == null) return v;
-            v.bind(info);
-            v.findViewById(R.id.roaming_text).setVisibility(mSignalCallback.mInfo.roaming
-                    ? View.VISIBLE : View.INVISIBLE);
-            return v;
-        }
-
-        public void setMobileDataEnabled(boolean enabled) {
-            fireToggleStateChanged(enabled);
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index a33650c..6cff4cd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -25,8 +25,6 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
@@ -34,16 +32,10 @@
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.ZenRule;
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
-import android.util.Slog;
-import android.view.LayoutInflater;
 import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
-import android.view.ViewGroup;
 import android.widget.Switch;
-import android.widget.Toast;
 
 import androidx.annotation.Nullable;
 
@@ -52,13 +44,11 @@
 import com.android.settingslib.notification.EnableZenModeDialog;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
-import com.android.systemui.SysUIToast;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -69,7 +59,6 @@
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.util.settings.SecureSettings;
-import com.android.systemui.volume.ZenModePanel;
 
 import javax.inject.Inject;
 
@@ -83,14 +72,12 @@
             new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS);
 
     private final ZenModeController mController;
-    private final DndDetailAdapter mDetailAdapter;
     private final SharedPreferences mSharedPreferences;
     private final SettingObserver mSettingZenDuration;
     private final DialogLaunchAnimator mDialogLaunchAnimator;
     private final QSZenModeDialogMetricsLogger mQSZenDialogMetricsLogger;
 
     private boolean mListening;
-    private boolean mShowingDetail;
 
     @Inject
     public DndTile(
@@ -111,7 +98,6 @@
                 statusBarStateController, activityStarter, qsLogger);
         mController = zenModeController;
         mSharedPreferences = sharedPreferences;
-        mDetailAdapter = new DndDetailAdapter();
         mController.observe(getLifecycle(), mZenCallback);
         mDialogLaunchAnimator = dialogLaunchAnimator;
         mSettingZenDuration = new SettingObserver(secureSettings, mUiHandler,
@@ -142,11 +128,6 @@
     }
 
     @Override
-    public DetailAdapter getDetailAdapter() {
-        return mDetailAdapter;
-    }
-
-    @Override
     public BooleanState newTileState() {
         return new BooleanState();
     }
@@ -225,28 +206,7 @@
 
     @Override
     protected void handleSecondaryClick(@Nullable View view) {
-        if (mController.isVolumeRestricted()) {
-            // Collapse the panels, so the user can see the toast.
-            mHost.collapsePanels();
-            SysUIToast.makeText(mContext, mContext.getString(
-                    com.android.internal.R.string.error_message_change_not_allowed),
-                    Toast.LENGTH_LONG).show();
-            return;
-        }
-        if (!mState.value) {
-            // Because of the complexity of the zen panel, it needs to be shown after
-            // we turn on zen below.
-            mController.addCallback(new ZenModeController.Callback() {
-                @Override
-                public void onZenChanged(int zen) {
-                    mController.removeCallback(this);
-                    showDetail(true);
-                }
-            });
-            mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
-        } else {
-            showDetail(true);
-        }
+        handleLongClick(view);
     }
 
     @Override
@@ -295,9 +255,6 @@
                         R.string.accessibility_quick_settings_dnd);
                 break;
         }
-        if (valueChanged) {
-            fireToggleStateChanged(state.value);
-        }
         state.dualLabelContentDescription = mContext.getResources().getString(
                 R.string.accessibility_quick_settings_open_settings, getTileLabel());
         state.expandedAccessibilityClassName = Switch.class.getName();
@@ -349,146 +306,6 @@
     private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
         public void onZenChanged(int zen) {
             refreshState(zen);
-            if (isShowingDetail()) {
-                mDetailAdapter.updatePanel();
-            }
-        }
-
-        @Override
-        public void onConfigChanged(ZenModeConfig config) {
-            if (isShowingDetail()) {
-                mDetailAdapter.updatePanel();
-            }
-        }
-    };
-
-    private final class DndDetailAdapter implements DetailAdapter, OnAttachStateChangeListener {
-
-        @Nullable
-        private ZenModePanel mZenPanel;
-        private boolean mAuto;
-
-        @Override
-        public CharSequence getTitle() {
-            return mContext.getString(R.string.quick_settings_dnd_label);
-        }
-
-        @Override
-        public Boolean getToggleState() {
-            return mState.value;
-        }
-
-        @Override
-        public Intent getSettingsIntent() {
-            return ZEN_SETTINGS;
-        }
-
-        @Override
-        public void setToggleState(boolean state) {
-            MetricsLogger.action(mContext, MetricsEvent.QS_DND_TOGGLE, state);
-            if (!state) {
-                mController.setZen(ZEN_MODE_OFF, null, TAG);
-                mAuto = false;
-            } else {
-                mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
-            }
-        }
-
-        @Override
-        public int getMetricsCategory() {
-            return MetricsEvent.QS_DND_DETAILS;
-        }
-
-        @Override
-        public View createDetailView(Context context, View convertView, ViewGroup parent) {
-            mZenPanel = convertView != null ? (ZenModePanel) convertView
-                    : (ZenModePanel) LayoutInflater.from(context).inflate(
-                            R.layout.zen_mode_panel, parent, false);
-            if (convertView == null) {
-                mZenPanel.init(mController);
-                mZenPanel.addOnAttachStateChangeListener(this);
-                mZenPanel.setCallback(mZenModePanelCallback);
-                mZenPanel.setEmptyState(R.drawable.ic_qs_dnd_detail_empty, R.string.dnd_is_off);
-            }
-            updatePanel();
-            return mZenPanel;
-        }
-
-        private void updatePanel() {
-            if (mZenPanel == null) return;
-            mAuto = false;
-            if (mController.getZen() == ZEN_MODE_OFF) {
-                mZenPanel.setState(ZenModePanel.STATE_OFF);
-            } else {
-                ZenModeConfig config = mController.getConfig();
-                String summary = "";
-                if (config.manualRule != null && config.manualRule.enabler != null) {
-                    summary = getOwnerCaption(config.manualRule.enabler);
-                }
-                for (ZenRule automaticRule : config.automaticRules.values()) {
-                    if (automaticRule.isAutomaticActive()) {
-                        if (summary.isEmpty()) {
-                            summary = mContext.getString(R.string.qs_dnd_prompt_auto_rule,
-                                    automaticRule.name);
-                        } else {
-                            summary = mContext.getString(R.string.qs_dnd_prompt_auto_rule_app);
-                        }
-                    }
-                }
-                if (summary.isEmpty()) {
-                    mZenPanel.setState(ZenModePanel.STATE_MODIFY);
-                } else {
-                    mAuto = true;
-                    mZenPanel.setState(ZenModePanel.STATE_AUTO_RULE);
-                    mZenPanel.setAutoText(summary);
-                }
-            }
-        }
-
-        private String getOwnerCaption(String owner) {
-            final PackageManager pm = mContext.getPackageManager();
-            try {
-                final ApplicationInfo info = pm.getApplicationInfo(owner, 0);
-                if (info != null) {
-                    final CharSequence seq = info.loadLabel(pm);
-                    if (seq != null) {
-                        final String str = seq.toString().trim();
-                        return mContext.getString(R.string.qs_dnd_prompt_app, str);
-                    }
-                }
-            } catch (Throwable e) {
-                Slog.w(TAG, "Error loading owner caption", e);
-            }
-            return "";
-        }
-
-        @Override
-        public void onViewAttachedToWindow(View v) {
-            mShowingDetail = true;
-        }
-
-        @Override
-        public void onViewDetachedFromWindow(View v) {
-            mShowingDetail = false;
-            mZenPanel = null;
-        }
-    }
-
-    private final ZenModePanel.Callback mZenModePanelCallback = new ZenModePanel.Callback() {
-        @Override
-        public void onPrioritySettings() {
-            mActivityStarter.postStartActivityDismissingKeyguard(
-                    ZEN_PRIORITY_SETTINGS, 0);
-        }
-
-        @Override
-        public void onInteraction() {
-            // noop
-        }
-
-        @Override
-        public void onExpanded(boolean expanded) {
-            // noop
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 9df942d..cd7021e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -411,10 +411,6 @@
         }
         boolean wifiConnected = cb.mEnabled && (cb.mWifiSignalIconId > 0) && (cb.mSsid != null);
         boolean wifiNotConnected = (cb.mWifiSignalIconId > 0) && (cb.mSsid == null);
-        boolean enabledChanging = state.value != cb.mEnabled;
-        if (enabledChanging) {
-            fireToggleStateChanged(cb.mEnabled);
-        }
         if (state.slash == null) {
             state.slash = new SlashState();
             state.slash.rotation = 6;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index 0be0619..f4dd415 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -36,7 +36,6 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -134,12 +133,6 @@
         return new Intent(Settings.ACTION_PRIVACY_SETTINGS);
     }
 
-    @Nullable
-    @Override
-    public DetailAdapter getDetailAdapter() {
-        return super.getDetailAdapter();
-    }
-
     @Override
     public void onSensorBlockedChanged(int sensor, boolean blocked) {
         if (sensor == getSensorId()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index 076ef35..5840a3d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -150,6 +150,6 @@
     }
 
     protected int getFontSizeDimen() {
-        return R.dimen.qs_detail_item_secondary_text_size;
+        return R.dimen.qs_tile_text_size;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
deleted file mode 100644
index db1b6e6..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.qs.tiles;
-
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.Settings;
-import android.util.Pair;
-import android.view.View;
-
-import androidx.annotation.Nullable;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
-import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTile.State;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-
-import javax.inject.Inject;
-
-public class UserTile extends QSTileImpl<State> implements UserInfoController.OnUserInfoChangedListener {
-
-    private final UserSwitcherController mUserSwitcherController;
-    private final UserInfoController mUserInfoController;
-    @Nullable
-    private Pair<String, Drawable> mLastUpdate;
-
-    @Inject
-    public UserTile(
-            QSHost host,
-            @Background Looper backgroundLooper,
-            @Main Handler mainHandler,
-            FalsingManager falsingManager,
-            MetricsLogger metricsLogger,
-            StatusBarStateController statusBarStateController,
-            ActivityStarter activityStarter,
-            QSLogger qsLogger,
-            UserSwitcherController userSwitcherController,
-            UserInfoController userInfoController
-    ) {
-        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
-                statusBarStateController, activityStarter, qsLogger);
-        mUserSwitcherController = userSwitcherController;
-        mUserInfoController = userInfoController;
-        mUserInfoController.observe(getLifecycle(), this);
-    }
-
-    @Override
-    public State newTileState() {
-        return new QSTile.State();
-    }
-
-    @Override
-    public Intent getLongClickIntent() {
-        return new Intent(Settings.ACTION_USER_SETTINGS);
-    }
-
-    @Override
-    protected void handleClick(@Nullable View view) {
-        showDetail(true);
-    }
-
-    @Override
-    public DetailAdapter getDetailAdapter() {
-        return mUserSwitcherController.mUserDetailAdapter;
-    }
-
-    @Override
-    public int getMetricsCategory() {
-        return MetricsEvent.QS_USER_TILE;
-    }
-
-    @Override
-    public CharSequence getTileLabel() {
-        return getState().label;
-    }
-
-    @Override
-    protected void handleUpdateState(State state, Object arg) {
-        final Pair<String, Drawable> p = arg != null ? (Pair<String, Drawable>) arg : mLastUpdate;
-        if (p != null) {
-            state.label = p.first;
-            // TODO: Better content description.
-            state.contentDescription = p.first;
-            state.icon = new Icon() {
-                @Override
-                public Drawable getDrawable(Context context) {
-                    return p.second;
-                }
-            };
-        } else {
-            // TODO: Default state.
-        }
-    }
-
-    @Override
-    public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
-        mLastUpdate = new Pair<>(name, picture);
-        refreshState(mLastUpdate);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index c82ff34..b2be56cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -28,27 +28,22 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.Switch;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.wifi.AccessPoint;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.SignalState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.AlphaControlledSignalTileView;
-import com.android.systemui.qs.QSDetailItems;
-import com.android.systemui.qs.QSDetailItems.Item;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSIconViewImpl;
@@ -58,9 +53,6 @@
 import com.android.systemui.statusbar.connectivity.SignalCallback;
 import com.android.systemui.statusbar.connectivity.WifiIcons;
 import com.android.systemui.statusbar.connectivity.WifiIndicators;
-import com.android.wifitrackerlib.WifiEntry;
-
-import java.util.List;
 
 import javax.inject.Inject;
 
@@ -70,7 +62,6 @@
 
     protected final NetworkController mController;
     private final AccessPointController mWifiController;
-    private final WifiDetailAdapter mDetailAdapter;
     private final QSTile.SignalState mStateBeforeClick = newTileState();
 
     protected final WifiSignalCallback mSignalCallback = new WifiSignalCallback();
@@ -93,7 +84,6 @@
                 statusBarStateController, activityStarter, qsLogger);
         mController = networkController;
         mWifiController = accessPointController;
-        mDetailAdapter = (WifiDetailAdapter) createDetailAdapter();
         mController.observe(getLifecycle(), mSignalCallback);
         mStateBeforeClick.spec = "wifi";
     }
@@ -104,25 +94,6 @@
     }
 
     @Override
-    public void setDetailListening(boolean listening) {
-        if (listening) {
-            mWifiController.addAccessPointCallback(mDetailAdapter);
-        } else {
-            mWifiController.removeAccessPointCallback(mDetailAdapter);
-        }
-    }
-
-    @Override
-    public DetailAdapter getDetailAdapter() {
-        return mDetailAdapter;
-    }
-
-    @Override
-    protected DetailAdapter createDetailAdapter() {
-        return new WifiDetailAdapter();
-    }
-
-    @Override
     public QSIconView createTileView(Context context) {
         return new AlphaControlledSignalTileView(context);
     }
@@ -158,7 +129,6 @@
                     new Intent(Settings.ACTION_WIFI_SETTINGS), 0);
             return;
         }
-        showDetail(true);
         if (!mState.value) {
             mController.setWifiEnabled(true);
         }
@@ -185,11 +155,6 @@
                 && (cb.ssid != null || cb.wifiSignalIconId != WifiIcons.QS_WIFI_NO_NETWORK);
         boolean wifiNotConnected = (cb.ssid == null)
                 && (cb.wifiSignalIconId == WifiIcons.QS_WIFI_NO_NETWORK);
-        boolean enabledChanging = state.value != cb.enabled;
-        if (enabledChanging) {
-            mDetailAdapter.setItemsVisible(cb.enabled);
-            fireToggleStateChanged(cb.enabled);
-        }
         if (state.slash == null) {
             state.slash = new SlashState();
             state.slash.rotation = 6;
@@ -315,150 +280,7 @@
             mInfo.wifiSignalContentDescription = indicators.qsIcon.contentDescription;
             mInfo.isTransient = indicators.isTransient;
             mInfo.statusLabel = indicators.statusLabel;
-            if (isShowingDetail()) {
-                mDetailAdapter.updateItems();
-            }
             refreshState();
         }
     }
-
-    protected class WifiDetailAdapter implements DetailAdapter,
-            AccessPointController.AccessPointCallback, QSDetailItems.Callback {
-
-        @Nullable
-        private QSDetailItems mItems;
-        @Nullable
-        private WifiEntry[] mAccessPoints;
-
-        @Override
-        public CharSequence getTitle() {
-            return mContext.getString(R.string.quick_settings_wifi_label);
-        }
-
-        public Intent getSettingsIntent() {
-            return WIFI_SETTINGS;
-        }
-
-        @Override
-        public Boolean getToggleState() {
-            return mState.value;
-        }
-
-        @Override
-        public void setToggleState(boolean state) {
-            if (DEBUG) Log.d(TAG, "setToggleState " + state);
-            MetricsLogger.action(mContext, MetricsEvent.QS_WIFI_TOGGLE, state);
-            mController.setWifiEnabled(state);
-        }
-
-        @Override
-        public int getMetricsCategory() {
-            return MetricsEvent.QS_WIFI_DETAILS;
-        }
-
-        @Override
-        public View createDetailView(Context context, View convertView, ViewGroup parent) {
-            if (DEBUG) Log.d(TAG, "createDetailView convertView=" + (convertView != null));
-            mAccessPoints = null;
-            mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
-            mItems.setTagSuffix("Wifi");
-            mItems.setCallback(this);
-            mWifiController.scanForAccessPoints(); // updates APs and items
-            setItemsVisible(mState.value);
-            return mItems;
-        }
-
-        @Override
-        public void onAccessPointsChanged(final List<WifiEntry> accessPoints) {
-            mAccessPoints = filterUnreachableAPs(accessPoints);
-
-            updateItems();
-        }
-
-        /** Filter unreachable APs from mAccessPoints */
-        private WifiEntry[] filterUnreachableAPs(List<WifiEntry> unfiltered) {
-            int numReachable = 0;
-            for (WifiEntry ap : unfiltered) {
-                if (isWifiEntryReachable(ap)) numReachable++;
-            }
-            if (numReachable != unfiltered.size()) {
-                WifiEntry[] accessPoints = new WifiEntry[numReachable];
-                int i = 0;
-                for (WifiEntry ap : unfiltered) {
-                    if (isWifiEntryReachable(ap)) accessPoints[i++] = ap;
-                }
-                return accessPoints;
-            }
-            return unfiltered.toArray(new WifiEntry[0]);
-        }
-
-        @Override
-        public void onSettingsActivityTriggered(Intent settingsIntent) {
-            mActivityStarter.postStartActivityDismissingKeyguard(settingsIntent, 0);
-        }
-
-        @Override
-        public void onDetailItemClick(Item item) {
-            if (item == null || item.tag == null) return;
-            final WifiEntry ap = (WifiEntry) item.tag;
-            if (ap.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED) {
-                if (mWifiController.connect(ap)) {
-                    mHost.collapsePanels();
-                }
-            }
-            showDetail(false);
-        }
-
-        @Override
-        public void onDetailItemDisconnect(Item item) {
-            // noop
-        }
-
-        public void setItemsVisible(boolean visible) {
-            if (mItems == null) return;
-            mItems.setItemsVisible(visible);
-        }
-
-        private void updateItems() {
-            if (mItems == null) return;
-            if ((mAccessPoints != null && mAccessPoints.length > 0)
-                    || !mSignalCallback.mInfo.enabled) {
-                fireScanStateChanged(false);
-            } else {
-                fireScanStateChanged(true);
-            }
-
-            // Wi-Fi is off
-            if (!mSignalCallback.mInfo.enabled) {
-                mItems.setEmptyState(WifiIcons.QS_WIFI_NO_NETWORK,
-                        R.string.wifi_is_off);
-                mItems.setItems(null);
-                return;
-            }
-
-            // No available access points
-            mItems.setEmptyState(WifiIcons.QS_WIFI_NO_NETWORK,
-                    R.string.quick_settings_wifi_detail_empty_text);
-
-            // Build the list
-            Item[] items = null;
-            if (mAccessPoints != null) {
-                items = new Item[mAccessPoints.length];
-                for (int i = 0; i < mAccessPoints.length; i++) {
-                    final WifiEntry ap = mAccessPoints[i];
-                    final Item item = new Item(mWifiController.getIcon(ap), ap.getSsid(), ap);
-                    item.line2 = ap.getSummary();
-                    item.icon2 = ap.getSecurity() != AccessPoint.SECURITY_NONE
-                            ? R.drawable.qs_ic_wifi_lock
-                            : -1;
-                    items[i] = item;
-                }
-            }
-            mItems.setItems(items);
-        }
-    }
-
-    private static boolean isWifiEntryReachable(WifiEntry ap) {
-        return ap.getLevel() != WifiEntry.WIFI_LEVEL_UNREACHABLE;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
index 7c8f4b15..8c8c5c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
@@ -24,11 +24,13 @@
 import android.view.LayoutInflater
 import android.view.View
 import androidx.annotation.VisibleForTesting
+import com.android.internal.logging.UiEventLogger
 import com.android.systemui.R
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.QSUserSwitcherEvent
 import com.android.systemui.qs.tiles.UserDetailView
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import javax.inject.Inject
@@ -43,6 +45,7 @@
     private val activityStarter: ActivityStarter,
     private val falsingManager: FalsingManager,
     private val dialogLaunchAnimator: DialogLaunchAnimator,
+    private val uiEventLogger: UiEventLogger,
     private val dialogFactory: (Context) -> SystemUIDialog
 ) {
 
@@ -51,12 +54,14 @@
         userDetailViewAdapterProvider: Provider<UserDetailView.Adapter>,
         activityStarter: ActivityStarter,
         falsingManager: FalsingManager,
-        dialogLaunchAnimator: DialogLaunchAnimator
+        dialogLaunchAnimator: DialogLaunchAnimator,
+        uiEventLogger: UiEventLogger
     ) : this(
         userDetailViewAdapterProvider,
         activityStarter,
         falsingManager,
         dialogLaunchAnimator,
+        uiEventLogger,
         { SystemUIDialog(it) }
     )
 
@@ -76,10 +81,13 @@
             setCanceledOnTouchOutside(true)
 
             setTitle(R.string.qs_user_switch_dialog_title)
-            setPositiveButton(R.string.quick_settings_done, null)
+            setPositiveButton(R.string.quick_settings_done) { _, _ ->
+                uiEventLogger.log(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE)
+            }
             setNeutralButton(R.string.quick_settings_more_user_settings) { _, _ ->
                 if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
                     dialogLaunchAnimator.disableAllCurrentDialogsExitAnimations()
+                    uiEventLogger.log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
                     activityStarter.postStartActivityDismissingKeyguard(
                         USER_SETTINGS_INTENT,
                         0
@@ -95,6 +103,7 @@
             adapter.linkToViewGroup(gridFrame.findViewById(R.id.grid))
 
             dialogLaunchAnimator.showFromView(this, view)
+            uiEventLogger.log(QSUserSwitcherEvent.QS_USER_DETAIL_OPEN)
             adapter.injectDialogShower(DialogShowerImpl(this, dialogLaunchAnimator))
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 30456a8..50765f2 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -51,7 +51,9 @@
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
-import android.media.MediaActionSound;
+import android.media.AudioAttributes;
+import android.media.AudioSystem;
+import android.media.MediaPlayer;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -93,6 +95,7 @@
 
 import com.google.common.util.concurrent.ListenableFuture;
 
+import java.io.File;
 import java.util.List;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.ExecutionException;
@@ -248,7 +251,7 @@
     private final WindowManager mWindowManager;
     private final WindowManager.LayoutParams mWindowLayoutParams;
     private final AccessibilityManager mAccessibilityManager;
-    private final MediaActionSound mCameraSound;
+    private final MediaPlayer mCameraSound;
     private final ScrollCaptureClient mScrollCaptureClient;
     private final PhoneWindow mWindow;
     private final DisplayManager mDisplayManager;
@@ -331,8 +334,13 @@
         reloadAssets();
 
         // Setup the Camera shutter sound
-        mCameraSound = new MediaActionSound();
-        mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
+        mCameraSound = MediaPlayer.create(mContext,
+                Uri.fromFile(new File(mContext.getResources().getString(
+                        com.android.internal.R.string.config_cameraShutterSound))), null,
+                new AudioAttributes.Builder()
+                        .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+                        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+                        .build(), AudioSystem.newAudioSessionId());
 
         mCopyBroadcastReceiver = new BroadcastReceiver() {
             @Override
@@ -430,7 +438,9 @@
     void releaseContext() {
         mContext.unregisterReceiver(mCopyBroadcastReceiver);
         mContext.release();
-        mCameraSound.release();
+        if (mCameraSound != null) {
+            mCameraSound.release();
+        }
         mBgExecutor.shutdownNow();
     }
 
@@ -806,7 +816,9 @@
      */
     private void saveScreenshotAndToast(Consumer<Uri> finisher) {
         // Play the shutter sound to notify that we've taken a screenshot
-        mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+        if (mCameraSound != null) {
+            mCameraSound.start();
+        }
 
         saveScreenshotInWorkerThread(
                 /* onComplete */ finisher,
@@ -840,7 +852,9 @@
                 mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash);
 
         // Play the shutter sound to notify that we've taken a screenshot
-        mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+        if (mCameraSound != null) {
+            mCameraSound.start();
+        }
 
         if (DEBUG_ANIM) {
             Log.d(TAG, "starting post-screenshot animation");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index e359968..72c4ce8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -37,6 +37,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.Parcelable;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
@@ -371,10 +372,13 @@
         }
         Drawable drawable;
         try {
+            Trace.beginSection("StatusBarIconView#updateDrawable()");
             drawable = getIcon(mIcon);
         } catch (OutOfMemoryError e) {
             Log.w(TAG, "OOM while inflating " + mIcon.icon + " for slot " + mSlot);
             return false;
+        } finally {
+            Trace.endSection();
         }
 
         if (drawable == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
index 357a12b..324d47e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
@@ -29,9 +29,7 @@
 import com.android.systemui.flags.Flags;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.qs.FooterActionsView;
-import com.android.systemui.qs.QSDetailDisplayer;
 import com.android.systemui.qs.dagger.QSScope;
 import com.android.systemui.qs.user.UserSwitchDialogController;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -44,7 +42,6 @@
 public class MultiUserSwitchController extends ViewController<MultiUserSwitch> {
     private final UserManager mUserManager;
     private final UserSwitcherController mUserSwitcherController;
-    private final QSDetailDisplayer mQsDetailDisplayer;
     private final FalsingManager mFalsingManager;
     private final UserSwitchDialogController mUserSwitchDialogController;
     private final ActivityStarter mActivityStarter;
@@ -66,17 +63,8 @@
                 mActivityStarter.startActivity(intent, true /* dismissShade */,
                         ActivityLaunchAnimator.Controller.fromView(v, null),
                         true /* showOverlockscreenwhenlocked */);
-            } else if (mFeatureFlags.isEnabled(Flags.NEW_USER_SWITCHER)) {
-                mUserSwitchDialogController.showDialog(v);
             } else {
-                View center = mView.getChildCount() > 0 ? mView.getChildAt(0) : mView;
-
-                int[] tmpInt = new int[2];
-                center.getLocationInWindow(tmpInt);
-                tmpInt[0] += center.getWidth() / 2;
-                tmpInt[1] += center.getHeight() / 2;
-
-                mQsDetailDisplayer.showDetailAdapter(getUserDetailAdapter(), tmpInt[0], tmpInt[1]);
+                mUserSwitchDialogController.showDialog(v);
             }
         }
     };
@@ -85,7 +73,6 @@
     public static class Factory {
         private final UserManager mUserManager;
         private final UserSwitcherController mUserSwitcherController;
-        private final QSDetailDisplayer mQsDetailDisplayer;
         private final FalsingManager mFalsingManager;
         private final UserSwitchDialogController mUserSwitchDialogController;
         private final ActivityStarter mActivityStarter;
@@ -93,12 +80,11 @@
 
         @Inject
         public Factory(UserManager userManager, UserSwitcherController userSwitcherController,
-                QSDetailDisplayer qsDetailDisplayer, FalsingManager falsingManager,
+                FalsingManager falsingManager,
                 UserSwitchDialogController userSwitchDialogController, FeatureFlags featureFlags,
                 ActivityStarter activityStarter) {
             mUserManager = userManager;
             mUserSwitcherController = userSwitcherController;
-            mQsDetailDisplayer = qsDetailDisplayer;
             mFalsingManager = falsingManager;
             mUserSwitchDialogController = userSwitchDialogController;
             mActivityStarter = activityStarter;
@@ -107,20 +93,19 @@
 
         public MultiUserSwitchController create(FooterActionsView view) {
             return new MultiUserSwitchController(view.findViewById(R.id.multi_user_switch),
-                    mUserManager, mUserSwitcherController, mQsDetailDisplayer,
+                    mUserManager, mUserSwitcherController,
                     mFalsingManager, mUserSwitchDialogController, mFeatureFlags,
                     mActivityStarter);
         }
     }
 
     private MultiUserSwitchController(MultiUserSwitch view, UserManager userManager,
-            UserSwitcherController userSwitcherController, QSDetailDisplayer qsDetailDisplayer,
+            UserSwitcherController userSwitcherController,
             FalsingManager falsingManager, UserSwitchDialogController userSwitchDialogController,
             FeatureFlags featureFlags, ActivityStarter activityStarter) {
         super(view);
         mUserManager = userManager;
         mUserSwitcherController = userSwitcherController;
-        mQsDetailDisplayer = qsDetailDisplayer;
         mFalsingManager = falsingManager;
         mUserSwitchDialogController = userSwitchDialogController;
         mFeatureFlags = featureFlags;
@@ -143,10 +128,6 @@
         mView.setOnClickListener(null);
     }
 
-    protected DetailAdapter getUserDetailAdapter() {
-        return mUserSwitcherController.mUserDetailAdapter;
-    }
-
     private void registerListener() {
         if (mUserManager.isUserSwitcherEnabled() && mUserListener == null) {
 
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 7a2eceb..d93c013 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -146,12 +146,10 @@
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.FalsingManager.FalsingTapListener;
-import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
-import com.android.systemui.qs.QSDetailDisplayer;
 import com.android.systemui.screenrecord.RecordingController;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.CommandQueue;
@@ -325,7 +323,6 @@
     private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
     private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
     private final IdleViewComponent.Factory mIdleViewComponentFactory;
-    private final QSDetailDisplayer mQSDetailDisplayer;
     private final FragmentService mFragmentService;
     private final ScrimController mScrimController;
     private final PrivacyDotViewController mPrivacyDotViewController;
@@ -773,7 +770,6 @@
             CommunalViewComponent.Factory communalViewComponentFactory,
             IdleViewComponent.Factory idleViewComponentFactory,
             LockscreenShadeTransitionController lockscreenShadeTransitionController,
-            QSDetailDisplayer qsDetailDisplayer,
             NotificationGroupManagerLegacy groupManager,
             NotificationIconAreaController notificationIconAreaController,
             AuthController authController,
@@ -848,7 +844,6 @@
         mContentResolver = contentResolver;
         mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
         mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory;
-        mQSDetailDisplayer = qsDetailDisplayer;
         mFragmentService = fragmentService;
         mSettingsChangeObserver = new SettingsChangeObserver(handler);
         mShouldUseSplitNotificationShade =
@@ -1136,7 +1131,6 @@
                     mKeyguardQsUserSwitchComponentFactory.build(userAvatarView);
             mKeyguardQsUserSwitchController =
                     userSwitcherComponent.getKeyguardQsUserSwitchController();
-            mKeyguardQsUserSwitchController.setNotificationPanelViewController(this);
             mKeyguardQsUserSwitchController.init();
             mKeyguardStatusBarViewController.setKeyguardUserSwitcherEnabled(true);
         } else if (keyguardUserSwitcherView != null) {
@@ -1848,18 +1842,6 @@
         }
     }
 
-    public void expandWithQsDetail(DetailAdapter qsDetailAdapter) {
-        traceQsJank(true /* startTracing */, false /* wasCancelled */);
-        flingSettings(0 /* velocity */, FLING_EXPAND);
-        // When expanding with a panel, there's no meaningful touch point to correspond to. Set the
-        // origin to somewhere above the screen. This is used for animations.
-        int x = mQsFrame.getWidth() / 2;
-        mQSDetailDisplayer.showDetailAdapter(qsDetailAdapter, x, -getHeight());
-        if (mAccessibilityManager.isEnabled()) {
-            mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
-        }
-    }
-
     public void expandWithoutQs() {
         if (isQsExpanded()) {
             flingSettings(0 /* velocity */, FLING_COLLAPSE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 716e8c7..f13334e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1110,6 +1110,12 @@
     }
 
     private void onFoldedStateChanged(boolean isFolded, boolean willGoToSleep) {
+        Trace.beginSection("StatusBar#onFoldedStateChanged");
+        onFoldedStateChangedInternal(isFolded, willGoToSleep);
+        Trace.endSection();
+    }
+
+    private void onFoldedStateChangedInternal(boolean isFolded, boolean willGoToSleep) {
         // Folded state changes are followed by a screen off event.
         // By default turning off the screen also closes the shade.
         // We want to make sure that the shade status is kept after
@@ -3672,6 +3678,7 @@
 
         @Override
         public void onScreenTurnedOff() {
+            Trace.beginSection("StatusBar#onScreenTurnedOff");
             mFalsingCollector.onScreenOff();
             mScrimController.onScreenTurnedOff();
             if (mCloseQsBeforeScreenOff) {
@@ -3679,6 +3686,7 @@
                 mCloseQsBeforeScreenOff = false;
             }
             updateIsKeyguard();
+            Trace.endSection();
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
index cf9a5db..4081962 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -235,11 +235,6 @@
         // Settings are not available in setup
         if (!mDeviceProvisionedController.isCurrentUserSetup()) return;
 
-
-        QSPanelController qsPanelController = mStatusBar.getQSPanelController();
-        if (subPanel != null && qsPanelController != null) {
-            qsPanelController.openDetails(subPanel);
-        }
         mNotificationPanelViewController.expandWithQs();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
index a124753..909261f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
@@ -16,9 +16,15 @@
 
 package com.android.systemui.statusbar.phone.userswitcher
 
+import android.content.Intent
 import android.view.View
+import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.plugins.ActivityStarter
 
 import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.user.UserSwitcherActivity
 import com.android.systemui.util.ViewController
 
 import javax.inject.Inject
@@ -30,7 +36,9 @@
     view: StatusBarUserSwitcherContainer,
     private val tracker: StatusBarUserInfoTracker,
     private val featureController: StatusBarUserSwitcherFeatureController,
-    private val userSwitcherDialogController: UserSwitchDialogController
+    private val userSwitcherDialogController: UserSwitchDialogController,
+    private val featureFlags: FeatureFlags,
+    private val activityStarter: ActivityStarter
 ) : ViewController<StatusBarUserSwitcherContainer>(view),
         StatusBarUserSwitcherController {
     private val listener = object : CurrentUserChipInfoUpdatedListener {
@@ -52,8 +60,17 @@
     override fun onViewAttached() {
         tracker.addCallback(listener)
         featureController.addCallback(featureFlagListener)
-        mView.setOnClickListener {
-            userSwitcherDialogController.showDialog(it)
+        mView.setOnClickListener { view: View ->
+            if (featureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) {
+                val intent = Intent(context, UserSwitcherActivity::class.java)
+                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
+
+                activityStarter.startActivity(intent, true /* dismissShade */,
+                        ActivityLaunchAnimator.Controller.fromView(view, null),
+                        true /* showOverlockscreenwhenlocked */)
+            } else {
+                userSwitcherDialogController.showDialog(view)
+            }
         }
 
         updateEnabled()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index d903639..1d414745 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -21,6 +21,7 @@
 
 import android.annotation.Nullable;
 import android.hardware.devicestate.DeviceStateManager;
+import android.os.Trace;
 import android.util.Log;
 
 import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
@@ -117,11 +118,16 @@
 
     private void updateDeviceState(int state) {
         Log.v(TAG, "updateDeviceState [state=" + state + "]");
-        if (mDeviceState == state) {
-            return;
-        }
+        Trace.beginSection("updateDeviceState [state=" + state + "]");
+        try {
+            if (mDeviceState == state) {
+                return;
+            }
 
-        readPersistedSetting(state);
+            readPersistedSetting(state);
+        } finally {
+            Trace.endSection();
+        }
     }
 
     private void readPersistedSetting(int state) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
index b591545..7e2488f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -37,12 +37,9 @@
 import com.android.systemui.R;
 import com.android.systemui.communal.CommunalStateController;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.qs.tiles.UserDetailView;
 import com.android.systemui.qs.user.UserSwitchDialogController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
@@ -51,13 +48,11 @@
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
-import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.phone.UserAvatarView;
 import com.android.systemui.util.ViewController;
 
 import javax.inject.Inject;
-import javax.inject.Provider;
 
 /**
  * Manages the user switch on the Keyguard that is used for opening the QS user panel.
@@ -81,11 +76,8 @@
     protected final SysuiStatusBarStateController mStatusBarStateController;
     private final ConfigurationController mConfigurationController;
     private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
-    private final KeyguardUserDetailAdapter mUserDetailAdapter;
-    private final FeatureFlags mFeatureFlags;
     private final UserSwitchDialogController mUserSwitchDialogController;
     private final UiEventLogger mUiEventLogger;
-    private NotificationPanelViewController mNotificationPanelViewController;
     private UserAvatarView mUserAvatarView;
     UserSwitcherController.UserRecord mCurrentUser;
 
@@ -133,9 +125,7 @@
             ConfigurationController configurationController,
             SysuiStatusBarStateController statusBarStateController,
             DozeParameters dozeParameters,
-            Provider<UserDetailView.Adapter> userDetailViewAdapterProvider,
             ScreenOffAnimationController screenOffAnimationController,
-            FeatureFlags featureFlags,
             UserSwitchDialogController userSwitchDialogController,
             UiEventLogger uiEventLogger) {
         super(view);
@@ -152,8 +142,6 @@
                 keyguardStateController, dozeParameters,
                 screenOffAnimationController,  /* animateYPos= */ false,
                 /* visibleOnCommunal= */ false);
-        mUserDetailAdapter = new KeyguardUserDetailAdapter(context, userDetailViewAdapterProvider);
-        mFeatureFlags = featureFlags;
         mUserSwitchDialogController = userSwitchDialogController;
         mUiEventLogger = uiEventLogger;
     }
@@ -182,11 +170,7 @@
             mUiEventLogger.log(
                     LockscreenGestureLogger.LockscreenUiEvent.LOCKSCREEN_SWITCH_USER_TAP);
 
-            if (mFeatureFlags.isEnabled(Flags.NEW_USER_SWITCHER)) {
-                mUserSwitchDialogController.showDialog(mView);
-            } else {
-                openQsUserPanel();
-            }
+            mUserSwitchDialogController.showDialog(mView);
         });
 
         mUserAvatarView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@@ -331,40 +315,4 @@
     private boolean isListAnimating() {
         return mKeyguardVisibilityHelper.isVisibilityAnimating();
     }
-
-    private void openQsUserPanel() {
-        mNotificationPanelViewController.expandWithQsDetail(mUserDetailAdapter);
-    }
-
-    public void setNotificationPanelViewController(
-            NotificationPanelViewController notificationPanelViewController) {
-        mNotificationPanelViewController = notificationPanelViewController;
-    }
-
-    class KeyguardUserDetailAdapter extends UserSwitcherController.UserDetailAdapter {
-        KeyguardUserDetailAdapter(Context context,
-                Provider<UserDetailView.Adapter> userDetailViewAdapterProvider) {
-            super(context, userDetailViewAdapterProvider);
-        }
-
-        @Override
-        public boolean shouldAnimate() {
-            return false;
-        }
-
-        @Override
-        public int getDoneText() {
-            return R.string.quick_settings_close_user_panel;
-        }
-
-        @Override
-        public boolean onDoneButtonClicked() {
-            if (mNotificationPanelViewController != null) {
-                mNotificationPanelViewController.animateCloseQs(true /* animateAway */);
-                return true;
-            } else {
-                return false;
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 7a7af4d..e416ed1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -51,7 +51,6 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.WindowManagerGlobal;
 import android.widget.BaseAdapter;
 
@@ -60,7 +59,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.LatencyTracker;
 import com.android.settingslib.RestrictedLockUtilsInternal;
 import com.android.systemui.Dumpable;
@@ -77,9 +75,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.qs.QSUserSwitcherEvent;
-import com.android.systemui.qs.tiles.UserDetailView;
 import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -96,7 +92,6 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import javax.inject.Inject;
-import javax.inject.Provider;
 
 /**
  * Keeps a list of all users on the device for user switching.
@@ -153,7 +148,6 @@
     private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
     private final UiEventLogger mUiEventLogger;
     private final IActivityManager mActivityManager;
-    public final DetailAdapter mUserDetailAdapter;
     private final Executor mBgExecutor;
     private final boolean mGuestUserAutoCreated;
     private final AtomicBoolean mGuestIsResetting;
@@ -177,7 +171,6 @@
             FalsingManager falsingManager,
             TelephonyListenerManager telephonyListenerManager,
             IActivityTaskManager activityTaskManager,
-            UserDetailAdapter userDetailAdapter,
             SecureSettings secureSettings,
             @Background Executor bgExecutor,
             InteractionJankMonitor interactionJankMonitor,
@@ -196,7 +189,6 @@
         mLatencyTracker = latencyTracker;
         mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(
                 this, mUserTracker, mUiEventLogger, secureSettings);
-        mUserDetailAdapter = userDetailAdapter;
         mBgExecutor = bgExecutor;
         if (!UserManager.isGuestUserEphemeral()) {
             mGuestResumeSessionReceiver.register(mBroadcastDispatcher);
@@ -1118,77 +1110,6 @@
         }
     }
 
-    public static class UserDetailAdapter implements DetailAdapter {
-        private final Intent USER_SETTINGS_INTENT = new Intent(Settings.ACTION_USER_SETTINGS);
-
-        private final Context mContext;
-        private final Provider<UserDetailView.Adapter> mUserDetailViewAdapterProvider;
-
-        @Inject
-        UserDetailAdapter(Context context,
-                Provider<UserDetailView.Adapter> userDetailViewAdapterProvider) {
-            mContext = context;
-            mUserDetailViewAdapterProvider = userDetailViewAdapterProvider;
-        }
-
-        @Override
-        public CharSequence getTitle() {
-            return mContext.getString(R.string.quick_settings_user_title);
-        }
-
-        @Override
-        public View createDetailView(Context context, View convertView, ViewGroup parent) {
-            UserDetailView v;
-            if (!(convertView instanceof UserDetailView)) {
-                v = UserDetailView.inflate(context, parent, false);
-                v.setAdapter(mUserDetailViewAdapterProvider.get());
-            } else {
-                v = (UserDetailView) convertView;
-            }
-            v.refreshAdapter();
-            return v;
-        }
-
-        @Override
-        public Intent getSettingsIntent() {
-            return USER_SETTINGS_INTENT;
-        }
-
-        @Override
-        public int getSettingsText() {
-            return R.string.quick_settings_more_user_settings;
-        }
-
-        @Override
-        public Boolean getToggleState() {
-            return null;
-        }
-
-        @Override
-        public void setToggleState(boolean state) {
-        }
-
-        @Override
-        public int getMetricsCategory() {
-            return MetricsEvent.QS_USERDETAIL;
-        }
-
-        @Override
-        public UiEventLogger.UiEventEnum openDetailEvent() {
-            return QSUserSwitcherEvent.QS_USER_DETAIL_OPEN;
-        }
-
-        @Override
-        public UiEventLogger.UiEventEnum closeDetailEvent() {
-            return QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE;
-        }
-
-        @Override
-        public UiEventLogger.UiEventEnum moreSettingsEvent() {
-            return QSUserSwitcherEvent.QS_USER_MORE_SETTINGS;
-        }
-    };
-
     private final KeyguardStateController.Callback mCallback =
             new KeyguardStateController.Callback() {
                 @Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java
deleted file mode 100644
index 4d95969..0000000
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerZenModePanel.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.tuner;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.Intent;
-import android.provider.Settings;
-import android.provider.Settings.Global;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.Checkable;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.systemui.Prefs;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.volume.ZenModePanel;
-import com.android.systemui.volume.ZenModePanel.Callback;
-
-public class TunerZenModePanel extends LinearLayout implements OnClickListener {
-    private static final String TAG = "TunerZenModePanel";
-
-    private Callback mCallback;
-    private ZenModePanel mZenModePanel;
-    private View mHeaderSwitch;
-    private int mZenMode;
-    private ZenModeController mController;
-    private View mButtons;
-    private View mMoreSettings;
-    private View mDone;
-    private OnClickListener mDoneListener;
-    private boolean mEditing;
-
-    public TunerZenModePanel(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public void init(ZenModeController zenModeController) {
-        mController = zenModeController;
-        mHeaderSwitch = findViewById(R.id.tuner_zen_switch);
-        mHeaderSwitch.setVisibility(View.VISIBLE);
-        mHeaderSwitch.setOnClickListener(this);
-        ((TextView) mHeaderSwitch.findViewById(android.R.id.title)).setText(
-                R.string.quick_settings_dnd_label);
-        mZenModePanel = (ZenModePanel) findViewById(R.id.zen_mode_panel);
-        mZenModePanel.init(zenModeController);
-        mButtons = findViewById(R.id.tuner_zen_buttons);
-        mMoreSettings = mButtons.findViewById(android.R.id.button2);
-        mMoreSettings.setOnClickListener(this);
-        ((TextView) mMoreSettings).setText(R.string.quick_settings_more_settings);
-        mDone = mButtons.findViewById(android.R.id.button1);
-        mDone.setOnClickListener(this);
-        ((TextView) mDone).setText(R.string.quick_settings_done);
-        // Hide the resizing space because it causes issues in the volume panel.
-        ViewGroup detail_header = findViewById(R.id.tuner_zen_switch);
-        detail_header.getChildAt(0).setVisibility(View.GONE);
-        // No background so it can blend with volume panel.
-        findViewById(R.id.edit_container).setBackground(null);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mEditing = false;
-    }
-
-    public void setCallback(Callback zenPanelCallback) {
-        mCallback = zenPanelCallback;
-        mZenModePanel.setCallback(zenPanelCallback);
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (v == mHeaderSwitch) {
-            mEditing = true;
-            if (mZenMode == Global.ZEN_MODE_OFF) {
-                mZenMode = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN,
-                        Global.ZEN_MODE_ALARMS);
-                mController.setZen(mZenMode, null, TAG);
-                postUpdatePanel();
-            } else {
-                mZenMode = Global.ZEN_MODE_OFF;
-                mController.setZen(Global.ZEN_MODE_OFF, null, TAG);
-                postUpdatePanel();
-            }
-        } else if (v == mMoreSettings) {
-            Intent intent = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            getContext().startActivity(intent);
-        } else if (v == mDone) {
-            mEditing = false;
-            setVisibility(View.GONE);
-            mDoneListener.onClick(v);
-        }
-    }
-
-    public boolean isEditing() {
-        return mEditing;
-    }
-
-    public void setZenState(int zenMode) {
-        mZenMode = zenMode;
-        postUpdatePanel();
-    }
-
-    private void postUpdatePanel() {
-        // The complicated structure from reusing the same ZenPanel has resulted in some
-        // unstableness/flickering from callbacks coming in quickly. To solve this just
-        // post the UI updates a little bit.
-        removeCallbacks(mUpdate);
-        postDelayed(mUpdate, 40);
-    }
-
-    public void setDoneListener(OnClickListener onClickListener) {
-        mDoneListener = onClickListener;
-    }
-
-    private void updatePanel() {
-        boolean zenOn = mZenMode != Global.ZEN_MODE_OFF;
-        ((Checkable) mHeaderSwitch.findViewById(android.R.id.toggle)).setChecked(zenOn);
-        mZenModePanel.setVisibility(zenOn ? View.VISIBLE : View.GONE);
-        mButtons.setVisibility(zenOn ? View.VISIBLE : View.GONE);
-    }
-
-    private final Runnable mUpdate = new Runnable() {
-        @Override
-        public void run() {
-            updatePanel();
-        }
-    };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java b/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java
index 0bbf56c..db35437e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Condition.java
@@ -36,6 +36,7 @@
     private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
     private boolean mIsConditionMet = false;
     private boolean mStarted = false;
+    private boolean mOverriding = false;
 
     /**
      * Starts monitoring the condition.
@@ -48,6 +49,21 @@
     protected abstract void stop();
 
     /**
+     * Sets whether this condition's value overrides others in determining the overall state.
+     */
+    public void setOverriding(boolean overriding) {
+        mOverriding = overriding;
+        updateCondition(mIsConditionMet);
+    }
+
+    /**
+     * Returns whether the current condition overrides
+     */
+    public boolean isOverridingCondition() {
+        return mOverriding;
+    }
+
+    /**
      * Registers a callback to receive updates once started. This should be called before
      * {@link #start()}. Also triggers the callback immediately if already started.
      */
@@ -57,7 +73,7 @@
         mCallbacks.add(new WeakReference<>(callback));
 
         if (mStarted) {
-            callback.onConditionChanged(this, mIsConditionMet);
+            callback.onConditionChanged(this);
             return;
         }
 
@@ -107,11 +123,15 @@
             if (cb == null) {
                 iterator.remove();
             } else {
-                cb.onConditionChanged(this, mIsConditionMet);
+                cb.onConditionChanged(this);
             }
         }
     }
 
+    public boolean isConditionMet() {
+        return mIsConditionMet;
+    }
+
     private boolean shouldLog() {
         return Log.isLoggable(mTag, Log.DEBUG);
     }
@@ -124,8 +144,7 @@
          * Called when the fulfillment of the condition changes.
          *
          * @param condition The condition in question.
-         * @param isConditionMet True if the condition has been fulfilled. False otherwise.
          */
-        void onConditionChanged(Condition condition, boolean isConditionMet);
+        void onConditionChanged(Condition condition);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
index 8b6e982..7f3d54d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
@@ -18,15 +18,17 @@
 
 import android.util.Log;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.statusbar.policy.CallbackController;
 
 import org.jetbrains.annotations.NotNull;
 
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 
@@ -41,9 +43,7 @@
 
     // Set of all conditions that need to be monitored.
     private final Set<Condition> mConditions;
-
-    // Map of values of each condition.
-    private final HashMap<Condition, Boolean> mConditionsMap = new HashMap<>();
+    private final Executor mExecutor;
 
     // Whether all conditions have been met.
     private boolean mAllConditionsMet = false;
@@ -52,10 +52,43 @@
     private boolean mHaveConditionsStarted = false;
 
     // Callback for when each condition has been updated.
-    private final Condition.Callback mConditionCallback = (condition, isConditionMet) -> {
-        mConditionsMap.put(condition, isConditionMet);
+    private final Condition.Callback mConditionCallback = new Condition.Callback() {
+        @Override
+        public void onConditionChanged(Condition condition) {
+            mExecutor.execute(() -> updateConditionMetState());
+        }
+    };
 
-        final boolean newAllConditionsMet = !mConditionsMap.containsValue(false);
+    @Inject
+    public Monitor(Executor executor, Set<Condition> conditions, Set<Callback> callbacks) {
+        mConditions = new HashSet<>();
+        mExecutor = executor;
+
+        if (conditions != null) {
+            mConditions.addAll(conditions);
+        }
+
+        if (callbacks == null) {
+            return;
+        }
+
+        for (Callback callback : callbacks) {
+            addCallbackLocked(callback);
+        }
+    }
+
+    private void updateConditionMetState() {
+        // Overriding conditions do not override each other
+        final Collection<Condition> overridingConditions = mConditions.stream()
+                .filter(Condition::isOverridingCondition).collect(Collectors.toSet());
+
+        final Collection<Condition> targetCollection = overridingConditions.isEmpty()
+                ? mConditions : overridingConditions;
+
+        final boolean newAllConditionsMet = targetCollection.isEmpty() ? true : targetCollection
+                .stream()
+                .map(Condition::isConditionMet)
+                .allMatch(conditionMet -> conditionMet);
 
         if (newAllConditionsMet == mAllConditionsMet) {
             return;
@@ -74,32 +107,44 @@
                 callback.onConditionsChanged(mAllConditionsMet);
             }
         }
-    };
-
-    @Inject
-    public Monitor(Set<Condition> conditions, Set<Callback> callbacks) {
-        mConditions = conditions;
-
-        // If there is no condition, give green pass.
-        if (mConditions.isEmpty()) {
-            mAllConditionsMet = true;
-            return;
-        }
-
-        // Initializes the conditions map and registers a callback for each condition.
-        mConditions.forEach((condition -> mConditionsMap.put(condition, false)));
-
-        if (callbacks == null) {
-            return;
-        }
-
-        for (Callback callback : callbacks) {
-            addCallback(callback);
-        }
     }
 
-    @Override
-    public void addCallback(@NotNull Callback callback) {
+    private void addConditionLocked(@NotNull Condition condition) {
+        mConditions.add(condition);
+
+        if (!mHaveConditionsStarted) {
+            return;
+        }
+
+        condition.addCallback(mConditionCallback);
+        updateConditionMetState();
+    }
+
+    /**
+     * Adds a condition for the monitor to listen to and consider when determining whether the
+     * overall condition state is met.
+     */
+    public void addCondition(@NotNull Condition condition) {
+        mExecutor.execute(() -> addConditionLocked(condition));
+    }
+
+    /**
+     * Removes a condition from further consideration.
+     */
+    public void removeCondition(@NotNull Condition condition) {
+        mExecutor.execute(() -> {
+            mConditions.remove(condition);
+
+            if (!mHaveConditionsStarted) {
+                return;
+            }
+
+            condition.removeCallback(mConditionCallback);
+            updateConditionMetState();
+        });
+    }
+
+    private void addCallbackLocked(@NotNull Callback callback) {
         if (shouldLog()) Log.d(mTag, "adding callback");
         mCallbacks.add(callback);
 
@@ -109,36 +154,36 @@
         if (!mHaveConditionsStarted) {
             if (shouldLog()) Log.d(mTag, "starting all conditions");
             mConditions.forEach(condition -> condition.addCallback(mConditionCallback));
+            updateConditionMetState();
             mHaveConditionsStarted = true;
         }
     }
 
     @Override
-    public void removeCallback(@NotNull Callback callback) {
-        if (shouldLog()) Log.d(mTag, "removing callback");
-        final Iterator<Callback> iterator = mCallbacks.iterator();
-        while (iterator.hasNext()) {
-            final Callback cb = iterator.next();
-            if (cb == null || cb == callback) {
-                iterator.remove();
-            }
-        }
-
-        if (mCallbacks.isEmpty() && mHaveConditionsStarted) {
-            if (shouldLog()) Log.d(mTag, "stopping all conditions");
-            mConditions.forEach(condition -> condition.removeCallback(mConditionCallback));
-
-            mAllConditionsMet = false;
-            mHaveConditionsStarted = false;
-        }
+    public void addCallback(@NotNull Callback callback) {
+        mExecutor.execute(() -> addCallbackLocked(callback));
     }
 
-    /**
-     * Force updates each condition to the value provided.
-     */
-    @VisibleForTesting
-    public void overrideAllConditionsMet(boolean value) {
-        mConditions.forEach(condition -> condition.updateCondition(value));
+    @Override
+    public void removeCallback(@NotNull Callback callback) {
+        mExecutor.execute(() -> {
+            if (shouldLog()) Log.d(mTag, "removing callback");
+            final Iterator<Callback> iterator = mCallbacks.iterator();
+            while (iterator.hasNext()) {
+                final Callback cb = iterator.next();
+                if (cb == null || cb == callback) {
+                    iterator.remove();
+                }
+            }
+
+            if (mCallbacks.isEmpty() && mHaveConditionsStarted) {
+                if (shouldLog()) Log.d(mTag, "stopping all conditions");
+                mConditions.forEach(condition -> condition.removeCallback(mConditionCallback));
+
+                mAllConditionsMet = false;
+                mHaveConditionsStarted = false;
+            }
+        });
     }
 
     private boolean shouldLog() {
diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
index b64d7be..d8de07d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
@@ -21,6 +21,7 @@
 import com.android.internal.view.RotationPolicy
 import com.android.internal.view.RotationPolicy.RotationPolicyListener
 import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.traceSection
 import javax.inject.Inject
 
 /**
@@ -44,7 +45,9 @@
         RotationPolicyWrapper {
 
     override fun setRotationLock(enabled: Boolean) {
-        RotationPolicy.setRotationLock(context, enabled)
+        traceSection("RotationPolicyWrapperImpl#setRotationLock") {
+            RotationPolicy.setRotationLock(context, enabled)
+        }
     }
 
     override fun setRotationLockAtAngle(enabled: Boolean, rotation: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index c083c14..955d616 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -22,6 +22,7 @@
 import android.content.res.Configuration;
 import android.media.VolumePolicy;
 import android.os.Bundle;
+import android.provider.Settings;
 import android.view.WindowManager.LayoutParams;
 
 import com.android.settingslib.applications.InterestingConfigChanges;
@@ -59,6 +60,11 @@
     public static final boolean DEFAULT_VOLUME_UP_TO_EXIT_SILENT = false;
     public static final boolean DEFAULT_DO_NOT_DISTURB_WHEN_SILENT = false;
 
+    private static final Intent ZEN_SETTINGS =
+            new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
+    private static final Intent ZEN_PRIORITY_SETTINGS =
+            new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS);
+
     protected final Context mContext;
     private final VolumeDialogControllerImpl mController;
     private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
@@ -191,12 +197,12 @@
     private final VolumeDialogImpl.Callback mVolumeDialogCallback = new VolumeDialogImpl.Callback() {
         @Override
         public void onZenSettingsClicked() {
-            startSettings(ZenModePanel.ZEN_SETTINGS);
+            startSettings(ZEN_SETTINGS);
         }
 
         @Override
         public void onZenPrioritySettingsClicked() {
-            startSettings(ZenModePanel.ZEN_PRIORITY_SETTINGS);
+            startSettings(ZEN_PRIORITY_SETTINGS);
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 7bb987c..e69de29 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -1,1077 +0,0 @@
-/**
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.volume;
-
-import android.animation.LayoutTransition;
-import android.animation.LayoutTransition.TransitionListener;
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.content.res.Configuration;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.provider.Settings;
-import android.provider.Settings.Global;
-import android.service.notification.Condition;
-import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.ZenRule;
-import android.text.TextUtils;
-import android.text.format.DateFormat;
-import android.text.format.DateUtils;
-import android.util.ArraySet;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.MathUtils;
-import android.util.Slog;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.RadioButton;
-import android.widget.RadioGroup;
-import android.widget.TextView;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Prefs;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.ZenModeController;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.Locale;
-import java.util.Objects;
-
-public class ZenModePanel extends FrameLayout {
-    private static final String TAG = "ZenModePanel";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    public static final int STATE_MODIFY = 0;
-    public static final int STATE_AUTO_RULE = 1;
-    public static final int STATE_OFF = 2;
-
-    private static final int SECONDS_MS = 1000;
-    private static final int MINUTES_MS = 60 * SECONDS_MS;
-
-    private static final int[] MINUTE_BUCKETS = ZenModeConfig.MINUTE_BUCKETS;
-    private static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0];
-    private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1];
-    private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60);
-    private static final int FOREVER_CONDITION_INDEX = 0;
-    private static final int COUNTDOWN_CONDITION_INDEX = 1;
-    private static final int COUNTDOWN_ALARM_CONDITION_INDEX = 2;
-    private static final int COUNTDOWN_CONDITION_COUNT = 2;
-
-    public static final Intent ZEN_SETTINGS
-            = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
-    public static final Intent ZEN_PRIORITY_SETTINGS
-            = new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS);
-
-    private static final long TRANSITION_DURATION = 300;
-
-    private final Context mContext;
-    protected final LayoutInflater mInflater;
-    private final H mHandler = new H();
-    private final ZenPrefs mPrefs;
-    private final TransitionHelper mTransitionHelper = new TransitionHelper();
-    private final Uri mForeverId;
-    private final ConfigurableTexts mConfigurableTexts;
-
-    private String mTag = TAG + "/" + Integer.toHexString(System.identityHashCode(this));
-
-    protected SegmentedButtons mZenButtons;
-    private View mZenIntroduction;
-    private TextView mZenIntroductionMessage;
-    private View mZenIntroductionConfirm;
-    private TextView mZenIntroductionCustomize;
-    protected LinearLayout mZenConditions;
-    private TextView mZenAlarmWarning;
-    private RadioGroup mZenRadioGroup;
-    private LinearLayout mZenRadioGroupContent;
-
-    private Callback mCallback;
-    private ZenModeController mController;
-    private Condition mExitCondition;
-    private int mBucketIndex = -1;
-    private boolean mExpanded;
-    private boolean mHidden;
-    private int mSessionZen;
-    private int mAttachedZen;
-    private boolean mAttached;
-    private Condition mSessionExitCondition;
-    private boolean mVoiceCapable;
-
-    protected int mZenModeConditionLayoutId;
-    protected int mZenModeButtonLayoutId;
-    private View mEmpty;
-    private TextView mEmptyText;
-    private ImageView mEmptyIcon;
-    private View mAutoRule;
-    private TextView mAutoTitle;
-    private int mState = STATE_MODIFY;
-    private ViewGroup mEdit;
-
-    public ZenModePanel(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mContext = context;
-        mPrefs = new ZenPrefs();
-        mInflater = LayoutInflater.from(mContext);
-        mForeverId = Condition.newId(mContext).appendPath("forever").build();
-        mConfigurableTexts = new ConfigurableTexts(mContext);
-        mVoiceCapable = Util.isVoiceCapable(mContext);
-        mZenModeConditionLayoutId = R.layout.zen_mode_condition;
-        mZenModeButtonLayoutId = R.layout.zen_mode_button;
-        if (DEBUG) Log.d(mTag, "new ZenModePanel");
-    }
-
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("ZenModePanel state:");
-        pw.print("  mAttached="); pw.println(mAttached);
-        pw.print("  mHidden="); pw.println(mHidden);
-        pw.print("  mExpanded="); pw.println(mExpanded);
-        pw.print("  mSessionZen="); pw.println(mSessionZen);
-        pw.print("  mAttachedZen="); pw.println(mAttachedZen);
-        pw.print("  mConfirmedPriorityIntroduction=");
-        pw.println(mPrefs.mConfirmedPriorityIntroduction);
-        pw.print("  mConfirmedSilenceIntroduction=");
-        pw.println(mPrefs.mConfirmedSilenceIntroduction);
-        pw.print("  mVoiceCapable="); pw.println(mVoiceCapable);
-        mTransitionHelper.dump(fd, pw, args);
-    }
-
-    protected void createZenButtons() {
-        mZenButtons = findViewById(R.id.zen_buttons);
-        mZenButtons.addButton(R.string.interruption_level_none_twoline,
-                R.string.interruption_level_none_with_warning,
-                Global.ZEN_MODE_NO_INTERRUPTIONS);
-        mZenButtons.addButton(R.string.interruption_level_alarms_twoline,
-                R.string.interruption_level_alarms,
-                Global.ZEN_MODE_ALARMS);
-        mZenButtons.addButton(R.string.interruption_level_priority_twoline,
-                R.string.interruption_level_priority,
-                Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-        mZenButtons.setCallback(mZenButtonsCallback);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        createZenButtons();
-        mZenIntroduction = findViewById(R.id.zen_introduction);
-        mZenIntroductionMessage = findViewById(R.id.zen_introduction_message);
-        mZenIntroductionConfirm = findViewById(R.id.zen_introduction_confirm);
-        mZenIntroductionConfirm.setOnClickListener(v -> confirmZenIntroduction());
-        mZenIntroductionCustomize = findViewById(R.id.zen_introduction_customize);
-        mZenIntroductionCustomize.setOnClickListener(v -> {
-            confirmZenIntroduction();
-            if (mCallback != null) {
-                mCallback.onPrioritySettings();
-            }
-        });
-        mConfigurableTexts.add(mZenIntroductionCustomize, R.string.zen_priority_customize_button);
-
-        mZenConditions = findViewById(R.id.zen_conditions);
-        mZenAlarmWarning = findViewById(R.id.zen_alarm_warning);
-        mZenRadioGroup = findViewById(R.id.zen_radio_buttons);
-        mZenRadioGroupContent = findViewById(R.id.zen_radio_buttons_content);
-
-        mEdit = findViewById(R.id.edit_container);
-
-        mEmpty = findViewById(android.R.id.empty);
-        mEmpty.setVisibility(INVISIBLE);
-        mEmptyText = mEmpty.findViewById(android.R.id.title);
-        mEmptyIcon = mEmpty.findViewById(android.R.id.icon);
-
-        mAutoRule = findViewById(R.id.auto_rule);
-        mAutoTitle = mAutoRule.findViewById(android.R.id.title);
-        mAutoRule.setVisibility(INVISIBLE);
-    }
-
-    public void setEmptyState(int icon, int text) {
-        mEmptyIcon.post(() -> {
-            mEmptyIcon.setImageResource(icon);
-            mEmptyText.setText(text);
-        });
-    }
-
-    public void setAutoText(CharSequence text) {
-        mAutoTitle.post(() -> mAutoTitle.setText(text));
-    }
-
-    public void setState(int state) {
-        if (mState == state) return;
-        transitionFrom(getView(mState), getView(state));
-        mState = state;
-    }
-
-    private void transitionFrom(View from, View to) {
-        from.post(() -> {
-            // TODO: Better transitions
-            to.setAlpha(0);
-            to.setVisibility(VISIBLE);
-            to.bringToFront();
-            to.animate().cancel();
-            to.animate().alpha(1)
-                    .setDuration(TRANSITION_DURATION)
-                    .withEndAction(() -> from.setVisibility(INVISIBLE))
-                    .start();
-        });
-    }
-
-    private View getView(int state) {
-        switch (state) {
-            case STATE_AUTO_RULE:
-                return mAutoRule;
-            case STATE_OFF:
-                return mEmpty;
-            default:
-                return mEdit;
-        }
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        mConfigurableTexts.update();
-        if (mZenButtons != null) {
-            mZenButtons.update();
-        }
-    }
-
-    private void confirmZenIntroduction() {
-        final String prefKey = prefKeyForConfirmation(getSelectedZen(Global.ZEN_MODE_OFF));
-        if (prefKey == null) return;
-        if (DEBUG) Log.d(TAG, "confirmZenIntroduction " + prefKey);
-        Prefs.putBoolean(mContext, prefKey, true);
-        mHandler.sendEmptyMessage(H.UPDATE_WIDGETS);
-    }
-
-    private static String prefKeyForConfirmation(int zen) {
-        switch (zen) {
-            case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
-                return Prefs.Key.DND_CONFIRMED_PRIORITY_INTRODUCTION;
-            case Global.ZEN_MODE_NO_INTERRUPTIONS:
-                return Prefs.Key.DND_CONFIRMED_SILENCE_INTRODUCTION;
-            case Global.ZEN_MODE_ALARMS:
-                return Prefs.Key.DND_CONFIRMED_ALARM_INTRODUCTION;
-            default:
-                return null;
-        }
-    }
-
-    private void onAttach() {
-        setExpanded(true);
-        mAttachedZen = mController.getZen();
-        ZenRule manualRule = mController.getManualRule();
-        mExitCondition = manualRule != null ? manualRule.condition : null;
-        if (DEBUG) Log.d(mTag, "onAttach " + mAttachedZen + " " + manualRule);
-        handleUpdateManualRule(manualRule);
-        mZenButtons.setSelectedValue(mAttachedZen, false);
-        mSessionZen = mAttachedZen;
-        mTransitionHelper.clear();
-        mController.addCallback(mZenCallback);
-        setSessionExitCondition(copy(mExitCondition));
-        updateWidgets();
-        setAttached(true);
-    }
-
-    private void onDetach() {
-        if (DEBUG) Log.d(mTag, "onDetach");
-        setExpanded(false);
-        checkForAttachedZenChange();
-        setAttached(false);
-        mAttachedZen = -1;
-        mSessionZen = -1;
-        mController.removeCallback(mZenCallback);
-        setSessionExitCondition(null);
-        mTransitionHelper.clear();
-    }
-
-    @VisibleForTesting
-    void setAttached(boolean attached) {
-        mAttached = attached;
-    }
-
-    @Override
-    public void onVisibilityAggregated(boolean isVisible) {
-        super.onVisibilityAggregated(isVisible);
-        if (isVisible == mAttached) return;
-        if (isVisible) {
-            onAttach();
-        } else {
-            onDetach();
-        }
-    }
-
-    private void setSessionExitCondition(Condition condition) {
-        if (Objects.equals(condition, mSessionExitCondition)) return;
-        if (DEBUG) Log.d(mTag, "mSessionExitCondition=" + getConditionId(condition));
-        mSessionExitCondition = condition;
-    }
-
-    public void setHidden(boolean hidden) {
-        if (mHidden == hidden) return;
-        if (DEBUG) Log.d(mTag, "hidden=" + hidden);
-        mHidden = hidden;
-        updateWidgets();
-    }
-
-    private void checkForAttachedZenChange() {
-        final int selectedZen = getSelectedZen(-1);
-        if (DEBUG) Log.d(mTag, "selectedZen=" + selectedZen);
-        if (selectedZen != mAttachedZen) {
-            if (DEBUG) Log.d(mTag, "attachedZen: " + mAttachedZen + " -> " + selectedZen);
-            if (selectedZen == Global.ZEN_MODE_NO_INTERRUPTIONS) {
-                mPrefs.trackNoneSelected();
-            }
-        }
-    }
-
-    private void setExpanded(boolean expanded) {
-        if (expanded == mExpanded) return;
-        if (DEBUG) Log.d(mTag, "setExpanded " + expanded);
-        mExpanded = expanded;
-        updateWidgets();
-        fireExpanded();
-    }
-
-    protected void addZenConditions(int count) {
-        for (int i = 0; i < count; i++) {
-            final View rb = mInflater.inflate(mZenModeButtonLayoutId, mEdit, false);
-            rb.setId(i);
-            mZenRadioGroup.addView(rb);
-            final View rbc = mInflater.inflate(mZenModeConditionLayoutId, mEdit, false);
-            rbc.setId(i + count);
-            mZenRadioGroupContent.addView(rbc);
-        }
-    }
-
-    public void init(ZenModeController controller) {
-        mController = controller;
-        final int minConditions = 1 /*forever*/ + COUNTDOWN_CONDITION_COUNT;
-        addZenConditions(minConditions);
-        mSessionZen = getSelectedZen(-1);
-        handleUpdateManualRule(mController.getManualRule());
-        if (DEBUG) Log.d(mTag, "init mExitCondition=" + mExitCondition);
-        hideAllConditions();
-    }
-
-    private void setExitCondition(Condition exitCondition) {
-        if (Objects.equals(mExitCondition, exitCondition)) return;
-        mExitCondition = exitCondition;
-        if (DEBUG) Log.d(mTag, "mExitCondition=" + getConditionId(mExitCondition));
-        updateWidgets();
-    }
-
-    private static Uri getConditionId(Condition condition) {
-        return condition != null ? condition.id : null;
-    }
-
-    private Uri getRealConditionId(Condition condition) {
-        return isForever(condition) ? null : getConditionId(condition);
-    }
-
-    private static Condition copy(Condition condition) {
-        return condition == null ? null : condition.copy();
-    }
-
-    public void setCallback(Callback callback) {
-        mCallback = callback;
-    }
-
-    @VisibleForTesting
-    void handleUpdateManualRule(ZenRule rule) {
-        final int zen = rule != null ? rule.zenMode : Global.ZEN_MODE_OFF;
-        handleUpdateZen(zen);
-        final Condition c = rule == null ? null
-                : rule.condition != null ? rule.condition
-                : createCondition(rule.conditionId);
-        handleUpdateConditions(c);
-        setExitCondition(c);
-    }
-
-    private Condition createCondition(Uri conditionId) {
-        if (ZenModeConfig.isValidCountdownToAlarmConditionId(conditionId)) {
-            long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
-            Condition c = ZenModeConfig.toNextAlarmCondition(
-                    mContext, time, ActivityManager.getCurrentUser());
-            return c;
-        } else if (ZenModeConfig.isValidCountdownConditionId(conditionId)) {
-            long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
-            int mins = (int) ((time - System.currentTimeMillis() + DateUtils.MINUTE_IN_MILLIS / 2)
-                    / DateUtils.MINUTE_IN_MILLIS);
-            Condition c = ZenModeConfig.toTimeCondition(mContext, time, mins,
-                    ActivityManager.getCurrentUser(), false);
-            return c;
-        }
-        // If there is a manual rule, but it has no condition listed then it is forever.
-        return forever();
-    }
-
-    private void handleUpdateZen(int zen) {
-        if (mSessionZen != -1 && mSessionZen != zen) {
-            mSessionZen = zen;
-        }
-        mZenButtons.setSelectedValue(zen, false /* fromClick */);
-        updateWidgets();
-    }
-
-    @VisibleForTesting
-    int getSelectedZen(int defValue) {
-        final Object zen = mZenButtons.getSelectedValue();
-        return zen != null ? (Integer) zen : defValue;
-    }
-
-    private void updateWidgets() {
-        if (mTransitionHelper.isTransitioning()) {
-            mTransitionHelper.pendingUpdateWidgets();
-            return;
-        }
-        final int zen = getSelectedZen(Global.ZEN_MODE_OFF);
-        final boolean zenImportant = zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
-        final boolean zenAlarm = zen == Global.ZEN_MODE_ALARMS;
-        final boolean introduction = (zenImportant && !mPrefs.mConfirmedPriorityIntroduction
-                || zenNone && !mPrefs.mConfirmedSilenceIntroduction
-                || zenAlarm && !mPrefs.mConfirmedAlarmIntroduction);
-
-        mZenButtons.setVisibility(mHidden ? GONE : VISIBLE);
-        mZenIntroduction.setVisibility(introduction ? VISIBLE : GONE);
-        if (introduction) {
-            int message = zenImportant
-                    ? R.string.zen_priority_introduction
-                    : zenAlarm
-                            ? R.string.zen_alarms_introduction
-                            : mVoiceCapable
-                                    ? R.string.zen_silence_introduction_voice
-                                    : R.string.zen_silence_introduction;
-            mConfigurableTexts.add(mZenIntroductionMessage, message);
-            mConfigurableTexts.update();
-            mZenIntroductionCustomize.setVisibility(zenImportant ? VISIBLE : GONE);
-        }
-        final String warning = computeAlarmWarningText(zenNone);
-        mZenAlarmWarning.setVisibility(warning != null ? VISIBLE : GONE);
-        mZenAlarmWarning.setText(warning);
-    }
-
-    private String computeAlarmWarningText(boolean zenNone) {
-        if (!zenNone) {
-            return null;
-        }
-        final long now = System.currentTimeMillis();
-        final long nextAlarm = mController.getNextAlarm();
-        if (nextAlarm < now) {
-            return null;
-        }
-        int warningRes = 0;
-        if (mSessionExitCondition == null || isForever(mSessionExitCondition)) {
-            warningRes = R.string.zen_alarm_warning_indef;
-        } else {
-            final long time = ZenModeConfig.tryParseCountdownConditionId(mSessionExitCondition.id);
-            if (time > now && nextAlarm < time) {
-                warningRes = R.string.zen_alarm_warning;
-            }
-        }
-        if (warningRes == 0) {
-            return null;
-        }
-        final boolean soon = (nextAlarm - now) < 24 * 60 * 60 * 1000;
-        final boolean is24 = DateFormat.is24HourFormat(mContext, ActivityManager.getCurrentUser());
-        final String skeleton = soon ? (is24 ? "Hm" : "hma") : (is24 ? "EEEHm" : "EEEhma");
-        final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
-        final CharSequence formattedTime = DateFormat.format(pattern, nextAlarm);
-        final int templateRes = soon ? R.string.alarm_template : R.string.alarm_template_far;
-        final String template = getResources().getString(templateRes, formattedTime);
-        return getResources().getString(warningRes, template);
-    }
-
-    @VisibleForTesting
-    void handleUpdateConditions(Condition c) {
-        if (mTransitionHelper.isTransitioning()) {
-            return;
-        }
-        // forever
-        bind(forever(), mZenRadioGroupContent.getChildAt(FOREVER_CONDITION_INDEX),
-                FOREVER_CONDITION_INDEX);
-        if (c == null) {
-            bindGenericCountdown();
-            bindNextAlarm(getTimeUntilNextAlarmCondition());
-        } else if (isForever(c)) {
-
-            getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true);
-            bindGenericCountdown();
-            bindNextAlarm(getTimeUntilNextAlarmCondition());
-        } else {
-            if (isAlarm(c)) {
-                bindGenericCountdown();
-                bindNextAlarm(c);
-                getConditionTagAt(COUNTDOWN_ALARM_CONDITION_INDEX).rb.setChecked(true);
-            } else if (isCountdown(c)) {
-                bindNextAlarm(getTimeUntilNextAlarmCondition());
-                bind(c, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
-                        COUNTDOWN_CONDITION_INDEX);
-                getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
-            } else {
-                Slog.wtf(TAG, "Invalid manual condition: " + c);
-            }
-        }
-        mZenConditions.setVisibility(mSessionZen != Global.ZEN_MODE_OFF ? View.VISIBLE : View.GONE);
-    }
-
-    private void bindGenericCountdown() {
-        mBucketIndex = DEFAULT_BUCKET_INDEX;
-        Condition countdown = ZenModeConfig.toTimeCondition(mContext,
-                MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
-        // don't change the hour condition while the user is viewing the panel
-        if (!mAttached || getConditionTagAt(COUNTDOWN_CONDITION_INDEX).condition == null) {
-            bind(countdown, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
-                    COUNTDOWN_CONDITION_INDEX);
-        }
-    }
-
-    private void bindNextAlarm(Condition c) {
-        View alarmContent = mZenRadioGroupContent.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX);
-        ConditionTag tag = (ConditionTag) alarmContent.getTag();
-        // Don't change the alarm condition while the user is viewing the panel
-        if (c != null && (!mAttached || tag == null || tag.condition == null)) {
-            bind(c, alarmContent, COUNTDOWN_ALARM_CONDITION_INDEX);
-        }
-
-        tag = (ConditionTag) alarmContent.getTag();
-        boolean showAlarm = tag != null && tag.condition != null;
-        mZenRadioGroup.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility(
-                showAlarm ? View.VISIBLE : View.INVISIBLE);
-        alarmContent.setVisibility(showAlarm ? View.VISIBLE : View.INVISIBLE);
-    }
-
-    private Condition forever() {
-        return new Condition(mForeverId, foreverSummary(mContext), "", "", 0 /*icon*/,
-                Condition.STATE_TRUE, 0 /*flags*/);
-    }
-
-    private static String foreverSummary(Context context) {
-        return context.getString(com.android.internal.R.string.zen_mode_forever);
-    }
-
-    // Returns a time condition if the next alarm is within the next week.
-    private Condition getTimeUntilNextAlarmCondition() {
-        GregorianCalendar weekRange = new GregorianCalendar();
-        setToMidnight(weekRange);
-        weekRange.add(Calendar.DATE, 6);
-        final long nextAlarmMs = mController.getNextAlarm();
-        if (nextAlarmMs > 0) {
-            GregorianCalendar nextAlarm = new GregorianCalendar();
-            nextAlarm.setTimeInMillis(nextAlarmMs);
-            setToMidnight(nextAlarm);
-
-            if (weekRange.compareTo(nextAlarm) >= 0) {
-                return ZenModeConfig.toNextAlarmCondition(mContext, nextAlarmMs,
-                        ActivityManager.getCurrentUser());
-            }
-        }
-        return null;
-    }
-
-    private void setToMidnight(Calendar calendar) {
-        calendar.set(Calendar.HOUR_OF_DAY, 0);
-        calendar.set(Calendar.MINUTE, 0);
-        calendar.set(Calendar.SECOND, 0);
-        calendar.set(Calendar.MILLISECOND, 0);
-    }
-
-    @VisibleForTesting
-    ConditionTag getConditionTagAt(int index) {
-        return (ConditionTag) mZenRadioGroupContent.getChildAt(index).getTag();
-    }
-
-    @VisibleForTesting
-    int getVisibleConditions() {
-        int rt = 0;
-        final int N = mZenRadioGroupContent.getChildCount();
-        for (int i = 0; i < N; i++) {
-            rt += mZenRadioGroupContent.getChildAt(i).getVisibility() == VISIBLE ? 1 : 0;
-        }
-        return rt;
-    }
-
-    private void hideAllConditions() {
-        final int N = mZenRadioGroupContent.getChildCount();
-        for (int i = 0; i < N; i++) {
-            mZenRadioGroupContent.getChildAt(i).setVisibility(GONE);
-        }
-    }
-
-    private static boolean isAlarm(Condition c) {
-        return c != null && ZenModeConfig.isValidCountdownToAlarmConditionId(c.id);
-    }
-
-    private static boolean isCountdown(Condition c) {
-        return c != null && ZenModeConfig.isValidCountdownConditionId(c.id);
-    }
-
-    private boolean isForever(Condition c) {
-        return c != null && mForeverId.equals(c.id);
-    }
-
-    private void bind(final Condition condition, final View row, final int rowId) {
-        if (condition == null) throw new IllegalArgumentException("condition must not be null");
-        final boolean enabled = condition.state == Condition.STATE_TRUE;
-        final ConditionTag tag =
-                row.getTag() != null ? (ConditionTag) row.getTag() : new ConditionTag();
-        row.setTag(tag);
-        final boolean first = tag.rb == null;
-        if (tag.rb == null) {
-            tag.rb = (RadioButton) mZenRadioGroup.getChildAt(rowId);
-        }
-        tag.condition = condition;
-        final Uri conditionId = getConditionId(tag.condition);
-        if (DEBUG) Log.d(mTag, "bind i=" + mZenRadioGroupContent.indexOfChild(row) + " first="
-                + first + " condition=" + conditionId);
-        tag.rb.setEnabled(enabled);
-        tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
-            @Override
-            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                if (mExpanded && isChecked) {
-                    tag.rb.setChecked(true);
-                    if (DEBUG) Log.d(mTag, "onCheckedChanged " + conditionId);
-                    MetricsLogger.action(mContext, MetricsEvent.QS_DND_CONDITION_SELECT);
-                    select(tag.condition);
-                    announceConditionSelection(tag);
-                }
-            }
-        });
-
-        if (tag.lines == null) {
-            tag.lines = row.findViewById(android.R.id.content);
-        }
-        if (tag.line1 == null) {
-            tag.line1 = (TextView) row.findViewById(android.R.id.text1);
-            mConfigurableTexts.add(tag.line1);
-        }
-        if (tag.line2 == null) {
-            tag.line2 = (TextView) row.findViewById(android.R.id.text2);
-            mConfigurableTexts.add(tag.line2);
-        }
-        final String line1 = !TextUtils.isEmpty(condition.line1) ? condition.line1
-                : condition.summary;
-        final String line2 = condition.line2;
-        tag.line1.setText(line1);
-        if (TextUtils.isEmpty(line2)) {
-            tag.line2.setVisibility(GONE);
-        } else {
-            tag.line2.setVisibility(VISIBLE);
-            tag.line2.setText(line2);
-        }
-        tag.lines.setEnabled(enabled);
-        tag.lines.setAlpha(enabled ? 1 : .4f);
-
-        final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
-        button1.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                onClickTimeButton(row, tag, false /*down*/, rowId);
-            }
-        });
-
-        final ImageView button2 = (ImageView) row.findViewById(android.R.id.button2);
-        button2.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                onClickTimeButton(row, tag, true /*up*/, rowId);
-            }
-        });
-        tag.lines.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                tag.rb.setChecked(true);
-            }
-        });
-
-        final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
-        if (rowId != COUNTDOWN_ALARM_CONDITION_INDEX && time > 0) {
-            button1.setVisibility(VISIBLE);
-            button2.setVisibility(VISIBLE);
-            if (mBucketIndex > -1) {
-                button1.setEnabled(mBucketIndex > 0);
-                button2.setEnabled(mBucketIndex < MINUTE_BUCKETS.length - 1);
-            } else {
-                final long span = time - System.currentTimeMillis();
-                button1.setEnabled(span > MIN_BUCKET_MINUTES * MINUTES_MS);
-                final Condition maxCondition = ZenModeConfig.toTimeCondition(mContext,
-                        MAX_BUCKET_MINUTES, ActivityManager.getCurrentUser());
-                button2.setEnabled(!Objects.equals(condition.summary, maxCondition.summary));
-            }
-
-            button1.setAlpha(button1.isEnabled() ? 1f : .5f);
-            button2.setAlpha(button2.isEnabled() ? 1f : .5f);
-        } else {
-            button1.setVisibility(GONE);
-            button2.setVisibility(GONE);
-        }
-        // wire up interaction callbacks for newly-added condition rows
-        if (first) {
-            Interaction.register(tag.rb, mInteractionCallback);
-            Interaction.register(tag.lines, mInteractionCallback);
-            Interaction.register(button1, mInteractionCallback);
-            Interaction.register(button2, mInteractionCallback);
-        }
-        row.setVisibility(VISIBLE);
-    }
-
-    private void announceConditionSelection(ConditionTag tag) {
-        final int zen = getSelectedZen(Global.ZEN_MODE_OFF);
-        String modeText;
-        switch(zen) {
-            case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
-                modeText = mContext.getString(R.string.interruption_level_priority);
-                break;
-            case Global.ZEN_MODE_NO_INTERRUPTIONS:
-                modeText = mContext.getString(R.string.interruption_level_none);
-                break;
-            case Global.ZEN_MODE_ALARMS:
-                modeText = mContext.getString(R.string.interruption_level_alarms);
-                break;
-            default:
-                return;
-        }
-        announceForAccessibility(mContext.getString(R.string.zen_mode_and_condition, modeText,
-                tag.line1.getText()));
-    }
-
-    private void onClickTimeButton(View row, ConditionTag tag, boolean up, int rowId) {
-        MetricsLogger.action(mContext, MetricsEvent.QS_DND_TIME, up);
-        Condition newCondition = null;
-        final int N = MINUTE_BUCKETS.length;
-        if (mBucketIndex == -1) {
-            // not on a known index, search for the next or prev bucket by time
-            final Uri conditionId = getConditionId(tag.condition);
-            final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
-            final long now = System.currentTimeMillis();
-            for (int i = 0; i < N; i++) {
-                int j = up ? i : N - 1 - i;
-                final int bucketMinutes = MINUTE_BUCKETS[j];
-                final long bucketTime = now + bucketMinutes * MINUTES_MS;
-                if (up && bucketTime > time || !up && bucketTime < time) {
-                    mBucketIndex = j;
-                    newCondition = ZenModeConfig.toTimeCondition(mContext,
-                            bucketTime, bucketMinutes, ActivityManager.getCurrentUser(),
-                            false /*shortVersion*/);
-                    break;
-                }
-            }
-            if (newCondition == null) {
-                mBucketIndex = DEFAULT_BUCKET_INDEX;
-                newCondition = ZenModeConfig.toTimeCondition(mContext,
-                        MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
-            }
-        } else {
-            // on a known index, simply increment or decrement
-            mBucketIndex = Math.max(0, Math.min(N - 1, mBucketIndex + (up ? 1 : -1)));
-            newCondition = ZenModeConfig.toTimeCondition(mContext,
-                    MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
-        }
-        bind(newCondition, row, rowId);
-        tag.rb.setChecked(true);
-        select(newCondition);
-        announceConditionSelection(tag);
-    }
-
-    private void select(final Condition condition) {
-        if (DEBUG) Log.d(mTag, "select " + condition);
-        if (mSessionZen == -1 || mSessionZen == Global.ZEN_MODE_OFF) {
-            if (DEBUG) Log.d(mTag, "Ignoring condition selection outside of manual zen");
-            return;
-        }
-        final Uri realConditionId = getRealConditionId(condition);
-        if (mController != null) {
-            AsyncTask.execute(new Runnable() {
-                @Override
-                public void run() {
-                    mController.setZen(mSessionZen, realConditionId, TAG + ".selectCondition");
-                }
-            });
-        }
-        setExitCondition(condition);
-        if (realConditionId == null) {
-            mPrefs.setMinuteIndex(-1);
-        } else if ((isAlarm(condition) || isCountdown(condition)) && mBucketIndex != -1) {
-            mPrefs.setMinuteIndex(mBucketIndex);
-        }
-        setSessionExitCondition(copy(condition));
-    }
-
-    private void fireInteraction() {
-        if (mCallback != null) {
-            mCallback.onInteraction();
-        }
-    }
-
-    private void fireExpanded() {
-        if (mCallback != null) {
-            mCallback.onExpanded(mExpanded);
-        }
-    }
-
-    private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
-        @Override
-        public void onManualRuleChanged(ZenRule rule) {
-            mHandler.obtainMessage(H.MANUAL_RULE_CHANGED, rule).sendToTarget();
-        }
-    };
-
-    private final class H extends Handler {
-        private static final int MANUAL_RULE_CHANGED = 2;
-        private static final int UPDATE_WIDGETS = 3;
-
-        private H() {
-            super(Looper.getMainLooper());
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MANUAL_RULE_CHANGED: handleUpdateManualRule((ZenRule) msg.obj); break;
-                case UPDATE_WIDGETS: updateWidgets(); break;
-            }
-        }
-    }
-
-    public interface Callback {
-        void onPrioritySettings();
-        void onInteraction();
-        void onExpanded(boolean expanded);
-    }
-
-    // used as the view tag on condition rows
-    @VisibleForTesting
-    static class ConditionTag {
-        RadioButton rb;
-        View lines;
-        TextView line1;
-        TextView line2;
-        Condition condition;
-    }
-
-    private final class ZenPrefs implements OnSharedPreferenceChangeListener {
-        private final int mNoneDangerousThreshold;
-
-        private int mMinuteIndex;
-        private int mNoneSelected;
-        private boolean mConfirmedPriorityIntroduction;
-        private boolean mConfirmedSilenceIntroduction;
-        private boolean mConfirmedAlarmIntroduction;
-
-        private ZenPrefs() {
-            mNoneDangerousThreshold = mContext.getResources()
-                    .getInteger(R.integer.zen_mode_alarm_warning_threshold);
-            Prefs.registerListener(mContext, this);
-            updateMinuteIndex();
-            updateNoneSelected();
-            updateConfirmedPriorityIntroduction();
-            updateConfirmedSilenceIntroduction();
-            updateConfirmedAlarmIntroduction();
-        }
-
-        public void trackNoneSelected() {
-            mNoneSelected = clampNoneSelected(mNoneSelected + 1);
-            if (DEBUG) Log.d(mTag, "Setting none selected: " + mNoneSelected + " threshold="
-                    + mNoneDangerousThreshold);
-            Prefs.putInt(mContext, Prefs.Key.DND_NONE_SELECTED, mNoneSelected);
-        }
-
-        public int getMinuteIndex() {
-            return mMinuteIndex;
-        }
-
-        public void setMinuteIndex(int minuteIndex) {
-            minuteIndex = clampIndex(minuteIndex);
-            if (minuteIndex == mMinuteIndex) return;
-            mMinuteIndex = clampIndex(minuteIndex);
-            if (DEBUG) Log.d(mTag, "Setting favorite minute index: " + mMinuteIndex);
-            Prefs.putInt(mContext, Prefs.Key.DND_FAVORITE_BUCKET_INDEX, mMinuteIndex);
-        }
-
-        @Override
-        public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
-            updateMinuteIndex();
-            updateNoneSelected();
-            updateConfirmedPriorityIntroduction();
-            updateConfirmedSilenceIntroduction();
-            updateConfirmedAlarmIntroduction();
-        }
-
-        private void updateMinuteIndex() {
-            mMinuteIndex = clampIndex(Prefs.getInt(mContext,
-                    Prefs.Key.DND_FAVORITE_BUCKET_INDEX, DEFAULT_BUCKET_INDEX));
-            if (DEBUG) Log.d(mTag, "Favorite minute index: " + mMinuteIndex);
-        }
-
-        private int clampIndex(int index) {
-            return MathUtils.constrain(index, -1, MINUTE_BUCKETS.length - 1);
-        }
-
-        private void updateNoneSelected() {
-            mNoneSelected = clampNoneSelected(Prefs.getInt(mContext,
-                    Prefs.Key.DND_NONE_SELECTED, 0));
-            if (DEBUG) Log.d(mTag, "None selected: " + mNoneSelected);
-        }
-
-        private int clampNoneSelected(int noneSelected) {
-            return MathUtils.constrain(noneSelected, 0, Integer.MAX_VALUE);
-        }
-
-        private void updateConfirmedPriorityIntroduction() {
-            final boolean confirmed =  Prefs.getBoolean(mContext,
-                    Prefs.Key.DND_CONFIRMED_PRIORITY_INTRODUCTION, false);
-            if (confirmed == mConfirmedPriorityIntroduction) return;
-            mConfirmedPriorityIntroduction = confirmed;
-            if (DEBUG) Log.d(mTag, "Confirmed priority introduction: "
-                    + mConfirmedPriorityIntroduction);
-        }
-
-        private void updateConfirmedSilenceIntroduction() {
-            final boolean confirmed =  Prefs.getBoolean(mContext,
-                    Prefs.Key.DND_CONFIRMED_SILENCE_INTRODUCTION, false);
-            if (confirmed == mConfirmedSilenceIntroduction) return;
-            mConfirmedSilenceIntroduction = confirmed;
-            if (DEBUG) Log.d(mTag, "Confirmed silence introduction: "
-                    + mConfirmedSilenceIntroduction);
-        }
-
-        private void updateConfirmedAlarmIntroduction() {
-            final boolean confirmed =  Prefs.getBoolean(mContext,
-                    Prefs.Key.DND_CONFIRMED_ALARM_INTRODUCTION, false);
-            if (confirmed == mConfirmedAlarmIntroduction) return;
-            mConfirmedAlarmIntroduction = confirmed;
-            if (DEBUG) Log.d(mTag, "Confirmed alarm introduction: "
-                    + mConfirmedAlarmIntroduction);
-        }
-    }
-
-    protected final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() {
-        @Override
-        public void onSelected(final Object value, boolean fromClick) {
-            if (value != null && mZenButtons.isShown() && isAttachedToWindow()) {
-                final int zen = (Integer) value;
-                if (fromClick) {
-                    MetricsLogger.action(mContext, MetricsEvent.QS_DND_ZEN_SELECT, zen);
-                }
-                if (DEBUG) Log.d(mTag, "mZenButtonsCallback selected=" + zen);
-                final Uri realConditionId = getRealConditionId(mSessionExitCondition);
-                AsyncTask.execute(new Runnable() {
-                    @Override
-                    public void run() {
-                        mController.setZen(zen, realConditionId, TAG + ".selectZen");
-                        if (zen != Global.ZEN_MODE_OFF) {
-                            Prefs.putInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, zen);
-                        }
-                    }
-                });
-            }
-        }
-
-        @Override
-        public void onInteraction() {
-            fireInteraction();
-        }
-    };
-
-    private final Interaction.Callback mInteractionCallback = new Interaction.Callback() {
-        @Override
-        public void onInteraction() {
-            fireInteraction();
-        }
-    };
-
-    private final class TransitionHelper implements TransitionListener, Runnable {
-        private final ArraySet<View> mTransitioningViews = new ArraySet<View>();
-
-        private boolean mTransitioning;
-        private boolean mPendingUpdateWidgets;
-
-        public void clear() {
-            mTransitioningViews.clear();
-            mPendingUpdateWidgets = false;
-        }
-
-        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            pw.println("  TransitionHelper state:");
-            pw.print("    mPendingUpdateWidgets="); pw.println(mPendingUpdateWidgets);
-            pw.print("    mTransitioning="); pw.println(mTransitioning);
-            pw.print("    mTransitioningViews="); pw.println(mTransitioningViews);
-        }
-
-        public void pendingUpdateWidgets() {
-            mPendingUpdateWidgets = true;
-        }
-
-        public boolean isTransitioning() {
-            return !mTransitioningViews.isEmpty();
-        }
-
-        @Override
-        public void startTransition(LayoutTransition transition,
-                ViewGroup container, View view, int transitionType) {
-            mTransitioningViews.add(view);
-            updateTransitioning();
-        }
-
-        @Override
-        public void endTransition(LayoutTransition transition,
-                ViewGroup container, View view, int transitionType) {
-            mTransitioningViews.remove(view);
-            updateTransitioning();
-        }
-
-        @Override
-        public void run() {
-            if (DEBUG) Log.d(mTag, "TransitionHelper run"
-                    + " mPendingUpdateWidgets=" + mPendingUpdateWidgets);
-            if (mPendingUpdateWidgets) {
-                updateWidgets();
-            }
-            mPendingUpdateWidgets = false;
-        }
-
-        private void updateTransitioning() {
-            final boolean transitioning = isTransitioning();
-            if (mTransitioning == transitioning) return;
-            mTransitioning = transitioning;
-            if (DEBUG) Log.d(mTag, "TransitionHelper mTransitioning=" + mTransitioning);
-            if (!mTransitioning) {
-                if (mPendingUpdateWidgets) {
-                    mHandler.post(this);
-                } else {
-                    mPendingUpdateWidgets = false;
-                }
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
index 9f8f6c1..06082b6 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
@@ -32,6 +32,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.tuner.TunerService;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -66,6 +67,11 @@
         mController.setupUri(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
     }
 
+    @After
+    public void tearDown() {
+        mController.onViewDetached();
+    }
+
     @Test
     public void refresh_replacesSliceContentAndNotifiesListener() {
         mController.refresh();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java
index 2d52c42..c5b1a1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java
@@ -16,7 +16,8 @@
 
 package com.android.systemui.communal;
 
-import static org.mockito.ArgumentMatchers.anyBoolean;
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
@@ -59,7 +60,8 @@
 
         final Condition.Callback callback = mock(Condition.Callback.class);
         mCondition.addCallback(callback);
-        verify(callback).onConditionChanged(mCondition, true);
+        verify(callback).onConditionChanged(mCondition);
+        assertThat(mCondition.isConditionMet()).isTrue();
     }
 
     @Test
@@ -68,7 +70,7 @@
 
         final Condition.Callback callback = mock(Condition.Callback.class);
         mCondition.addCallback(callback);
-        verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+        verify(callback, never()).onConditionChanged(eq(mCondition));
     }
 
     @Test
@@ -80,7 +82,8 @@
         clearInvocations(callback);
 
         updateCommunalSetting(true);
-        verify(callback).onConditionChanged(mCondition, true);
+        verify(callback).onConditionChanged(mCondition);
+        assertThat(mCondition.isConditionMet()).isTrue();
     }
 
     @Test
@@ -92,7 +95,8 @@
         clearInvocations(callback);
 
         updateCommunalSetting(false);
-        verify(callback).onConditionChanged(mCondition, false);
+        verify(callback).onConditionChanged(mCondition);
+        assertThat(mCondition.isConditionMet()).isFalse();
     }
 
     @Test
@@ -104,7 +108,8 @@
         clearInvocations(callback);
 
         updateCommunalSetting(true);
-        verify(callback, never()).onConditionChanged(mCondition, true);
+        verify(callback, never()).onConditionChanged(mCondition);
+        assertThat(mCondition.isConditionMet()).isTrue();
     }
 
     private void updateCommunalSetting(boolean value) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java
index 61a5126..500205c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java
@@ -16,7 +16,8 @@
 
 package com.android.systemui.communal;
 
-import static org.mockito.ArgumentMatchers.anyBoolean;
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.clearInvocations;
@@ -49,6 +50,7 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
@@ -89,7 +91,8 @@
         networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
 
         // Verifies that the callback is triggered.
-        verify(callback).onConditionChanged(mCondition, true);
+        verify(callback).onConditionChanged(mCondition);
+        assertThat(mCondition.isConditionMet()).isTrue();
     }
 
     @Test
@@ -110,7 +113,7 @@
         networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi2));
 
         // Verifies that the callback is not triggered.
-        verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+        verify(callback, never()).onConditionChanged(eq(mCondition));
     }
 
     @Test
@@ -126,11 +129,13 @@
         networkCallback.onAvailable(network);
         networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
 
+        Mockito.clearInvocations(callback);
         // Connected to non-trusted Wi-Fi network.
         networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities("random-wifi"));
 
         // Verifies that the callback is triggered.
-        verify(callback).onConditionChanged(mCondition, false);
+        verify(callback).onConditionChanged(mCondition);
+        assertThat(mCondition.isConditionMet()).isFalse();
     }
 
     @Test
@@ -151,7 +156,8 @@
         networkCallback.onLost(network);
 
         // Verifies that the callback is triggered.
-        verify(callback).onConditionChanged(mCondition, false);
+        verify(callback).onConditionChanged(mCondition);
+        assertThat(mCondition.isConditionMet()).isFalse();
     }
 
     // Captures and returns the network callback, assuming it is registered with the connectivity
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index cdffaec..7e1edd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -35,6 +35,7 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Matchers.anyObject;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.verify;
@@ -306,4 +307,11 @@
         // THEN the display screen state will change
         assertEquals(Display.STATE_DOZE_SUSPEND, mServiceFake.screenState);
     }
+
+    @Test
+    public void authCallbackRemovedOnDestroy() {
+        mScreen.destroy();
+
+        verify(mAuthController).removeCallback(anyObject());
+    }
 }
\ No newline at end of file
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 609291a..708fc91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -74,6 +74,7 @@
 private const val SESSION_ARTIST = "SESSION_ARTIST"
 private const val SESSION_TITLE = "SESSION_TITLE"
 private const val USER_ID = 0
+private const val DISABLED_DEVICE_NAME = "DISABLED_DEVICE_NAME"
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -131,7 +132,7 @@
 
     private lateinit var session: MediaSession
     private val device = MediaDeviceData(true, null, DEVICE_NAME)
-    private val disabledDevice = MediaDeviceData(false, null, "Disabled Device")
+    private val disabledDevice = MediaDeviceData(false, null, DISABLED_DEVICE_NAME)
     private lateinit var mediaData: MediaData
     private val clock = FakeSystemClock()
 
@@ -396,13 +397,12 @@
     @Test
     fun bindDisabledDevice() {
         seamless.id = 1
-        val fallbackString = context.getString(R.string.media_seamless_other_device)
         player.attachPlayer(holder, MediaViewController.TYPE.PLAYER)
         val state = mediaData.copy(device = disabledDevice)
         player.bindPlayer(state, PACKAGE)
         assertThat(seamless.isEnabled()).isFalse()
-        assertThat(seamlessText.getText()).isEqualTo(fallbackString)
-        assertThat(seamless.contentDescription).isEqualTo(fallbackString)
+        assertThat(seamlessText.getText()).isEqualTo(DISABLED_DEVICE_NAME)
+        assertThat(seamless.contentDescription).isEqualTo(DISABLED_DEVICE_NAME)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 649ee87..f4fa921 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -1,6 +1,5 @@
 package com.android.systemui.media
 
-import android.app.Notification
 import android.app.Notification.MediaStyle
 import android.app.PendingIntent
 import android.app.smartspace.SmartspaceAction
@@ -240,15 +239,14 @@
 
     @Test
     fun testOnNotificationAdded_isRcn_markedRemote() {
-        val bundle = Bundle().apply {
-            putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, "Remote Cast Notification")
-        }
         val rcn = SbnBuilder().run {
             setPkg("com.android.systemui") // System package
             modifyNotification(context).also {
                 it.setSmallIcon(android.R.drawable.ic_media_pause)
-                it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
-                it.addExtras(bundle)
+                it.setStyle(MediaStyle().apply {
+                    setMediaSession(session.sessionToken)
+                    setRemotePlaybackInfo("Remote device", 0, null)
+                })
             }
             build()
         }
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 64b5b86..d912a89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -437,6 +437,24 @@
         verify(mr2, never()).getRoutingSessionForMediaController(eq(controller))
     }
 
+    @Test
+    fun testRemotePlaybackDeviceOverride() {
+        whenever(route.name).thenReturn(DEVICE_NAME)
+        val deviceData = MediaDeviceData(false, null, REMOTE_DEVICE_NAME, null)
+        val mediaDataWithDevice = mediaData.copy(device = deviceData)
+
+        // GIVEN media data that already has a device set
+        manager.onMediaDataLoaded(KEY, null, mediaDataWithDevice)
+        fakeBgExecutor.runAllReady()
+        fakeFgExecutor.runAllReady()
+
+        // THEN we keep the device info, and don't register a listener
+        val data = captureDeviceData(KEY)
+        assertThat(data.enabled).isFalse()
+        assertThat(data.name).isEqualTo(REMOTE_DEVICE_NAME)
+        verify(lmm, never()).registerCallback(any())
+    }
+
     fun captureCallback(): LocalMediaManager.DeviceCallback {
         val captor = ArgumentCaptor.forClass(LocalMediaManager.DeviceCallback::class.java)
         verify(lmm).registerCallback(captor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 0576987..bdc3117 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -201,7 +201,7 @@
 
         assertThat(devices.containsAll(mMediaDevices)).isTrue();
         assertThat(devices.size()).isEqualTo(mMediaDevices.size());
-        verify(mCb).onRouteChanged();
+        verify(mCb).onDeviceListChanged();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
deleted file mode 100644
index 84776c7..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_MORE_SETTINGS;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-import android.testing.ViewUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.qs.DetailAdapter;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class QSDetailTest extends SysuiTestCase {
-
-    private MetricsLogger mMetricsLogger;
-    private QSDetail mQsDetail;
-    private QSPanelController mQsPanelController;
-    private QuickStatusBarHeader mQuickHeader;
-    private ActivityStarter mActivityStarter;
-    private DetailAdapter mMockDetailAdapter;
-    private TestableLooper mTestableLooper;
-    private UiEventLoggerFake mUiEventLogger;
-    private FrameLayout mParent;
-
-    @Before
-    public void setup() throws Exception {
-        mTestableLooper = TestableLooper.get(this);
-        mUiEventLogger = QSEvents.INSTANCE.setLoggerForTesting();
-
-        mParent = new FrameLayout(mContext);
-        mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
-        mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
-        LayoutInflater.from(mContext).inflate(R.layout.qs_detail, mParent);
-        mQsDetail = (QSDetail) mParent.getChildAt(0);
-
-        mQsPanelController = mock(QSPanelController.class);
-        mQuickHeader = mock(QuickStatusBarHeader.class);
-        mQsDetail.setQsPanel(mQsPanelController, mQuickHeader, mock(QSFooter.class),
-                mock(FalsingManager.class));
-        mQsDetail.mClipper = mock(QSDetailClipper.class);
-
-        mMockDetailAdapter = mock(DetailAdapter.class);
-        when(mMockDetailAdapter.createDetailView(any(), any(), any()))
-                .thenReturn(new View(mContext));
-
-        // Only detail in use is the user detail
-        when(mMockDetailAdapter.openDetailEvent())
-                .thenReturn(QSUserSwitcherEvent.QS_USER_DETAIL_OPEN);
-        when(mMockDetailAdapter.closeDetailEvent())
-                .thenReturn(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE);
-        when(mMockDetailAdapter.moreSettingsEvent())
-                .thenReturn(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS);
-        ViewUtils.attachView(mParent);
-    }
-
-    @After
-    public void tearDown() {
-        QSEvents.INSTANCE.resetLogger();
-        mTestableLooper.processAllMessages();
-        ViewUtils.detachView(mParent);
-    }
-
-    @Test
-    public void testShowDetail_Metrics() {
-        mTestableLooper.processAllMessages();
-
-        mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
-        verify(mMetricsLogger).visible(eq(mMockDetailAdapter.getMetricsCategory()));
-        assertEquals(1, mUiEventLogger.numLogs());
-        assertEquals(QSUserSwitcherEvent.QS_USER_DETAIL_OPEN.getId(), mUiEventLogger.eventId(0));
-        mUiEventLogger.getLogs().clear();
-
-        mQsDetail.handleShowingDetail(null, 0, 0, false);
-        verify(mMetricsLogger).hidden(eq(mMockDetailAdapter.getMetricsCategory()));
-
-        assertEquals(1, mUiEventLogger.numLogs());
-        assertEquals(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE.getId(), mUiEventLogger.eventId(0));
-    }
-
-    @Test
-    public void testShowDetail_ShouldAnimate() {
-        mTestableLooper.processAllMessages();
-
-        when(mMockDetailAdapter.shouldAnimate()).thenReturn(true);
-        mQsDetail.setFullyExpanded(true);
-
-        mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
-        verify(mQsDetail.mClipper).updateCircularClip(eq(true) /* animate */, anyInt(), anyInt(),
-                eq(true) /* in */, any());
-        clearInvocations(mQsDetail.mClipper);
-
-        mQsDetail.handleShowingDetail(null, 0, 0, false);
-        verify(mQsDetail.mClipper).updateCircularClip(eq(true) /* animate */, anyInt(), anyInt(),
-                eq(false) /* in */, any());
-    }
-
-    @Test
-    public void testShowDetail_ShouldNotAnimate() {
-        mTestableLooper.processAllMessages();
-
-        when(mMockDetailAdapter.shouldAnimate()).thenReturn(false);
-        mQsDetail.setFullyExpanded(true);
-
-        mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
-        verify(mQsDetail.mClipper).updateCircularClip(eq(false) /* animate */, anyInt(), anyInt(),
-                eq(true) /* in */, any());
-        clearInvocations(mQsDetail.mClipper);
-
-        // Detail adapters should always animate on close. shouldAnimate() should only affect the
-        // open transition
-        mQsDetail.handleShowingDetail(null, 0, 0, false);
-        verify(mQsDetail.mClipper).updateCircularClip(eq(true) /* animate */, anyInt(), anyInt(),
-                eq(false) /* in */, any());
-    }
-
-    @Test
-    public void testDoneButton_CloseDetailPanel() {
-        mTestableLooper.processAllMessages();
-
-        when(mMockDetailAdapter.onDoneButtonClicked()).thenReturn(false);
-
-        mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
-        mQsDetail.requireViewById(android.R.id.button1).performClick();
-        verify(mQsPanelController).closeDetail();
-    }
-
-    @Test
-    public void testDoneButton_KeepDetailPanelOpen() {
-        mTestableLooper.processAllMessages();
-
-        when(mMockDetailAdapter.onDoneButtonClicked()).thenReturn(true);
-
-        mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
-        mQsDetail.requireViewById(android.R.id.button1).performClick();
-        verify(mQsPanelController, never()).closeDetail();
-    }
-
-    @Test
-    public void testMoreSettingsButton() {
-        mTestableLooper.processAllMessages();
-
-        mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
-        mUiEventLogger.getLogs().clear();
-        mQsDetail.requireViewById(android.R.id.button2).performClick();
-
-        int metricsCategory = mMockDetailAdapter.getMetricsCategory();
-        verify(mMetricsLogger).action(eq(ACTION_QS_MORE_SETTINGS), eq(metricsCategory));
-        assertEquals(1, mUiEventLogger.numLogs());
-        assertEquals(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS.getId(), mUiEventLogger.eventId(0));
-
-        verify(mActivityStarter).postStartActivityDismissingKeyguard(any(), anyInt());
-    }
-
-    @Test
-    public void testNullAdapterClick() {
-        DetailAdapter mock = mock(DetailAdapter.class);
-        when(mock.moreSettingsEvent()).thenReturn(DetailAdapter.INVALID);
-        mQsDetail.setupDetailFooter(mock);
-        mQsDetail.requireViewById(android.R.id.button2).performClick();
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 3266d6a..4ab3926 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -146,7 +146,6 @@
                 mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class),
                 mock(SecureSettings.class), mock(CustomTileStatePersister.class),
                 mTileServiceRequestControllerBuilder, mock(TileLifecycleManager.Factory.class));
-        qs.setHost(host);
 
         qs.setListening(true);
         processAllMessages();
@@ -186,7 +185,6 @@
                 mock(QSTileHost.class),
                 mock(StatusBarStateController.class),
                 commandQueue,
-                new QSDetailDisplayer(),
                 mQSMediaHost,
                 mQQSMediaHost,
                 mBypassController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
deleted file mode 100644
index b2ca62f..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.testing.UiEventLoggerFake;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.classifier.FalsingManagerFake;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.media.MediaHost;
-import com.android.systemui.plugins.qs.QSTileView;
-import com.android.systemui.qs.customize.QSCustomizerController;
-import com.android.systemui.qs.logging.QSLogger;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.settings.brightness.BrightnessController;
-import com.android.systemui.settings.brightness.BrightnessSliderController;
-import com.android.systemui.settings.brightness.ToggleSlider;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.animation.DisappearParameters;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Collections;
-
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class QSPanelControllerTest extends SysuiTestCase {
-
-    @Mock
-    private QSPanel mQSPanel;
-    @Mock
-    private QSTileHost mQSTileHost;
-    @Mock
-    private QSCustomizerController mQSCustomizerController;
-    @Mock
-    private QSTileRevealController.Factory mQSTileRevealControllerFactory;
-    @Mock
-    private QSTileRevealController mQSTileRevealController;
-    @Mock
-    private MediaHost mMediaHost;
-    @Mock
-    private MetricsLogger mMetricsLogger;
-    private UiEventLogger mUiEventLogger = new UiEventLoggerFake();
-    private DumpManager mDumpManager = new DumpManager();
-    @Mock
-    private TunerService mTunerService;
-    @Mock
-    private QSFgsManagerFooter mQSFgsManagerFooter;
-    @Mock
-    private QSSecurityFooter mQSSecurityFooter;
-    @Mock
-    private QSLogger mQSLogger;
-    @Mock
-    private BrightnessController.Factory mBrightnessControllerFactory;
-    @Mock
-    private BrightnessController mBrightnessController;
-    @Mock
-    private BrightnessSliderController.Factory mToggleSliderViewControllerFactory;
-    @Mock
-    private BrightnessSliderController mBrightnessSliderController;
-    @Mock
-    QSTileImpl mQSTile;
-    @Mock
-    QSTileView mQSTileView;
-    @Mock
-    PagedTileLayout mPagedTileLayout;
-    @Mock
-    CommandQueue mCommandQueue;
-    FalsingManagerFake mFalsingManager = new FalsingManagerFake();
-    @Mock
-    Resources mResources;
-    @Mock
-    Configuration mConfiguration;
-    @Mock
-    FeatureFlags mFeatureFlags;
-
-    private QSPanelController mController;
-
-    @Before
-    public void setup() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        when(mQSPanel.isAttachedToWindow()).thenReturn(true);
-        when(mQSPanel.getDumpableTag()).thenReturn("QSPanel");
-        when(mQSPanel.getOrCreateTileLayout()).thenReturn(mPagedTileLayout);
-        when(mQSPanel.getTileLayout()).thenReturn(mPagedTileLayout);
-        when(mQSPanel.getResources()).thenReturn(mResources);
-        when(mResources.getConfiguration()).thenReturn(mConfiguration);
-        when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
-        when(mQSTileHost.createTileView(any(), eq(mQSTile), anyBoolean())).thenReturn(mQSTileView);
-        when(mToggleSliderViewControllerFactory.create(any(), any()))
-                .thenReturn(mBrightnessSliderController);
-        when(mBrightnessControllerFactory.create(any(ToggleSlider.class)))
-                .thenReturn(mBrightnessController);
-        when(mQSTileRevealControllerFactory.create(any(), any()))
-                .thenReturn(mQSTileRevealController);
-        when(mMediaHost.getDisappearParameters()).thenReturn(new DisappearParameters());
-
-        mController = new QSPanelController(mQSPanel, mQSFgsManagerFooter, mQSSecurityFooter,
-                mTunerService, mQSTileHost, mQSCustomizerController, true, mMediaHost,
-                mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
-                mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
-                mFalsingManager, mCommandQueue, mFeatureFlags
-        );
-
-        mController.init();
-    }
-
-    @Test
-    public void testOpenDetailsWithNonExistingTile_NoException() {
-        mController.openDetails("none");
-
-        verify(mQSPanel, never()).openDetails(any());
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index 4ae19332..5213a30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -36,10 +36,7 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
 
@@ -64,9 +61,6 @@
     private lateinit var mParentView: ViewGroup
 
     @Mock
-    private lateinit var mCallback: QSDetail.Callback
-
-    @Mock
     private lateinit var mQSTileView: QSTileView
 
     private lateinit var mFooter: View
@@ -97,29 +91,10 @@
             whenever(mHost.tiles).thenReturn(emptyList())
             whenever(mHost.createTileView(any(), any(), anyBoolean())).thenReturn(mQSTileView)
             mQsPanel.addTile(mDndTileRecord)
-            mQsPanel.setCallback(mCallback)
         }
     }
 
     @Test
-    fun testOpenDetailsWithExistingTile_NoException() {
-        mTestableLooper.runWithLooper {
-            mQsPanel.openDetails(dndTile)
-        }
-
-        verify(mCallback).onShowingDetail(any(), anyInt(), anyInt())
-    }
-
-    @Test
-    fun testOpenDetailsWithNullParameter_NoException() {
-        mTestableLooper.runWithLooper {
-            mQsPanel.openDetails(null)
-        }
-
-        verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt())
-    }
-
-    @Test
     fun testSecurityFooter_appearsOnBottomOnSplitShade() {
         mQsPanel.onConfigurationChanged(getNewOrientationConfig(ORIENTATION_LANDSCAPE))
         mQsPanel.switchSecurityFooter(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 8b7346d..30b464b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -53,7 +53,6 @@
 import com.android.internal.logging.InstanceId;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.QSTileHost;
@@ -420,11 +419,5 @@
 
         @Override
         public void destroy() {}
-
-
-        @Override
-        public DetailAdapter getDetailAdapter() {
-            return null;
-        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
index 88b133e..0f2c2647 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
@@ -48,7 +48,6 @@
 import com.android.systemui.qs.tiles.RotationLockTile
 import com.android.systemui.qs.tiles.ScreenRecordTile
 import com.android.systemui.qs.tiles.UiModeNightTile
-import com.android.systemui.qs.tiles.UserTile
 import com.android.systemui.qs.tiles.WifiTile
 import com.android.systemui.qs.tiles.WorkModeTile
 import com.android.systemui.util.leak.GarbageMonitor
@@ -76,7 +75,6 @@
         "location" to LocationTile::class.java,
         "cast" to CastTile::class.java,
         "hotspot" to HotspotTile::class.java,
-        "user" to UserTile::class.java,
         "battery" to BatterySaverTile::class.java,
         "saver" to DataSaverTile::class.java,
         "night" to NightDisplayTile::class.java,
@@ -115,7 +113,6 @@
     @Mock private lateinit var locationTile: LocationTile
     @Mock private lateinit var castTile: CastTile
     @Mock private lateinit var hotspotTile: HotspotTile
-    @Mock private lateinit var userTile: UserTile
     @Mock private lateinit var batterySaverTile: BatterySaverTile
     @Mock private lateinit var dataSaverTile: DataSaverTile
     @Mock private lateinit var nightDisplayTile: NightDisplayTile
@@ -159,7 +156,6 @@
                 { locationTile },
                 { castTile },
                 { hotspotTile },
-                { userTile },
                 { batterySaverTile },
                 { dataSaverTile },
                 { nightDisplayTile },
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
index b7fdc1a..8695b29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -22,11 +22,13 @@
 import android.testing.AndroidTestingRunner
 import android.view.View
 import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogLaunchAnimator
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.qs.PseudoGridView
+import com.android.systemui.qs.QSUserSwitcherEvent
 import com.android.systemui.qs.tiles.UserDetailView
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.util.mockito.any
@@ -62,6 +64,8 @@
     private lateinit var launchView: View
     @Mock
     private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+    @Mock
+    private lateinit var uiEventLogger: UiEventLogger
     @Captor
     private lateinit var clickCaptor: ArgumentCaptor<DialogInterface.OnClickListener>
 
@@ -79,6 +83,7 @@
                 activityStarter,
                 falsingManager,
                 dialogLaunchAnimator,
+                uiEventLogger,
                 { dialog }
         )
     }
@@ -87,6 +92,7 @@
     fun showDialog_callsDialogShow() {
         controller.showDialog(launchView)
         verify(dialogLaunchAnimator).showFromView(dialog, launchView)
+        verify(uiEventLogger).log(QSUserSwitcherEvent.QS_USER_DETAIL_OPEN)
     }
 
     @Test
@@ -108,10 +114,14 @@
     }
 
     @Test
-    fun doneButtonSetWithNullHandler() {
+    fun doneButtonLogsCorrectly() {
         controller.showDialog(launchView)
 
-        verify(dialog).setPositiveButton(anyInt(), eq(null))
+        verify(dialog).setPositiveButton(anyInt(), capture(clickCaptor))
+
+        clickCaptor.value.onClick(dialog, DialogInterface.BUTTON_NEUTRAL)
+
+        verify(uiEventLogger).log(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE)
     }
 
     @Test
@@ -129,6 +139,7 @@
                         argThat(IntentMatcher(Settings.ACTION_USER_SETTINGS)),
                         eq(0)
                 )
+        verify(uiEventLogger).log(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
index 218e7db..711187b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
@@ -22,7 +22,6 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.OverlayPlugin;
 import com.android.systemui.plugins.annotations.Requires;
-import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.qs.QS.HeightListener;
 import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
@@ -99,7 +98,6 @@
 
     @Requires(target = QS.class, version = QS.VERSION)
     @Requires(target = HeightListener.class, version = HeightListener.VERSION)
-    @Requires(target = DetailAdapter.class, version = DetailAdapter.VERSION)
     public static class QSImpl {
     }
 }
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 36d1629f..f6eff82 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
@@ -110,7 +110,6 @@
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.qrcodescanner.controller.QRCodeScannerController;
-import com.android.systemui.qs.QSDetailDisplayer;
 import com.android.systemui.screenrecord.RecordingController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
@@ -273,8 +272,6 @@
     @Mock
     private IdleHostViewController mIdleHostViewController;
     @Mock
-    private QSDetailDisplayer mQSDetailDisplayer;
-    @Mock
     private KeyguardStatusViewComponent mKeyguardStatusViewComponent;
     @Mock
     private KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
@@ -528,7 +525,6 @@
                 mCommunalViewComponentFactory,
                 mIdleViewComponentFactory,
                 mLockscreenShadeTransitionController,
-                mQSDetailDisplayer,
                 mGroupManager,
                 mNotificationAreaController,
                 mAuthController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
index a57f6a1..4a579cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
@@ -27,26 +27,22 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.CommunalStateController
-import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.keyguard.ScreenLifecycle
 import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.qs.tiles.UserDetailView
 import com.android.systemui.qs.user.UserSwitchDialogController
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.phone.DozeParameters
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger
-import com.android.systemui.statusbar.phone.NotificationPanelViewController
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController
 import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.`when`
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
-import javax.inject.Provider
 
 @SmallTest
 @TestableLooper.RunWithLooper
@@ -77,23 +73,14 @@
     private lateinit var dozeParameters: DozeParameters
 
     @Mock
-    private lateinit var userDetailViewAdapterProvider: Provider<UserDetailView.Adapter>
-
-    @Mock
     private lateinit var screenOffAnimationController: ScreenOffAnimationController
 
     @Mock
-    private lateinit var featureFlags: FeatureFlags
-
-    @Mock
     private lateinit var userSwitchDialogController: UserSwitchDialogController
 
     @Mock
     private lateinit var uiEventLogger: UiEventLogger
 
-    @Mock
-    private lateinit var notificationPanelViewController: NotificationPanelViewController
-
     private lateinit var view: FrameLayout
     private lateinit var testableLooper: TestableLooper
     private lateinit var keyguardQsUserSwitchController: KeyguardQsUserSwitchController
@@ -118,16 +105,12 @@
                 configurationController,
                 statusBarStateController,
                 dozeParameters,
-                userDetailViewAdapterProvider,
                 screenOffAnimationController,
-                featureFlags,
                 userSwitchDialogController,
                 uiEventLogger)
 
         ViewUtils.attachView(view)
         testableLooper.processAllMessages()
-        keyguardQsUserSwitchController
-                .setNotificationPanelViewController(notificationPanelViewController)
         `when`(userSwitcherController.keyguardStateController).thenReturn(keyguardStateController)
         `when`(userSwitcherController.keyguardStateController.isShowing).thenReturn(true)
         keyguardQsUserSwitchController.init()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
index 9dabf69..c344aea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -54,8 +54,8 @@
 import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.time.FakeSystemClock
 import org.junit.Assert.assertEquals
-import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotNull
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
@@ -86,7 +86,6 @@
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
     @Mock private lateinit var activityTaskManager: IActivityTaskManager
-    @Mock private lateinit var userDetailAdapter: UserSwitcherController.UserDetailAdapter
     @Mock private lateinit var telephonyListenerManager: TelephonyListenerManager
     @Mock private lateinit var secureSettings: SecureSettings
     @Mock private lateinit var falsingManager: FalsingManager
@@ -161,7 +160,6 @@
                 falsingManager,
                 telephonyListenerManager,
                 activityTaskManager,
-                userDetailAdapter,
                 secureSettings,
                 uiBgExecutor,
                 interactionJankMonitor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
index d645449..dff77f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.util.condition;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
@@ -24,16 +25,21 @@
 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.testing.AndroidTestingRunner;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.util.Arrays;
@@ -46,6 +52,7 @@
     private FakeCondition mCondition2;
     private FakeCondition mCondition3;
     private HashSet<Condition> mConditions;
+    private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
 
     private Monitor mConditionMonitor;
 
@@ -58,7 +65,83 @@
         mCondition3 = spy(new FakeCondition());
         mConditions = new HashSet<>(Arrays.asList(mCondition1, mCondition2, mCondition3));
 
-        mConditionMonitor = new Monitor(mConditions, null /*callbacks*/);
+        mConditionMonitor = new Monitor(mExecutor, mConditions, null /*callbacks*/);
+    }
+
+    @Test
+    public void testOverridingCondition() {
+        final Condition overridingCondition = Mockito.mock(Condition.class);
+        final Condition regularCondition = Mockito.mock(Condition.class);
+        final Monitor.Callback callback = Mockito.mock(Monitor.Callback.class);
+
+        final Monitor monitor = new Monitor(
+                mExecutor,
+                new HashSet<>(Arrays.asList(overridingCondition, regularCondition)),
+                new HashSet<>(Arrays.asList(callback)));
+
+        when(overridingCondition.isOverridingCondition()).thenReturn(true);
+        when(overridingCondition.isConditionMet()).thenReturn(true);
+        when(regularCondition.isConditionMet()).thenReturn(false);
+
+        final ArgumentCaptor<Condition.Callback> mCallbackCaptor =
+                ArgumentCaptor.forClass(Condition.Callback.class);
+
+        verify(overridingCondition).addCallback(mCallbackCaptor.capture());
+
+        mCallbackCaptor.getValue().onConditionChanged(overridingCondition);
+        mExecutor.runAllReady();
+
+        verify(callback).onConditionsChanged(eq(true));
+        Mockito.clearInvocations(callback);
+
+        when(regularCondition.isConditionMet()).thenReturn(true);
+        when(overridingCondition.isConditionMet()).thenReturn(false);
+
+        mCallbackCaptor.getValue().onConditionChanged(overridingCondition);
+        mExecutor.runAllReady();
+
+        verify(callback).onConditionsChanged(eq(false));
+
+        clearInvocations(callback);
+        monitor.removeCondition(overridingCondition);
+        mExecutor.runAllReady();
+
+        verify(callback).onConditionsChanged(eq(true));
+    }
+
+    /**
+     * Ensures that when multiple overriding conditions are present, it is the aggregate of those
+     * conditions that are considered.
+     */
+    @Test
+    public void testMultipleOverridingConditions() {
+        final Condition overridingCondition = Mockito.mock(Condition.class);
+        final Condition overridingCondition2 = Mockito.mock(Condition.class);
+        final Condition regularCondition = Mockito.mock(Condition.class);
+        final Monitor.Callback callback = Mockito.mock(Monitor.Callback.class);
+
+        final Monitor monitor = new Monitor(
+                mExecutor,
+                new HashSet<>(Arrays.asList(overridingCondition, overridingCondition2,
+                        regularCondition)),
+                new HashSet<>(Arrays.asList(callback)));
+
+        when(overridingCondition.isOverridingCondition()).thenReturn(true);
+        when(overridingCondition.isConditionMet()).thenReturn(true);
+        when(overridingCondition2.isOverridingCondition()).thenReturn(true);
+        when(overridingCondition.isConditionMet()).thenReturn(false);
+        when(regularCondition.isConditionMet()).thenReturn(true);
+
+        final ArgumentCaptor<Condition.Callback> mCallbackCaptor =
+                ArgumentCaptor.forClass(Condition.Callback.class);
+
+        verify(overridingCondition).addCallback(mCallbackCaptor.capture());
+
+        mCallbackCaptor.getValue().onConditionChanged(overridingCondition);
+        mExecutor.runAllReady();
+
+        verify(callback).onConditionsChanged(eq(false));
+        Mockito.clearInvocations(callback);
     }
 
     @Test
@@ -66,11 +149,13 @@
         final Monitor.Callback callback1 =
                 mock(Monitor.Callback.class);
         mConditionMonitor.addCallback(callback1);
+        mExecutor.runAllReady();
         mConditions.forEach(condition -> verify(condition).addCallback(any()));
 
         final Monitor.Callback callback2 =
                 mock(Monitor.Callback.class);
         mConditionMonitor.addCallback(callback2);
+        mExecutor.runAllReady();
         mConditions.forEach(condition -> verify(condition, times(1)).addCallback(any()));
     }
 
@@ -79,6 +164,7 @@
         final Monitor.Callback callback =
                 mock(Monitor.Callback.class);
         mConditionMonitor.addCallback(callback);
+        mExecutor.runAllReady();
         verify(callback).onConditionsChanged(false);
     }
 
@@ -86,38 +172,53 @@
     public void addCallback_addSecondCallback_reportWithExistingValue() {
         final Monitor.Callback callback1 =
                 mock(Monitor.Callback.class);
-        mConditionMonitor.addCallback(callback1);
-
-        mConditionMonitor.overrideAllConditionsMet(true);
+        final Condition condition = mock(Condition.class);
+        when(condition.isConditionMet()).thenReturn(true);
+        final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition)),
+                new HashSet<>(Arrays.asList(callback1)));
 
         final Monitor.Callback callback2 =
                 mock(Monitor.Callback.class);
-        mConditionMonitor.addCallback(callback2);
-        verify(callback2).onConditionsChanged(true);
+        monitor.addCallback(callback2);
+        mExecutor.runAllReady();
+        verify(callback2).onConditionsChanged(eq(true));
     }
 
     @Test
     public void addCallback_noConditions_reportAllConditionsMet() {
-        final Monitor monitor = new Monitor(new HashSet<>(), null /*callbacks*/);
+        final Monitor monitor = new Monitor(mExecutor, new HashSet<>(), null /*callbacks*/);
         final Monitor.Callback callback = mock(Monitor.Callback.class);
 
         monitor.addCallback(callback);
-
+        mExecutor.runAllReady();
         verify(callback).onConditionsChanged(true);
     }
 
     @Test
     public void removeCallback_shouldNoLongerReceiveUpdate() {
+        final Condition condition = mock(Condition.class);
+        final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition)),
+                null);
         final Monitor.Callback callback =
                 mock(Monitor.Callback.class);
-        mConditionMonitor.addCallback(callback);
+        monitor.addCallback(callback);
+        monitor.removeCallback(callback);
+        mExecutor.runAllReady();
         clearInvocations(callback);
-        mConditionMonitor.removeCallback(callback);
 
-        mConditionMonitor.overrideAllConditionsMet(true);
+        final ArgumentCaptor<Condition.Callback> conditionCallbackCaptor =
+                ArgumentCaptor.forClass(Condition.Callback.class);
+        verify(condition).addCallback(conditionCallbackCaptor.capture());
+        final Condition.Callback conditionCallback = conditionCallbackCaptor.getValue();
+
+        when(condition.isConditionMet()).thenReturn(true);
+        conditionCallback.onConditionChanged(condition);
+        mExecutor.runAllReady();
         verify(callback, never()).onConditionsChanged(true);
 
-        mConditionMonitor.overrideAllConditionsMet(false);
+        when(condition.isConditionMet()).thenReturn(false);
+        conditionCallback.onConditionChanged(condition);
+        mExecutor.runAllReady();
         verify(callback, never()).onConditionsChanged(false);
     }
 
@@ -131,9 +232,11 @@
         mConditionMonitor.addCallback(callback2);
 
         mConditionMonitor.removeCallback(callback1);
+        mExecutor.runAllReady();
         mConditions.forEach(condition -> verify(condition, never()).removeCallback(any()));
 
         mConditionMonitor.removeCallback(callback2);
+        mExecutor.runAllReady();
         mConditions.forEach(condition -> verify(condition).removeCallback(any()));
     }
 
@@ -147,6 +250,7 @@
         mCondition1.fakeUpdateCondition(true);
         mCondition2.fakeUpdateCondition(true);
         mCondition3.fakeUpdateCondition(true);
+        mExecutor.runAllReady();
 
         verify(callback).onConditionsChanged(true);
     }
@@ -163,6 +267,7 @@
         clearInvocations(callback);
 
         mCondition1.fakeUpdateCondition(false);
+        mExecutor.runAllReady();
         verify(callback).onConditionsChanged(false);
     }
 
@@ -171,16 +276,20 @@
         final Monitor.Callback callback =
                 mock(Monitor.Callback.class);
         mConditionMonitor.addCallback(callback);
+        mExecutor.runAllReady();
         verify(callback).onConditionsChanged(false);
         clearInvocations(callback);
 
         mCondition1.fakeUpdateCondition(true);
+        mExecutor.runAllReady();
         verify(callback, never()).onConditionsChanged(anyBoolean());
 
         mCondition2.fakeUpdateCondition(true);
+        mExecutor.runAllReady();
         verify(callback, never()).onConditionsChanged(anyBoolean());
 
         mCondition3.fakeUpdateCondition(true);
+        mExecutor.runAllReady();
         verify(callback).onConditionsChanged(true);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java
index 7fc6b51..9e0f863 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionTest.java
@@ -16,7 +16,8 @@
 
 package com.android.systemui.util.condition;
 
-import static org.mockito.ArgumentMatchers.anyBoolean;
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -73,7 +74,8 @@
 
         final Condition.Callback callback2 = mock(Condition.Callback.class);
         mCondition.addCallback(callback2);
-        verify(callback2).onConditionChanged(mCondition, true);
+        verify(callback2).onConditionChanged(mCondition);
+        assertThat(mCondition.isConditionMet()).isTrue();
     }
 
     @Test
@@ -94,7 +96,8 @@
         mCondition.addCallback(callback);
 
         mCondition.fakeUpdateCondition(true);
-        verify(callback).onConditionChanged(eq(mCondition), eq(true));
+        verify(callback).onConditionChanged(eq(mCondition));
+        assertThat(mCondition.isConditionMet()).isTrue();
     }
 
     @Test
@@ -105,7 +108,8 @@
         mCondition.addCallback(callback);
 
         mCondition.fakeUpdateCondition(false);
-        verify(callback).onConditionChanged(eq(mCondition), eq(false));
+        verify(callback).onConditionChanged(eq(mCondition));
+        assertThat(mCondition.isConditionMet()).isFalse();
     }
 
     @Test
@@ -116,7 +120,7 @@
         mCondition.addCallback(callback);
 
         mCondition.fakeUpdateCondition(true);
-        verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+        verify(callback, never()).onConditionChanged(eq(mCondition));
     }
 
     @Test
@@ -127,6 +131,6 @@
         mCondition.addCallback(callback);
 
         mCondition.fakeUpdateCondition(false);
-        verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+        verify(callback, never()).onConditionChanged(eq(mCondition));
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cf0a027..a478c31 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16140,6 +16140,23 @@
         }
 
         /**
+         * Returns package name by pid.
+         */
+        @Override
+        @Nullable
+        public String getPackageNameByPid(int pid) {
+            synchronized (mPidsSelfLocked) {
+                final ProcessRecord app = mPidsSelfLocked.get(pid);
+
+                if (app != null && app.info != null) {
+                    return app.info.packageName;
+                }
+
+                return null;
+            }
+        }
+
+        /**
          * Sets if the given pid has an overlay UI or not.
          *
          * @param pid The pid we are setting overlay UI for.
diff --git a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
index 7b76de2..3c780aa 100644
--- a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
@@ -29,6 +29,7 @@
 import android.util.Pair;
 import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.am.AppBatteryExemptionTracker.AppBatteryExemptionPolicy;
 import com.android.server.am.AppBatteryExemptionTracker.UidBatteryStates;
@@ -454,7 +455,8 @@
             super(injector, tracker,
                     KEY_BG_BATTERY_EXEMPTION_ENABLED, DEFAULT_BG_BATTERY_EXEMPTION_ENABLED,
                     AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_WINDOW,
-                    AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_WINDOW_MS);
+                    tracker.mContext.getResources()
+                    .getInteger(R.integer.config_bg_current_drain_window));
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 99808b6..6492662 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -38,7 +38,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.AppRestrictionController.DEVICE_CONFIG_SUBNAMESPACE_PREFIX;
-import static com.android.server.am.BaseAppStateTracker.ONE_DAY;
 import static com.android.server.am.BaseAppStateTracker.ONE_MINUTE;
 
 import android.annotation.NonNull;
@@ -47,6 +46,8 @@
 import android.app.ActivityManager.RestrictionLevel;
 import android.content.Context;
 import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.os.BatteryConsumer;
 import android.os.BatteryConsumer.Dimensions;
 import android.os.BatteryStatsInternal;
@@ -64,6 +65,7 @@
 import android.util.SparseBooleanArray;
 import android.util.TimeUtils;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
@@ -1025,70 +1027,58 @@
                 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_power_components";
 
         /**
-         * Default value to {@link #mTrackerEnabled}.
-         */
-        static final boolean DEFAULT_BG_CURRENT_DRAIN_MONITOR_ENABLED = true;
-
-        /**
          * Default value to the {@link #INDEX_REGULAR_CURRENT_DRAIN_THRESHOLD} of
          * the {@link #mBgCurrentDrainRestrictedBucketThreshold}.
          */
-        static final float DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_THRESHOLD =
-                isLowRamDeviceStatic() ? 4.0f : 2.0f;
+        final float mDefaultBgCurrentDrainRestrictedBucket;
 
         /**
          * Default value to the {@link #INDEX_REGULAR_CURRENT_DRAIN_THRESHOLD} of
          * the {@link #mBgCurrentDrainBgRestrictedThreshold}.
          */
-        static final float DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_THRESHOLD =
-                isLowRamDeviceStatic() ? 8.0f : 4.0f;
+        final float mDefaultBgCurrentDrainBgRestrictedThreshold;
 
         /**
          * Default value to {@link #mBgCurrentDrainWindowMs}.
          */
-        static final long DEFAULT_BG_CURRENT_DRAIN_WINDOW_MS = ONE_DAY;
+        final long mDefaultBgCurrentDrainWindowMs;
 
         /**
          * Default value to the {@link #INDEX_HIGH_CURRENT_DRAIN_THRESHOLD} of
          * the {@link #mBgCurrentDrainRestrictedBucketThreshold}.
          */
-        static final float DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_HIGH_THRESHOLD =
-                isLowRamDeviceStatic() ? 60.0f : 30.0f;
+        final float mDefaultBgCurrentDrainRestrictedBucketHighThreshold;
 
         /**
          * Default value to the {@link #INDEX_HIGH_CURRENT_DRAIN_THRESHOLD} of
          * the {@link #mBgCurrentDrainBgRestrictedThreshold}.
          */
-        static final float DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_HIGH_THRESHOLD =
-                isLowRamDeviceStatic() ? 60.0f : 30.0f;
+        final float mDefaultBgCurrentDrainBgRestrictedHighThreshold;
 
         /**
          * Default value to {@link #mBgCurrentDrainMediaPlaybackMinDuration}.
          */
-        static final long DEFAULT_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION = 30 * ONE_MINUTE;
+        final long mDefaultBgCurrentDrainMediaPlaybackMinDuration;
 
         /**
          * Default value to {@link #mBgCurrentDrainLocationMinDuration}.
          */
-        static final long DEFAULT_BG_CURRENT_DRAIN_LOCATION_MIN_DURATION = 30 * ONE_MINUTE;
+        final long mDefaultBgCurrentDrainLocationMinDuration;
 
         /**
          * Default value to {@link #mBgCurrentDrainEventDurationBasedThresholdEnabled}.
          */
-        static final boolean DEFAULT_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED =
-                false;
+        final boolean mDefaultBgCurrentDrainEventDurationBasedThresholdEnabled;
 
         /**
          * Default value to {@link #mBgCurrentDrainRestrictedBucketTypes}.
          */
-        static final int DEFAULT_BG_CURRENT_DRAIN_TYPES_TO_RESTRICTED_BUCKET =
-                BATTERY_USAGE_TYPE_BACKGROUND;
+        final int mDefaultCurrentDrainTypesToRestrictedBucket;
 
         /**
          * Default value to {@link #mBgCurrentDrainBgRestrictedTypes}.
          */
-        static final int DEFAULT_BG_CURRENT_DRAIN_TYPES_TO_BG_RESTRICTED =
-                BATTERY_USAGE_TYPE_BACKGROUND | BATTERY_USAGE_TYPE_FOREGROUND_SERVICE;
+        final int mDefaultBgCurrentDrainTypesToBgRestricted;
 
         /**
          * Default value to {@link #mBgCurrentDrainPowerComponents}.
@@ -1096,6 +1086,8 @@
         @BatteryConsumer.PowerComponent
         static final int DEFAULT_BG_CURRENT_DRAIN_POWER_COMPONENTS = POWER_COMPONENT_ANY;
 
+        final int mDefaultBgCurrentDrainPowerComponent;
+
         /**
          * The index to {@link #mBgCurrentDrainRestrictedBucketThreshold}
          * and {@link #mBgCurrentDrainBgRestrictedThreshold}.
@@ -1107,36 +1099,28 @@
          * @see #KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET.
          * @see #KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_RESTRICTED_BUCKET.
          */
-        volatile float[] mBgCurrentDrainRestrictedBucketThreshold = {
-                DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_THRESHOLD,
-                DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_HIGH_THRESHOLD,
-        };
+        volatile float[] mBgCurrentDrainRestrictedBucketThreshold = new float[2];
 
         /**
          * @see #KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_BG_RESTRICTED.
          * @see #KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_BG_RESTRICTED.
          */
-        volatile float[] mBgCurrentDrainBgRestrictedThreshold = {
-                DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_THRESHOLD,
-                DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_HIGH_THRESHOLD,
-        };
+        volatile float[] mBgCurrentDrainBgRestrictedThreshold = new float[2];
 
         /**
          * @see #KEY_BG_CURRENT_DRAIN_WINDOW.
          */
-        volatile long mBgCurrentDrainWindowMs = DEFAULT_BG_CURRENT_DRAIN_WINDOW_MS;
+        volatile long mBgCurrentDrainWindowMs;
 
         /**
          * @see #KEY_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION.
          */
-        volatile long mBgCurrentDrainMediaPlaybackMinDuration =
-                DEFAULT_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION;
+        volatile long mBgCurrentDrainMediaPlaybackMinDuration;
 
         /**
          * @see #KEY_BG_CURRENT_DRAIN_LOCATION_MIN_DURATION.
          */
-        volatile long mBgCurrentDrainLocationMinDuration =
-                DEFAULT_BG_CURRENT_DRAIN_LOCATION_MIN_DURATION;
+        volatile long mBgCurrentDrainLocationMinDuration;
 
         /**
          * @see #KEY_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED.
@@ -1183,8 +1167,62 @@
 
         AppBatteryPolicy(@NonNull Injector injector, @NonNull AppBatteryTracker tracker) {
             super(injector, tracker, KEY_BG_CURRENT_DRAIN_MONITOR_ENABLED,
-                    DEFAULT_BG_CURRENT_DRAIN_MONITOR_ENABLED);
+                    tracker.mContext.getResources()
+                    .getBoolean(R.bool.config_bg_current_drain_monitor_enabled));
             mLock = tracker.mLock;
+            final Resources resources = tracker.mContext.getResources();
+            float[] val = getFloatArray(resources.obtainTypedArray(
+                    R.array.config_bg_current_drain_threshold_to_restricted_bucket));
+            mDefaultBgCurrentDrainRestrictedBucket =
+                    isLowRamDeviceStatic() ? val[1] : val[0];
+            val = getFloatArray(resources.obtainTypedArray(
+                    R.array.config_bg_current_drain_threshold_to_bg_restricted));
+            mDefaultBgCurrentDrainBgRestrictedThreshold =
+                    isLowRamDeviceStatic() ? val[1] : val[0];
+            mDefaultBgCurrentDrainWindowMs = resources.getInteger(
+                    R.integer.config_bg_current_drain_window);
+            val = getFloatArray(resources.obtainTypedArray(
+                    R.array.config_bg_current_drain_high_threshold_to_restricted_bucket));
+            mDefaultBgCurrentDrainRestrictedBucketHighThreshold =
+                    isLowRamDeviceStatic() ? val[1] : val[0];
+            val = getFloatArray(resources.obtainTypedArray(
+                    R.array.config_bg_current_drain_high_threshold_to_bg_restricted));
+            mDefaultBgCurrentDrainBgRestrictedHighThreshold =
+                    isLowRamDeviceStatic() ? val[1] : val[0];
+            mDefaultBgCurrentDrainMediaPlaybackMinDuration = resources.getInteger(
+                    R.integer.config_bg_current_drain_media_playback_min_duration);
+            mDefaultBgCurrentDrainLocationMinDuration = resources.getInteger(
+                    R.integer.config_bg_current_drain_location_min_duration);
+            mDefaultBgCurrentDrainEventDurationBasedThresholdEnabled = resources.getBoolean(
+                    R.bool.config_bg_current_drain_event_duration_based_threshold_enabled);
+            mDefaultCurrentDrainTypesToRestrictedBucket = resources.getInteger(
+                    R.integer.config_bg_current_drain_types_to_restricted_bucket);
+            mDefaultBgCurrentDrainTypesToBgRestricted = resources.getInteger(
+                    R.integer.config_bg_current_drain_types_to_bg_restricted);
+            mDefaultBgCurrentDrainPowerComponent = resources.getInteger(
+                    R.integer.config_bg_current_drain_power_components);
+            mBgCurrentDrainRestrictedBucketThreshold[0] =
+                    mDefaultBgCurrentDrainRestrictedBucket;
+            mBgCurrentDrainRestrictedBucketThreshold[1] =
+                    mDefaultBgCurrentDrainRestrictedBucketHighThreshold;
+            mBgCurrentDrainBgRestrictedThreshold[0] =
+                    mDefaultBgCurrentDrainBgRestrictedThreshold;
+            mBgCurrentDrainBgRestrictedThreshold[1] =
+                    mDefaultBgCurrentDrainBgRestrictedHighThreshold;
+            mBgCurrentDrainWindowMs = mDefaultBgCurrentDrainWindowMs;
+            mBgCurrentDrainMediaPlaybackMinDuration =
+                    mDefaultBgCurrentDrainMediaPlaybackMinDuration;
+            mBgCurrentDrainLocationMinDuration = mDefaultBgCurrentDrainLocationMinDuration;
+        }
+
+        static float[] getFloatArray(TypedArray array) {
+            int length = array.length();
+            float[] floatArray = new float[length];
+            for (int i = 0; i < length; i++) {
+                floatArray[i] = array.getFloat(i, Float.NaN);
+            }
+            array.recycle();
+            return floatArray;
         }
 
         @Override
@@ -1234,31 +1272,31 @@
             mBgCurrentDrainRestrictedBucketThreshold[INDEX_REGULAR_CURRENT_DRAIN_THRESHOLD] =
                     DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET,
-                    DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_THRESHOLD);
+                    mDefaultBgCurrentDrainRestrictedBucket);
             mBgCurrentDrainRestrictedBucketThreshold[INDEX_HIGH_CURRENT_DRAIN_THRESHOLD] =
                     DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_RESTRICTED_BUCKET,
-                    DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_HIGH_THRESHOLD);
+                    mDefaultBgCurrentDrainRestrictedBucketHighThreshold);
             mBgCurrentDrainBgRestrictedThreshold[INDEX_REGULAR_CURRENT_DRAIN_THRESHOLD] =
                     DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_BG_RESTRICTED,
-                    DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_THRESHOLD);
+                    mDefaultBgCurrentDrainBgRestrictedThreshold);
             mBgCurrentDrainBgRestrictedThreshold[INDEX_HIGH_CURRENT_DRAIN_THRESHOLD] =
                     DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_BG_RESTRICTED,
-                    DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_HIGH_THRESHOLD);
+                    mDefaultBgCurrentDrainBgRestrictedHighThreshold);
             mBgCurrentDrainRestrictedBucketTypes =
                     DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     KEY_BG_CURRENT_DRAIN_TYPES_TO_RESTRICTED_BUCKET,
-                    DEFAULT_BG_CURRENT_DRAIN_TYPES_TO_RESTRICTED_BUCKET);
+                    mDefaultCurrentDrainTypesToRestrictedBucket);
             mBgCurrentDrainBgRestrictedTypes =
                     DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     KEY_BG_CURRENT_DRAIN_TYPES_TO_BG_RESTRICTED,
-                    DEFAULT_BG_CURRENT_DRAIN_TYPES_TO_BG_RESTRICTED);
+                    mDefaultBgCurrentDrainTypesToBgRestricted);
             mBgCurrentDrainPowerComponents =
                     DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     KEY_BG_CURRENT_DRAIN_POWER_COMPONENTS,
-                    DEFAULT_BG_CURRENT_DRAIN_POWER_COMPONENTS);
+                    mDefaultBgCurrentDrainPowerComponent);
             if (mBgCurrentDrainPowerComponents == DEFAULT_BG_CURRENT_DRAIN_POWER_COMPONENTS) {
                 mBatteryDimensions = BatteryUsage.BATT_DIMENS;
             } else {
@@ -1273,28 +1311,28 @@
             mBgCurrentDrainWindowMs = DeviceConfig.getLong(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     KEY_BG_CURRENT_DRAIN_WINDOW,
-                    DEFAULT_BG_CURRENT_DRAIN_WINDOW_MS);
+                    mDefaultBgCurrentDrainWindowMs);
         }
 
         private void updateCurrentDrainMediaPlaybackMinDuration() {
             mBgCurrentDrainMediaPlaybackMinDuration = DeviceConfig.getLong(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     KEY_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION,
-                    DEFAULT_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION);
+                    mDefaultBgCurrentDrainMediaPlaybackMinDuration);
         }
 
         private void updateCurrentDrainLocationMinDuration() {
             mBgCurrentDrainLocationMinDuration = DeviceConfig.getLong(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     KEY_BG_CURRENT_DRAIN_LOCATION_MIN_DURATION,
-                    DEFAULT_BG_CURRENT_DRAIN_LOCATION_MIN_DURATION);
+                    mDefaultBgCurrentDrainLocationMinDuration);
         }
 
         private void updateCurrentDrainEventDurationBasedThresholdEnabled() {
             mBgCurrentDrainEventDurationBasedThresholdEnabled = DeviceConfig.getBoolean(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     KEY_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED,
-                    DEFAULT_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED);
+                    mDefaultBgCurrentDrainEventDurationBasedThresholdEnabled);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index cedc7db..d2c6c13 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2968,8 +2968,9 @@
         int step;
 
         // skip a2dp absolute volume control request when the device
-        // is not an a2dp device
-        if (!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
+        // is neither an a2dp device nor BLE device
+        if ((!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
+                && !AudioSystem.DEVICE_OUT_ALL_BLE_SET.contains(device))
                 && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {
             return;
         }
@@ -3107,7 +3108,8 @@
             }
 
             if (device == AudioSystem.DEVICE_OUT_BLE_HEADSET
-                    && streamType == getBluetoothContextualVolumeStream()) {
+                    && streamType == getBluetoothContextualVolumeStream()
+                    && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
                 if (DEBUG_VOL) {
                     Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index="
                             + newIndex + " stream=" + streamType);
@@ -3707,8 +3709,9 @@
         int oldIndex;
 
         // skip a2dp absolute volume control request when the device
-        // is not an a2dp device
-        if (!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
+        // is neither an a2dp device nor BLE device
+        if ((!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
+                && !AudioSystem.DEVICE_OUT_ALL_BLE_SET.contains(device))
                 && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {
             return;
         }
@@ -3751,7 +3754,8 @@
             }
 
             if (device == AudioSystem.DEVICE_OUT_BLE_HEADSET
-                    && streamType == getBluetoothContextualVolumeStream()) {
+                    && streamType == getBluetoothContextualVolumeStream()
+                    && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
                 if (DEBUG_VOL) {
                     Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index="
                             + index + " stream=" + streamType);
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index 603f206..108e7bc 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -25,7 +25,6 @@
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
 import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES;
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH;
 import static com.android.server.net.NetworkPolicyManagerService.OPPORTUNISTIC_QUOTA_UNKNOWN;
@@ -191,6 +190,7 @@
     class MultipathTracker {
         final Network network;
         final String subscriberId;
+        private final int mSubId;
 
         private long mQuota;
         /** Current multipath budget. Nonzero iff we have budget and a UsageCallback is armed. */
@@ -204,9 +204,8 @@
             this.network = network;
             this.mNetworkCapabilities = new NetworkCapabilities(nc);
             NetworkSpecifier specifier = nc.getNetworkSpecifier();
-            int subId = INVALID_SUBSCRIPTION_ID;
             if (specifier instanceof TelephonyNetworkSpecifier) {
-                subId = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
+                mSubId = ((TelephonyNetworkSpecifier) specifier).getSubscriptionId();
             } else {
                 throw new IllegalStateException(String.format(
                         "Can't get subId from mobile network %s (%s)",
@@ -217,14 +216,14 @@
             if (tele == null) {
                 throw new IllegalStateException(String.format("Missing TelephonyManager"));
             }
-            tele = tele.createForSubscriptionId(subId);
+            tele = tele.createForSubscriptionId(mSubId);
             if (tele == null) {
                 throw new IllegalStateException(String.format(
-                        "Can't get TelephonyManager for subId %d", subId));
+                        "Can't get TelephonyManager for subId %d", mSubId));
             }
 
             subscriberId = Objects.requireNonNull(tele.getSubscriberId(),
-                    "Null subscriber Id for subId " + subId);
+                    "Null subscriber Id for subId " + mSubId);
             mNetworkTemplate = new NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
                     .setSubscriberIds(Set.of(subscriberId))
                     .setMeteredness(NetworkStats.METERED_YES)
@@ -282,6 +281,7 @@
                     .setSubscriberId(subscriberId)
                     .setRoaming(!nc.hasCapability(NET_CAPABILITY_NOT_ROAMING))
                     .setMetered(!nc.hasCapability(NET_CAPABILITY_NOT_METERED))
+                    .setSubId(mSubId)
                     .build();
         }
 
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index d71e07a..418e91d 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -75,6 +75,7 @@
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.io.PrintWriter;
+import java.util.Objects;
 
 /**
  * Controls the power state of the display.
@@ -236,42 +237,42 @@
 
     // True if we should fade the screen while turning it off, false if we should play
     // a stylish color fade animation instead.
-    private boolean mColorFadeFadesConfig;
+    private final boolean mColorFadeFadesConfig;
 
     // True if we need to fake a transition to off when coming out of a doze state.
     // Some display hardware will blank itself when coming out of doze in order to hide
     // artifacts. For these displays we fake a transition into OFF so that policy can appropriately
     // blank itself and begin an appropriate power on animation.
-    private boolean mDisplayBlanksAfterDozeConfig;
+    private final boolean mDisplayBlanksAfterDozeConfig;
 
     // True if there are only buckets of brightness values when the display is in the doze state,
     // rather than a full range of values. If this is true, then we'll avoid animating the screen
     // brightness since it'd likely be multiple jarring brightness transitions instead of just one
     // to reach the final state.
-    private boolean mBrightnessBucketsInDozeConfig;
+    private final boolean mBrightnessBucketsInDozeConfig;
 
     // The pending power request.
     // Initially null until the first call to requestPowerState.
-    // Guarded by mLock.
+    @GuardedBy("mLock")
     private DisplayPowerRequest mPendingRequestLocked;
 
     // True if a request has been made to wait for the proximity sensor to go negative.
-    // Guarded by mLock.
+    @GuardedBy("mLock")
     private boolean mPendingWaitForNegativeProximityLocked;
 
     // True if the pending power request or wait for negative proximity flag
     // has been changed since the last update occurred.
-    // Guarded by mLock.
+    @GuardedBy("mLock")
     private boolean mPendingRequestChangedLocked;
 
     // Set to true when the important parts of the pending power request have been applied.
     // The important parts are mainly the screen state.  Brightness changes may occur
     // concurrently.
-    // Guarded by mLock.
+    @GuardedBy("mLock")
     private boolean mDisplayReadyLocked;
 
     // Set to true if a power state update is required.
-    // Guarded by mLock.
+    @GuardedBy("mLock")
     private boolean mPendingUpdatePowerStateLocked;
 
     /* The following state must only be accessed by the handler thread. */
@@ -352,8 +353,8 @@
     // information.
     // At the time of this writing, this value is changed within updatePowerState() only, which is
     // limited to the thread used by DisplayControllerHandler.
-    private BrightnessReason mBrightnessReason = new BrightnessReason();
-    private BrightnessReason mBrightnessReasonTemp = new BrightnessReason();
+    private final BrightnessReason mBrightnessReason = new BrightnessReason();
+    private final BrightnessReason mBrightnessReasonTemp = new BrightnessReason();
 
     // Brightness animation ramp rates in brightness units per second
     private float mBrightnessRampRateFastDecrease;
@@ -849,6 +850,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void sendUpdatePowerStateLocked() {
         if (!mStopped && !mPendingUpdatePowerStateLocked) {
             mPendingUpdatePowerStateLocked = true;
@@ -2318,6 +2320,7 @@
         return mAutomaticBrightnessController.convertToNits(brightness);
     }
 
+    @GuardedBy("mLock")
     private void updatePendingProximityRequestsLocked() {
         mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
         mPendingWaitForNegativeProximityLocked = false;
@@ -2421,12 +2424,7 @@
         pw.println("  mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
         pw.println("  mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
 
-        mHandler.runWithScissors(new Runnable() {
-            @Override
-            public void run() {
-                dumpLocal(pw);
-            }
-        }, 1000);
+        mHandler.runWithScissors(() -> dumpLocal(pw), 1000);
     }
 
     private void dumpLocal(PrintWriter pw) {
@@ -2458,6 +2456,9 @@
         pw.println("  mAppliedThrottling=" + mAppliedThrottling);
         pw.println("  mAppliedScreenBrightnessOverride=" + mAppliedScreenBrightnessOverride);
         pw.println("  mAppliedTemporaryBrightness=" + mAppliedTemporaryBrightness);
+        pw.println("  mAppliedTemporaryAutoBrightnessAdjustment="
+                + mAppliedTemporaryAutoBrightnessAdjustment);
+        pw.println("  mAppliedBrightnessBoost=" + mAppliedBrightnessBoost);
         pw.println("  mDozing=" + mDozing);
         pw.println("  mSkipRampState=" + skipRampStateToString(mSkipRampState));
         pw.println("  mScreenOnBlockStartRealTime=" + mScreenOnBlockStartRealTime);
@@ -2465,21 +2466,21 @@
         pw.println("  mPendingScreenOnUnblocker=" + mPendingScreenOnUnblocker);
         pw.println("  mPendingScreenOffUnblocker=" + mPendingScreenOffUnblocker);
         pw.println("  mPendingScreenOff=" + mPendingScreenOff);
-        pw.println("  mReportedToPolicy=" +
-                reportedToPolicyToString(mReportedScreenStateToPolicy));
+        pw.println("  mReportedToPolicy="
+                + reportedToPolicyToString(mReportedScreenStateToPolicy));
 
         if (mScreenBrightnessRampAnimator != null) {
-            pw.println("  mScreenBrightnessRampAnimator.isAnimating()=" +
-                    mScreenBrightnessRampAnimator.isAnimating());
+            pw.println("  mScreenBrightnessRampAnimator.isAnimating()="
+                    + mScreenBrightnessRampAnimator.isAnimating());
         }
 
         if (mColorFadeOnAnimator != null) {
-            pw.println("  mColorFadeOnAnimator.isStarted()=" +
-                    mColorFadeOnAnimator.isStarted());
+            pw.println("  mColorFadeOnAnimator.isStarted()="
+                    + mColorFadeOnAnimator.isStarted());
         }
         if (mColorFadeOffAnimator != null) {
-            pw.println("  mColorFadeOffAnimator.isStarted()=" +
-                    mColorFadeOffAnimator.isStarted());
+            pw.println("  mColorFadeOffAnimator.isStarted()="
+                    + mColorFadeOffAnimator.isStarted());
         }
 
         if (mPowerState != null) {
@@ -2605,7 +2606,7 @@
         }
     }
 
-    private final void logHbmBrightnessStats(float brightness, int displayStatsId) {
+    private void logHbmBrightnessStats(float brightness, int displayStatsId) {
         synchronized (mHandler) {
             FrameworkStatsLog.write(
                     FrameworkStatsLog.DISPLAY_HBM_BRIGHTNESS_CHANGED, displayStatsId, brightness);
@@ -2824,7 +2825,7 @@
 
         @Override
         public boolean equals(Object obj) {
-            if (obj == null || !(obj instanceof BrightnessReason)) {
+            if (!(obj instanceof BrightnessReason)) {
                 return false;
             }
             BrightnessReason other = (BrightnessReason) obj;
@@ -2832,6 +2833,11 @@
         }
 
         @Override
+        public int hashCode() {
+            return Objects.hash(reason, modifier);
+        }
+
+        @Override
         public String toString() {
             return toString(0);
         }
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index c02e725..d233c5e 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -182,8 +182,11 @@
         private final long mPhysicalDisplayId;
         private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>();
         private final ArrayList<Integer> mSupportedColorModes = new ArrayList<>();
+        private final DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
+                new DisplayModeDirector.DesiredDisplayModeSpecs();
         private final boolean mIsDefaultDisplay;
         private final BacklightAdapter mBacklightAdapter;
+        private final SidekickInternal mSidekickInternal;
 
         private DisplayDeviceInfo mInfo;
         private boolean mHavePendingChanges;
@@ -200,8 +203,6 @@
         private int mActiveDisplayModeAtStartId = INVALID_MODE_ID;
         private Display.Mode mUserPreferredMode;
         private int mActiveModeId = INVALID_MODE_ID;
-        private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
-                new DisplayModeDirector.DesiredDisplayModeSpecs();
         private boolean mDisplayModeSpecsInvalid;
         private int mActiveColorMode;
         private Display.HdrCapabilities mHdrCapabilities;
@@ -210,13 +211,11 @@
         private boolean mAllmRequested;
         private boolean mGameContentTypeRequested;
         private boolean mSidekickActive;
-        private SidekickInternal mSidekickInternal;
         private SurfaceControl.StaticDisplayInfo mStaticDisplayInfo;
         // The supported display modes according to SurfaceFlinger
         private SurfaceControl.DisplayMode[] mSfDisplayModes;
         // The active display mode in SurfaceFlinger
         private SurfaceControl.DisplayMode mActiveSfDisplayMode;
-        private DisplayDeviceConfig mDisplayDeviceConfig;
 
         private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides =
                 new DisplayEventReceiver.FrameRateOverride[0];
@@ -233,7 +232,6 @@
             mSidekickInternal = LocalServices.getService(SidekickInternal.class);
             mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay,
                     mSurfaceControlProxy);
-            mDisplayDeviceConfig = null;
             mActiveDisplayModeAtStartId = dynamicInfo.activeDisplayModeId;
         }
 
@@ -459,9 +457,6 @@
             final Context context = getOverlayContext();
             mDisplayDeviceConfig = DisplayDeviceConfig.create(context, mPhysicalDisplayId,
                     mIsDefaultDisplay);
-            if (mDisplayDeviceConfig == null) {
-                return;
-            }
 
             // Load brightness HWC quirk
             mBacklightAdapter.setForceSurfaceControl(mDisplayDeviceConfig.hasQuirk(
@@ -1083,8 +1078,8 @@
             pw.println("mGameContentTypeRequested=" + mGameContentTypeRequested);
             pw.println("mStaticDisplayInfo=" + mStaticDisplayInfo);
             pw.println("mSfDisplayModes=");
-            for (int i = 0; i < mSfDisplayModes.length; i++) {
-                pw.println("  " + mSfDisplayModes[i]);
+            for (SurfaceControl.DisplayMode sfDisplayMode : mSfDisplayModes) {
+                pw.println("  " + sfDisplayMode);
             }
             pw.println("mActiveSfDisplayMode=" + mActiveSfDisplayMode);
             pw.println("mSupportedModes=");
@@ -1238,6 +1233,8 @@
     }
 
     public static class Injector {
+        // Native callback.
+        @SuppressWarnings("unused")
         private ProxyDisplayEventReceiver mReceiver;
         public void setDisplayEventListenerLocked(Looper looper, DisplayEventListener listener) {
             mReceiver = new ProxyDisplayEventReceiver(looper, listener);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 93c73be..6f5729f 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -825,7 +825,6 @@
      *
      * @param device The device to associate with the LogicalDisplay.
      * @param displayId The display ID to give the new display. If invalid, a new ID is assigned.
-     * @param isDefault Indicates if we are creating the default display.
      * @return The new logical display if created, null otherwise.
      */
     private LogicalDisplay createNewLogicalDisplayLocked(DisplayDevice device, int displayId) {
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index d8672fc..2567e43 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -55,7 +55,7 @@
      * If this is the first time the property is being set or if the rate is 0,
      * the value jumps directly to the target.
      *
-     * @param target The target value.
+     * @param targetLinear The target value.
      * @param rate The convergence rate in units per second, or 0 to set the value immediately.
      * @return True if the target differs from the previous target.
      */
diff --git a/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java b/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java
new file mode 100644
index 0000000..6b442a6
--- /dev/null
+++ b/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.logcat;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.os.ServiceManager;
+import android.os.logcat.ILogcatManagerService;
+import android.util.Slog;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+
+/**
+ * This dialog is shown to the user before an activity in a harmful app is launched.
+ *
+ * See {@code PackageManager.setLogcatAppInfo} for more info.
+ */
+public class LogAccessConfirmationActivity extends AlertActivity implements
+        DialogInterface.OnClickListener {
+    private static final String TAG = LogAccessConfirmationActivity.class.getSimpleName();
+
+    private String mPackageName;
+    private IntentSender mTarget;
+    private final ILogcatManagerService mLogcatManagerService =
+            ILogcatManagerService.Stub.asInterface(ServiceManager.getService("logcat"));
+
+    private int mUid;
+    private int mGid;
+    private int mPid;
+    private int mFd;
+
+    private static final String EXTRA_UID = "uid";
+    private static final String EXTRA_GID = "gid";
+    private static final String EXTRA_PID = "pid";
+    private static final String EXTRA_FD = "fd";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final Intent intent = getIntent();
+        mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+        mUid = intent.getIntExtra("uid", 0);
+        mGid = intent.getIntExtra("gid", 0);
+        mPid = intent.getIntExtra("pid", 0);
+        mFd = intent.getIntExtra("fd", 0);
+
+        final AlertController.AlertParams p = mAlertParams;
+        p.mTitle = getString(R.string.log_access_confirmation_title);
+        p.mView = createView();
+
+        p.mPositiveButtonText = getString(R.string.log_access_confirmation_allow);
+        p.mPositiveButtonListener = this;
+        p.mNegativeButtonText = getString(R.string.log_access_confirmation_deny);
+        p.mNegativeButtonListener = this;
+
+        mAlert.installContent(mAlertParams);
+    }
+
+    private View createView() {
+        final View view = getLayoutInflater().inflate(R.layout.harmful_app_warning_dialog,
+                null /*root*/);
+        ((TextView) view.findViewById(R.id.app_name_text))
+                .setText(mPackageName);
+        ((TextView) view.findViewById(R.id.message))
+                .setText(getIntent().getExtras().getString("body"));
+        return view;
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        switch (which) {
+            case DialogInterface.BUTTON_POSITIVE:
+                try {
+                    mLogcatManagerService.approve(mUid, mGid, mPid, mFd);
+                } catch (Throwable t) {
+                    Slog.e(TAG, "Could not start the LogcatManagerService.", t);
+                }
+                finish();
+                break;
+            case DialogInterface.BUTTON_NEGATIVE:
+                try {
+                    mLogcatManagerService.decline(mUid, mGid, mPid, mFd);
+                } catch (Throwable t) {
+                    Slog.e(TAG, "Could not start the LogcatManagerService.", t);
+                }
+                finish();
+                break;
+        }
+    }
+
+    /**
+     * Create the Intent for a LogAccessConfirmationActivity.
+     */
+    public static Intent createIntent(Context context, String targetPackageName,
+            IntentSender target, int uid, int gid, int pid, int fd) {
+        final Intent intent = new Intent();
+        intent.setClass(context, LogAccessConfirmationActivity.class);
+        intent.putExtra(Intent.EXTRA_PACKAGE_NAME, targetPackageName);
+        intent.putExtra(EXTRA_UID, uid);
+        intent.putExtra(EXTRA_GID, gid);
+        intent.putExtra(EXTRA_PID, pid);
+        intent.putExtra(EXTRA_FD, fd);
+
+        return intent;
+    }
+
+}
diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java
index ff6372ae..34614d5 100644
--- a/services/core/java/com/android/server/logcat/LogcatManagerService.java
+++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java
@@ -16,20 +16,36 @@
 
 package com.android.server.logcat;
 
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.ActivityManagerInternal;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.ILogd;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.os.logcat.ILogcatManagerService;
 import android.util.Slog;
 
+import com.android.internal.R;
+import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
+import java.util.Arrays;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
 /**
- * Service responsible for manage the access to Logcat.
+ * Service responsible for managing the access to Logcat.
  */
 public final class LogcatManagerService extends SystemService {
 
@@ -38,6 +54,43 @@
     private final BinderService mBinderService;
     private final ExecutorService mThreadExecutor;
     private ILogd mLogdService;
+    private NotificationManager mNotificationManager;
+    private @NonNull ActivityManager mActivityManager;
+    private ActivityManagerInternal mActivityManagerInternal;
+    private static final int MAX_UID_IMPORTANCE_COUNT_LISTENER = 2;
+    private static int sUidImportanceListenerCount = 0;
+    private static final int AID_SHELL_UID = 2000;
+
+    // TODO This allowlist is just a temporary workaround for the tests:
+    //      FrameworksServicesTests
+    //      PlatformRuleTests
+    // After adapting the test suites, the allowlist will be removed in
+    // the upcoming bug fix patches.
+    private static final String[] ALLOWABLE_TESTING_PACKAGES = {
+            "android.platform.test.rule.tests",
+            "com.android.frameworks.servicestests"
+    };
+
+    // TODO Same as the above ALLOWABLE_TESTING_PACKAGES.
+    private boolean isAllowableTestingPackage(int uid) {
+        PackageManager pm = mContext.getPackageManager();
+
+        String[] packageNames = pm.getPackagesForUid(uid);
+
+        if (ArrayUtils.isEmpty(packageNames)) {
+            return false;
+        }
+
+        for (String name : packageNames) {
+            Slog.e(TAG, "isAllowableTestingPackage: " + name);
+
+            if (Arrays.asList(ALLOWABLE_TESTING_PACKAGES).contains(name)) {
+                return true;
+            }
+        }
+
+        return false;
+    };
 
     private final class BinderService extends ILogcatManagerService.Stub {
         @Override
@@ -51,6 +104,197 @@
             // the logd data access is finished.
             mThreadExecutor.execute(new LogdMonitor(uid, gid, pid, fd, false));
         }
+
+        @Override
+        public void approve(int uid, int gid, int pid, int fd) {
+            try {
+                getLogdService().approve(uid, gid, pid, fd);
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+        }
+
+        @Override
+        public void decline(int uid, int gid, int pid, int fd) {
+            try {
+                getLogdService().decline(uid, gid, pid, fd);
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private ILogd getLogdService() {
+        synchronized (LogcatManagerService.this) {
+            if (mLogdService == null) {
+                LogcatManagerService.this.addLogdService();
+            }
+            return mLogdService;
+        }
+    }
+
+    private String getBodyString(Context context, String callingPackage, int uid) {
+        PackageManager pm = context.getPackageManager();
+        try {
+            return context.getString(
+                com.android.internal.R.string.log_access_confirmation_body,
+                pm.getApplicationInfoAsUser(callingPackage, PackageManager.MATCH_DIRECT_BOOT_AUTO,
+                    UserHandle.getUserId(uid)).loadLabel(pm));
+        } catch (NameNotFoundException e) {
+            // App name is unknown.
+            return null;
+        }
+    }
+
+    private void sendNotification(int notificationId, String clientInfo, int uid, int gid, int pid,
+            int fd) {
+
+        final ActivityManagerInternal activityManagerInternal =
+                LocalServices.getService(ActivityManagerInternal.class);
+
+        PackageManager pm = mContext.getPackageManager();
+        String packageName = activityManagerInternal.getPackageNameByPid(pid);
+        if (packageName != null) {
+            String notificationBody = getBodyString(mContext, packageName, uid);
+
+            final Intent mIntent = LogAccessConfirmationActivity.createIntent(mContext,
+                    packageName, null, uid, gid, pid, fd);
+
+            if (notificationBody == null) {
+                // Decline the logd access if the nofitication body is unknown
+                Slog.e(TAG, "Unknown notification body, declining the logd access");
+                declineLogdAccess(uid, gid, pid, fd);
+                return;
+            }
+
+            // TODO Next version will replace notification with dialogue
+            // per UX guidance.
+            generateNotificationWithBodyContent(notificationId, clientInfo, notificationBody,
+                    mIntent);
+            return;
+
+        }
+
+        String[] packageNames = pm.getPackagesForUid(uid);
+
+        if (ArrayUtils.isEmpty(packageNames)) {
+            // Decline the logd access if the app name is unknown
+            Slog.e(TAG, "Unknown calling package name, declining the logd access");
+            declineLogdAccess(uid, gid, pid, fd);
+            return;
+        }
+
+        String firstPackageName = packageNames[0];
+
+        if (firstPackageName == null || firstPackageName.length() == 0) {
+            // Decline the logd access if the package name from uid is unknown
+            Slog.e(TAG, "Unknown calling package name, declining the logd access");
+            declineLogdAccess(uid, gid, pid, fd);
+            return;
+        }
+
+        String notificationBody = getBodyString(mContext, firstPackageName, uid);
+
+        final Intent mIntent = LogAccessConfirmationActivity.createIntent(mContext,
+                firstPackageName, null, uid, gid, pid, fd);
+
+        if (notificationBody == null) {
+            Slog.e(TAG, "Unknown notification body, declining the logd access");
+            declineLogdAccess(uid, gid, pid, fd);
+            return;
+        }
+
+        // TODO Next version will replace notification with dialogue
+        // per UX guidance.
+        generateNotificationWithBodyContent(notificationId, clientInfo,
+                notificationBody, mIntent);
+    }
+
+    private void declineLogdAccess(int uid, int gid, int pid, int fd) {
+        try {
+            getLogdService().decline(uid, gid, pid, fd);
+        } catch (RemoteException ex) {
+            Slog.e(TAG, "Fails to call remote functions ", ex);
+        }
+    }
+
+    private void generateNotificationWithBodyContent(int notificationId, String clientInfo,
+            String notificationBody, Intent intent) {
+        final Notification.Builder notificationBuilder = new Notification.Builder(
+                mContext,
+                SystemNotificationChannels.ACCESSIBILITY_SECURITY_POLICY);
+        intent.setFlags(
+                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        intent.setIdentifier(String.valueOf(notificationId) + clientInfo);
+        intent.putExtra("body", notificationBody);
+
+        notificationBuilder
+            .setSmallIcon(R.drawable.ic_info)
+            .setContentTitle(
+                mContext.getString(R.string.log_access_confirmation_title))
+            .setContentText(notificationBody)
+            .setContentIntent(
+                PendingIntent.getActivity(mContext, 0, intent,
+                    PendingIntent.FLAG_IMMUTABLE))
+            .setTicker(mContext.getString(R.string.log_access_confirmation_title))
+            .setOnlyAlertOnce(true)
+            .setAutoCancel(true);
+        mNotificationManager.notify(notificationId, notificationBuilder.build());
+    }
+
+    /**
+     * A class which watches an uid for background access and notifies the logdMonitor when
+     * the package status becomes foreground (importance change)
+     */
+    private class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
+        private final int mExpectedUid;
+        private final int mExpectedGid;
+        private final int mExpectedPid;
+        private final int mExpectedFd;
+        private int mExpectedImportance;
+        private int mCurrentImportance = RunningAppProcessInfo.IMPORTANCE_GONE;
+
+        UidImportanceListener(int uid, int gid, int pid, int fd, int importance) {
+            mExpectedUid = uid;
+            mExpectedGid = gid;
+            mExpectedPid = pid;
+            mExpectedFd = fd;
+            mExpectedImportance = importance;
+        }
+
+        @Override
+        public void onUidImportance(int uid, int importance) {
+            if (uid == mExpectedUid) {
+                mCurrentImportance = importance;
+
+                /**
+                 * 1) If the process status changes to foreground, send a notification
+                 * for user consent.
+                 * 2) If the process status remains background, we decline logd access request.
+                 **/
+                if (importance <= RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE) {
+                    String clientInfo = getClientInfo(uid, mExpectedGid, mExpectedPid, mExpectedFd);
+                    sendNotification(0, clientInfo, uid, mExpectedGid, mExpectedPid,
+                            mExpectedFd);
+                    mActivityManager.removeOnUidImportanceListener(this);
+
+                    synchronized (LogcatManagerService.this) {
+                        sUidImportanceListenerCount--;
+                    }
+                } else {
+                    try {
+                        getLogdService().decline(uid, mExpectedGid, mExpectedPid, mExpectedFd);
+                    } catch (RemoteException ex) {
+                        Slog.e(TAG, "Fails to call remote functions ", ex);
+                    }
+                }
+            }
+        }
+    }
+
+    private static String getClientInfo(int uid, int gid, int pid, int fd) {
+        return "UID=" + Integer.toString(uid) + " GID=" + Integer.toString(gid) + " PID="
+            + Integer.toString(pid) + " FD=" + Integer.toString(fd);
     }
 
     private class LogdMonitor implements Runnable {
@@ -74,9 +318,7 @@
         }
 
         /**
-         * The current version grant the permission by default.
-         * And track the logd access.
-         * The next version will generate a prompt for users.
+         * LogdMonitor generates a prompt for users.
          * The users decide whether the logd access is allowed.
          */
         @Override
@@ -86,11 +328,14 @@
             }
 
             if (mStart) {
+
+                // TODO Temporarily approve all the requests to unblock testing failures.
                 try {
-                    mLogdService.approve(mUid, mGid, mPid, mFd);
-                } catch (RemoteException ex) {
-                    Slog.e(TAG, "Fails to call remote functions ", ex);
+                    getLogdService().approve(mUid, mGid, mPid, mFd);
+                } catch (RemoteException e) {
+                    e.printStackTrace();
                 }
+                return;
             }
         }
     }
@@ -100,6 +345,8 @@
         mContext = context;
         mBinderService = new BinderService();
         mThreadExecutor = Executors.newCachedThreadPool();
+        mActivityManager = context.getSystemService(ActivityManager.class);
+        mNotificationManager = mContext.getSystemService(NotificationManager.class);
     }
 
     @Override
@@ -114,5 +361,4 @@
     private void addLogdService() {
         mLogdService = ILogd.Stub.asInterface(ServiceManager.getService("logd"));
     }
-
 }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 240ed77..8d05415 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -63,7 +63,6 @@
 import static android.net.INetd.FIREWALL_RULE_DENY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -176,11 +175,9 @@
 import android.net.NetworkPolicyManager;
 import android.net.NetworkPolicyManager.UidState;
 import android.net.NetworkRequest;
-import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
 import android.net.NetworkStateSnapshot;
 import android.net.NetworkTemplate;
-import android.net.TelephonyNetworkSpecifier;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.os.BestClock;
@@ -1520,7 +1517,8 @@
                     .setType(TYPE_MOBILE)
                     .setSubscriberId(subscriberId)
                     .setMetered(true)
-                    .setDefaultNetwork(true).build();
+                    .setDefaultNetwork(true)
+                    .setSubId(subId).build();
             if (template.matches(probeIdent)) {
                 return subId;
             }
@@ -1757,7 +1755,8 @@
                 .setType(TYPE_MOBILE)
                 .setSubscriberId(subscriberId)
                 .setMetered(true)
-                .setDefaultNetwork(true).build();
+                .setDefaultNetwork(true)
+                .setSubId(subId).build();
         for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
             final NetworkTemplate template = mNetworkPolicy.keyAt(i);
             if (template.matches(probeIdent)) {
@@ -1989,7 +1988,8 @@
                             .setType(TYPE_MOBILE)
                             .setSubscriberId(subscriberId)
                             .setMetered(true)
-                            .setDefaultNetwork(true).build();
+                            .setDefaultNetwork(true)
+                            .setSubId(subId).build();
                     // Template is matched when subscriber id matches.
                     if (template.matches(probeIdent)) {
                         matchingSubIds.add(subId);
@@ -2091,7 +2091,8 @@
         mNetIdToSubId.clear();
         final ArrayMap<NetworkStateSnapshot, NetworkIdentity> identified = new ArrayMap<>();
         for (final NetworkStateSnapshot snapshot : snapshots) {
-            mNetIdToSubId.put(snapshot.getNetwork().getNetId(), parseSubId(snapshot));
+            final int subId = snapshot.getSubId();
+            mNetIdToSubId.put(snapshot.getNetwork().getNetId(), subId);
 
             // Policies matched by NPMS only match by subscriber ID or by network ID.
             final NetworkIdentity ident = new NetworkIdentity.Builder()
@@ -2296,7 +2297,8 @@
                 .setType(TYPE_MOBILE)
                 .setSubscriberId(subscriberId)
                 .setMetered(true)
-                .setDefaultNetwork(true).build();
+                .setDefaultNetwork(true)
+                .setSubId(subId).build();
         for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
             final NetworkTemplate template = mNetworkPolicy.keyAt(i);
             if (template.matches(probeIdent)) {
@@ -5919,17 +5921,6 @@
         }
     }
 
-    private int parseSubId(@NonNull NetworkStateSnapshot snapshot) {
-        int subId = INVALID_SUBSCRIPTION_ID;
-        if (snapshot.getNetworkCapabilities().hasTransport(TRANSPORT_CELLULAR)) {
-            NetworkSpecifier spec = snapshot.getNetworkCapabilities().getNetworkSpecifier();
-            if (spec instanceof TelephonyNetworkSpecifier) {
-                subId = ((TelephonyNetworkSpecifier) spec).getSubscriptionId();
-            }
-        }
-        return subId;
-    }
-
     @GuardedBy("mNetworkPoliciesSecondLock")
     private int getSubIdLocked(Network network) {
         return mNetIdToSubId.get(network.getNetId(), INVALID_SUBSCRIPTION_ID);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2717f0c..3d34976 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5805,6 +5805,7 @@
                             || channel.isImportanceLockedByCriticalDeviceFunction());
             final StatusBarNotification adjustedSbn = notificationRecord.getSbn();
             userId = adjustedSbn.getUser().getIdentifier();
+            int uid =  adjustedSbn.getUid();
             ArrayMap<String, String> summaries = mAutobundledSummaries.get(userId);
             if (summaries == null) {
                 summaries = new ArrayMap<>();
@@ -5851,7 +5852,7 @@
                         notificationRecord.getIsAppImportanceLocked());
                 summaries.put(pkg, summarySbn.getKey());
             }
-            if (summaryRecord != null && checkDisqualifyingFeatures(userId, MY_UID,
+            if (summaryRecord != null && checkDisqualifyingFeatures(userId, uid,
                     summaryRecord.getSbn().getId(), summaryRecord.getSbn().getTag(), summaryRecord,
                     true)) {
                 return summaryRecord;
@@ -6527,7 +6528,7 @@
 
     @VisibleForTesting
     protected void fixNotification(Notification notification, String pkg, String tag, int id,
-            int userId) throws NameNotFoundException {
+            int userId) throws NameNotFoundException, RemoteException {
         final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
                 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                 (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
@@ -6561,6 +6562,21 @@
             actions.toArray(notification.actions);
         }
 
+        // Ensure MediaStyle has correct permissions for remote device extras
+        if (notification.isStyle(Notification.MediaStyle.class)) {
+            int hasMediaContentControlPermission = mPackageManager.checkPermission(
+                    android.Manifest.permission.MEDIA_CONTENT_CONTROL, pkg, userId);
+            if (hasMediaContentControlPermission != PERMISSION_GRANTED) {
+                notification.extras.remove(Notification.EXTRA_MEDIA_REMOTE_DEVICE);
+                notification.extras.remove(Notification.EXTRA_MEDIA_REMOTE_ICON);
+                notification.extras.remove(Notification.EXTRA_MEDIA_REMOTE_INTENT);
+                if (DBG) {
+                    Slog.w(TAG, "Package " + pkg + ": Use of setRemotePlayback requires the "
+                            + "MEDIA_CONTENT_CONTROL permission");
+                }
+            }
+        }
+
         // Remote views? Are they too big?
         checkRemoteViews(pkg, tag, id, notification);
     }
@@ -6822,7 +6838,6 @@
             return false;
         }
 
-
         // blocked apps
         boolean isBlocked = !areNotificationsEnabledForPackageInt(pkg, uid);
         synchronized (mNotificationLock) {
@@ -7215,10 +7230,12 @@
                 if (mAssistants.isEnabled()) {
                     mAssistants.onNotificationEnqueuedLocked(r);
                     mHandler.postDelayed(
-                            new PostNotificationRunnable(r.getKey(), enqueueElapsedTimeMs),
+                            new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                                    r.getUid(), enqueueElapsedTimeMs),
                             DELAY_FOR_ASSISTANT_TIME);
                 } else {
-                    mHandler.post(new PostNotificationRunnable(r.getKey(), enqueueElapsedTimeMs));
+                    mHandler.post(new PostNotificationRunnable(r.getKey(),
+                            r.getSbn().getPackageName(), r.getUid(), enqueueElapsedTimeMs));
                 }
             }
         }
@@ -7242,16 +7259,19 @@
     protected class PostNotificationRunnable implements Runnable {
         private final String key;
         private final long postElapsedTimeMs;
+        private final String pkg;
+        private final int uid;
 
-        PostNotificationRunnable(String key, @ElapsedRealtimeLong long postElapsedTimeMs) {
+        PostNotificationRunnable(String key, String pkg, int uid,
+                @ElapsedRealtimeLong long postElapsedTimeMs) {
             this.key = key;
+            this.pkg = pkg;
+            this.uid = uid;
             this.postElapsedTimeMs = postElapsedTimeMs;
         }
 
         @Override
         public void run() {
-            String pkg = StatusBarNotification.getPkgFromKey(key);
-            int uid = StatusBarNotification.getUidFromKey(key);
             boolean appBanned = !areNotificationsEnabledForPackageInt(pkg, uid);
             synchronized (mNotificationLock) {
                 try {
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
index c2b3cbc..0320818 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
@@ -20,6 +20,8 @@
 import android.annotation.Nullable;
 import android.content.pm.ActivityInfo;
 
+import java.util.Set;
+
 /** @hide **/
 //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
 public interface ParsedActivity extends ParsedMainComponent {
@@ -59,6 +61,12 @@
     @Nullable
     String getPermission();
 
+    /**
+     * Gets the trusted host certificates of apps that are allowed to embed this activity.
+     */
+    @NonNull
+    Set<String> getKnownActivityEmbeddingCerts();
+
     int getPersistableMode();
 
     int getPrivateFlags();
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
index 91c0b07..acd5a81 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
@@ -23,6 +23,7 @@
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
 
 import static com.android.server.pm.pkg.parsing.ParsingPackageImpl.sForInternedString;
+import static com.android.server.pm.pkg.parsing.ParsingPackageImpl.sForStringSet;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -33,12 +34,17 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
+import android.util.ArraySet;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DataClass;
 import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
 import com.android.server.pm.pkg.parsing.ParsingUtils;
 
+import java.util.Collections;
+import java.util.Locale;
+import java.util.Set;
+
 /**
  * @hide
  **/
@@ -63,6 +69,8 @@
     @Nullable
     @DataClass.ParcelWith(ForInternedString.class)
     private String permission;
+    @Nullable
+    private Set<String> mKnownActivityEmbeddingCerts;
 
     private int launchMode;
     private int documentLaunchMode;
@@ -113,6 +121,7 @@
         this.rotationAnimation = other.rotationAnimation;
         this.colorMode = other.colorMode;
         this.windowLayout = other.windowLayout;
+        this.mKnownActivityEmbeddingCerts = other.mKnownActivityEmbeddingCerts;
     }
 
     /**
@@ -239,6 +248,25 @@
         return this;
     }
 
+    @NonNull
+    @Override
+    public Set<String> getKnownActivityEmbeddingCerts() {
+        return mKnownActivityEmbeddingCerts == null ? Collections.emptySet()
+                : mKnownActivityEmbeddingCerts;
+    }
+
+    /**
+     * Sets the trusted host certificates of apps that are allowed to embed this activity.
+     */
+    public void setKnownActivityEmbeddingCerts(@NonNull Set<String> knownActivityEmbeddingCerts) {
+        // Convert the provided digest to upper case for consistent Set membership
+        // checks when verifying the signing certificate digests of requesting apps.
+        this.mKnownActivityEmbeddingCerts = new ArraySet<>();
+        for (String knownCert : knownActivityEmbeddingCerts) {
+            this.mKnownActivityEmbeddingCerts.add(knownCert.toUpperCase(Locale.US));
+        }
+    }
+
     public String toString() {
         StringBuilder sb = new StringBuilder(128);
         sb.append("Activity{");
@@ -287,6 +315,7 @@
         } else {
             dest.writeBoolean(false);
         }
+        sForStringSet.parcel(this.mKnownActivityEmbeddingCerts, dest, flags);
     }
 
     public ParsedActivityImpl() {
@@ -320,6 +349,7 @@
         if (in.readBoolean()) {
             windowLayout = new ActivityInfo.WindowLayout(in);
         }
+        this.mKnownActivityEmbeddingCerts = sForStringSet.unparcel(in);
     }
 
     @NonNull
@@ -344,7 +374,7 @@
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -360,6 +390,7 @@
             @Nullable String taskAffinity,
             int privateFlags,
             @Nullable String permission,
+            @Nullable Set<String> knownActivityEmbeddingCerts,
             int launchMode,
             int documentLaunchMode,
             int maxRecents,
@@ -383,6 +414,7 @@
         this.taskAffinity = taskAffinity;
         this.privateFlags = privateFlags;
         this.permission = permission;
+        this.mKnownActivityEmbeddingCerts = knownActivityEmbeddingCerts;
         this.launchMode = launchMode;
         this.documentLaunchMode = documentLaunchMode;
         this.maxRecents = maxRecents;
@@ -645,10 +677,10 @@
     }
 
     @DataClass.Generated(
-            time = 1641431949361L,
+            time = 1644372875433L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityImpl.java",
-            inputSignatures = "private  int theme\nprivate  int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate  int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate  int launchMode\nprivate  int documentLaunchMode\nprivate  int maxRecents\nprivate  int configChanges\nprivate  int softInputMode\nprivate  int persistableMode\nprivate  int lockTaskLaunchMode\nprivate  int screenOrientation\nprivate  int resizeMode\nprivate  float maxAspectRatio\nprivate  float minAspectRatio\nprivate  boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate  int rotationAnimation\nprivate  int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<android.content.pm.parsing.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull android.content.pm.parsing.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull android.content.pm.parsing.component.ParsedActivityImpl makeAlias(java.lang.String,android.content.pm.parsing.component.ParsedActivity)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic  android.content.pm.parsing.component.ParsedActivityImpl setPermission(java.lang.String)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends android.content.pm.parsing.component.ParsedMainComponentImpl implements [android.content.pm.parsing.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+            sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java",
+            inputSignatures = "private  int theme\nprivate  int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate  int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate  int launchMode\nprivate  int documentLaunchMode\nprivate  int maxRecents\nprivate  int configChanges\nprivate  int softInputMode\nprivate  int persistableMode\nprivate  int lockTaskLaunchMode\nprivate  int screenOrientation\nprivate  int resizeMode\nprivate  float maxAspectRatio\nprivate  float minAspectRatio\nprivate  boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate  int rotationAnimation\nprivate  int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.server.pm.pkg.component.ParsedActivity)\npublic  com.android.server.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic  com.android.server.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic  com.android.server.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic  com.android.server.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic  void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic  java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.server.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
index a6c22a18..bbbf598 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
@@ -21,6 +21,7 @@
 
 import static com.android.server.pm.pkg.component.ComponentParseUtils.flag;
 import static com.android.server.pm.pkg.parsing.ParsingUtils.NOT_SET;
+import static com.android.server.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -151,7 +152,8 @@
                                         | flag(ActivityInfo.FLAG_SHOW_WHEN_LOCKED, R.styleable.AndroidManifestActivity_showWhenLocked, sa)
                                         | flag(ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE, R.styleable.AndroidManifestActivity_supportsPictureInPicture, sa)
                                         | flag(ActivityInfo.FLAG_TURN_SCREEN_ON, R.styleable.AndroidManifestActivity_turnScreenOn, sa)
-                                        | flag(ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING, R.styleable.AndroidManifestActivity_preferMinimalPostProcessing, sa)));
+                                        | flag(ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING, R.styleable.AndroidManifestActivity_preferMinimalPostProcessing, sa))
+                                        | flag(ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING, R.styleable.AndroidManifestActivity_allowUntrustedActivityEmbedding, sa));
 
                 activity.setPrivateFlags(activity.getPrivateFlags() | (flag(ActivityInfo.FLAG_INHERIT_SHOW_WHEN_LOCKED,
                                         R.styleable.AndroidManifestActivity_inheritShowWhenLocked, sa)
@@ -338,6 +340,20 @@
             activity.setPermission(permission != null ? permission : pkg.getPermission());
         }
 
+        final ParseResult<Set<String>> knownActivityEmbeddingCertsResult =
+                parseKnownActivityEmbeddingCerts(array, resources, isAlias
+                        ? R.styleable.AndroidManifestActivityAlias_knownActivityEmbeddingCerts
+                        : R.styleable.AndroidManifestActivity_knownActivityEmbeddingCerts, input);
+        if (knownActivityEmbeddingCertsResult.isError()) {
+            return input.error(knownActivityEmbeddingCertsResult);
+        } else {
+            final Set<String> knownActivityEmbeddingCerts = knownActivityEmbeddingCertsResult
+                    .getResult();
+            if (knownActivityEmbeddingCerts != null) {
+                activity.setKnownActivityEmbeddingCerts(knownActivityEmbeddingCerts);
+            }
+        }
+
         final boolean setExported = array.hasValue(exportedAttr);
         if (setExported) {
             activity.setExported(array.getBoolean(exportedAttr, false));
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
index b92b845..f199841 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/PackageInfoWithoutStateUtils.java
@@ -549,6 +549,7 @@
             ai.metaData = a.getMetaData();
         }
         ai.applicationInfo = applicationInfo;
+        ai.setKnownActivityEmbeddingCerts(a.getKnownActivityEmbeddingCerts());
         return ai;
     }
 
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
index 52d9b7a..1754877 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
@@ -381,6 +381,12 @@
 
     ParsingPackage setLocaleConfigRes(int localeConfigRes);
 
+    /**
+     * Sets the trusted host certificates of apps that are allowed to embed activities of this
+     * application.
+     */
+    ParsingPackage setKnownActivityEmbeddingCerts(Set<String> knownActivityEmbeddingCerts);
+
     // TODO(b/135203078): This class no longer has access to ParsedPackage, find a replacement
     //  for moving to the next step
     @CallSuper
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
index 84422cd..c4b1275 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java
@@ -563,6 +563,9 @@
         return (mBooleans & flag) != 0;
     }
 
+    @Nullable
+    private Set<String> mKnownActivityEmbeddingCerts;
+
     // Derived fields
     @NonNull
     private UUID mStorageUuid;
@@ -1150,6 +1153,9 @@
         appInfo.setVersionCode(mLongVersionCode);
         appInfo.setAppClassNamesByProcess(buildAppClassNamesByProcess());
         appInfo.setLocaleConfigRes(mLocaleConfigRes);
+        if (mKnownActivityEmbeddingCerts != null) {
+            appInfo.setKnownActivityEmbeddingCerts(mKnownActivityEmbeddingCerts);
+        }
 
         return appInfo;
     }
@@ -1329,6 +1335,7 @@
         dest.writeInt(this.nativeHeapZeroInitialized);
         sForBoolean.parcel(this.requestRawExternalStorageAccess, dest, flags);
         dest.writeInt(this.mLocaleConfigRes);
+        sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
     }
 
     public ParsingPackageImpl(Parcel in) {
@@ -1477,6 +1484,7 @@
         this.nativeHeapZeroInitialized = in.readInt();
         this.requestRawExternalStorageAccess = sForBoolean.unparcel(in);
         this.mLocaleConfigRes = in.readInt();
+        this.mKnownActivityEmbeddingCerts = sForStringSet.unparcel(in);
         assignDerivedFields();
     }
 
@@ -2376,6 +2384,13 @@
         return getBoolean(Booleans.RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED);
     }
 
+    @NonNull
+    @Override
+    public Set<String> getKnownActivityEmbeddingCerts() {
+        return mKnownActivityEmbeddingCerts == null ? Collections.emptySet()
+                : mKnownActivityEmbeddingCerts;
+    }
+
     @Override
     public boolean shouldInheritKeyStoreKeys() {
         return getBoolean(Booleans.INHERIT_KEYSTORE_KEYS);
@@ -2973,4 +2988,11 @@
         mLocaleConfigRes = value;
         return this;
     }
+
+    @Override
+    public ParsingPackage setKnownActivityEmbeddingCerts(
+            @Nullable Set<String> knownEmbeddingCerts) {
+        mKnownActivityEmbeddingCerts = knownEmbeddingCerts;
+        return this;
+    }
 }
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 e8f03ff..a891980 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
@@ -30,6 +30,8 @@
 import static android.os.Build.VERSION_CODES.O;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 
+import static com.android.server.pm.pkg.parsing.ParsingUtils.parseKnownActivityEmbeddingCerts;
+
 import android.annotation.AnyRes;
 import android.annotation.CheckResult;
 import android.annotation.IntDef;
@@ -2009,6 +2011,19 @@
                                 .AndroidManifestApplication_requestForegroundServiceExemption,
                         false));
             }
+            final ParseResult<Set<String>> knownActivityEmbeddingCertsResult =
+                    parseKnownActivityEmbeddingCerts(sa, res,
+                            R.styleable.AndroidManifestApplication_knownActivityEmbeddingCerts,
+                            input);
+            if (knownActivityEmbeddingCertsResult.isError()) {
+                return input.error(knownActivityEmbeddingCertsResult);
+            } else {
+                final Set<String> knownActivityEmbeddingCerts = knownActivityEmbeddingCertsResult
+                        .getResult();
+                if (knownActivityEmbeddingCerts != null) {
+                    pkg.setKnownActivityEmbeddingCerts(knownActivityEmbeddingCerts);
+                }
+            }
         } finally {
             sa.recycle();
         }
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
index 9430e98..cb474df 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingUtils.java
@@ -22,6 +22,8 @@
 import android.annotation.Nullable;
 import android.content.pm.parsing.result.ParseInput;
 import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -38,6 +40,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 /** @hide **/
 public class ParsingUtils {
@@ -168,4 +171,50 @@
             return list;
         }
     }
+
+    /**
+     * Parse the {@link android.R.attr#knownActivityEmbeddingCerts} attribute, if available.
+     */
+    @NonNull
+    public static ParseResult<Set<String>> parseKnownActivityEmbeddingCerts(@NonNull TypedArray sa,
+            @NonNull Resources res, int resourceId, @NonNull ParseInput input) {
+        if (!sa.hasValue(resourceId)) {
+            return input.success(null);
+        }
+
+        final int knownActivityEmbeddingCertsResource = sa.getResourceId(resourceId, 0);
+        if (knownActivityEmbeddingCertsResource != 0) {
+            // The knownCerts attribute supports both a string array resource as well as a
+            // string resource for the case where the permission should only be granted to a
+            // single known signer.
+            Set<String> knownEmbeddingCertificates = null;
+            final String resourceType = res.getResourceTypeName(
+                    knownActivityEmbeddingCertsResource);
+            if (resourceType.equals("array")) {
+                final String[] knownCerts = res.getStringArray(knownActivityEmbeddingCertsResource);
+                if (knownCerts != null) {
+                    knownEmbeddingCertificates = Set.of(knownCerts);
+                }
+            } else {
+                final String knownCert = res.getString(knownActivityEmbeddingCertsResource);
+                if (knownCert != null) {
+                    knownEmbeddingCertificates = Set.of(knownCert);
+                }
+            }
+            if (knownEmbeddingCertificates == null || knownEmbeddingCertificates.isEmpty()) {
+                return input.error("Defined a knownActivityEmbeddingCerts attribute but the "
+                        + "provided resource is null");
+            }
+            return input.success(knownEmbeddingCertificates);
+        }
+
+        // If the knownCerts resource ID is null - the app specified a string value for the
+        // attribute representing a single trusted signer.
+        final String knownCert = sa.getString(resourceId);
+        if (knownCert == null || knownCert.isEmpty()) {
+            return input.error("Defined a knownActivityEmbeddingCerts attribute but the provided "
+                    + "string is empty");
+        }
+        return input.success(Set.of(knownCert));
+    }
 }
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStateAppInfo.java b/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStateAppInfo.java
index 2ef90ac..3205b76 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStateAppInfo.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/PkgWithoutStateAppInfo.java
@@ -21,6 +21,8 @@
 import android.content.pm.ApplicationInfo;
 import android.util.SparseArray;
 
+import java.util.Set;
+
 /**
  * Container for fields that are eventually exposed through {@link ApplicationInfo}.
  * <p>
@@ -285,6 +287,13 @@
     String getPermission();
 
     /**
+     * @see ApplicationInfo#knownActivityEmbeddingCerts
+     * @see R.styleable#AndroidManifestApplication_knownActivityEmbeddingCerts
+     */
+    @NonNull
+    Set<String> getKnownActivityEmbeddingCerts();
+
+    /**
      * @see ApplicationInfo#processName
      * @see R.styleable#AndroidManifestApplication_process
      */
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 49f759d..adee325 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -4491,6 +4491,9 @@
     }
 
     int pullMediaCapabilitiesStats(int atomTag, List<StatsEvent> pulledData) {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+            return StatsManager.PULL_SKIP;
+        }
         AudioManager audioManager = mContext.getSystemService(AudioManager.class);
         if (audioManager == null) {
             return StatsManager.PULL_SKIP;
diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
index 27c0bee..10e868d 100644
--- a/services/core/java/com/android/server/tracing/TracingServiceProxy.java
+++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
@@ -15,6 +15,13 @@
  */
 package com.android.server.tracing;
 
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_BEGIN;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_BIND_PERM_INCORRECT;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_COMM_ERROR;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_HANDOFF;
+import static com.android.internal.util.FrameworkStatsLog.TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_PERM_MISSING;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.content.ComponentName;
@@ -39,6 +46,7 @@
 import android.util.Slog;
 
 import com.android.internal.infra.ServiceConnector;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.SystemService;
 
 import java.io.IOException;
@@ -71,6 +79,17 @@
     private static final String INTENT_ACTION_NOTIFY_SESSION_STOLEN =
             "com.android.traceur.NOTIFY_SESSION_STOLEN";
 
+    private static final int REPORT_BEGIN =
+            TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_BEGIN;
+    private static final int REPORT_SVC_HANDOFF =
+            TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_HANDOFF;
+    private static final int REPORT_BIND_PERM_INCORRECT =
+            TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_BIND_PERM_INCORRECT;
+    private static final int REPORT_SVC_PERM_MISSING =
+            TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_PERM_MISSING;
+    private static final int REPORT_SVC_COMM_ERROR =
+            TRACING_SERVICE_REPORT_EVENT__EVENT__TRACING_SERVICE_REPORT_SVC_COMM_ERROR;
+
     private final Context mContext;
     private final PackageManager mPackageManager;
     private final LruCache<ComponentName, ServiceConnector<IMessenger>> mCachedReporterServices;
@@ -134,17 +153,24 @@
     }
 
     private void reportTrace(@NonNull TraceReportParams params) {
+        FrameworkStatsLog.write(TRACING_SERVICE_REPORT_EVENT, REPORT_BEGIN,
+                params.uuidLsb, params.uuidMsb);
+
         // We don't need to do any permission checks on the caller because access
         // to this service is guarded by SELinux.
         ComponentName component = new ComponentName(params.reporterPackageName,
                 params.reporterClassName);
         if (!hasBindServicePermission(component)) {
+            FrameworkStatsLog.write(TRACING_SERVICE_REPORT_EVENT, REPORT_BIND_PERM_INCORRECT,
+                    params.uuidLsb, params.uuidMsb);
             return;
         }
         boolean hasDumpPermission = hasPermission(component, Manifest.permission.DUMP);
         boolean hasUsageStatsPermission = hasPermission(component,
                 Manifest.permission.PACKAGE_USAGE_STATS);
         if (!hasDumpPermission || !hasUsageStatsPermission) {
+            FrameworkStatsLog.write(TRACING_SERVICE_REPORT_EVENT, REPORT_SVC_PERM_MISSING,
+                    params.uuidLsb, params.uuidMsb);
             return;
         }
         final long ident = Binder.clearCallingIdentity();
@@ -178,8 +204,13 @@
             message.what = TraceReportService.MSG_REPORT_TRACE;
             message.obj = params;
             messenger.send(message);
+
+            FrameworkStatsLog.write(TRACING_SERVICE_REPORT_EVENT, REPORT_SVC_HANDOFF,
+                    params.uuidLsb, params.uuidMsb);
         }).whenComplete((res, err) -> {
             if (err != null) {
+                FrameworkStatsLog.write(TRACING_SERVICE_REPORT_EVENT, REPORT_SVC_COMM_ERROR,
+                        params.uuidLsb, params.uuidMsb);
                 Slog.e(TAG, "Failed to report trace", err);
             }
             try {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 56985af..87c8a79 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -429,7 +429,8 @@
         if (needsExtraction) {
             extractColors(wallpaper);
         }
-        notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId);
+        notifyColorListeners(getAdjustedWallpaperColorsOnDimming(wallpaper), which,
+                wallpaper.userId, displayId);
     }
 
     private static <T extends IInterface> boolean emptyCallbackList(RemoteCallbackList<T> list) {
@@ -1519,7 +1520,6 @@
                 if (mWallpaper.mWallpaperDimAmount != 0f) {
                     try {
                         connector.mEngine.applyDimming(mWallpaper.mWallpaperDimAmount);
-                        notifyWallpaperColorsChanged(mWallpaper, FLAG_SYSTEM);
                     } catch (RemoteException e) {
                         Slog.w(TAG, "Failed to dim wallpaper", e);
                     }
@@ -2711,8 +2711,31 @@
             extractColors(wallpaperData);
         }
 
+        return getAdjustedWallpaperColorsOnDimming(wallpaperData);
+    }
+
+    /**
+     * Gets the adjusted {@link WallpaperColors} if the wallpaper colors were not extracted from
+     * bitmap (i.e. it's a live wallpaper) and the dim amount is not 0. If these conditions apply,
+     * default to using color hints that do not support dark theme and dark text.
+     *
+     * @param wallpaperData WallpaperData containing the WallpaperColors and mWallpaperDimAmount
+     */
+    WallpaperColors getAdjustedWallpaperColorsOnDimming(WallpaperData wallpaperData) {
         synchronized (mLock) {
-            return wallpaperData.primaryColors;
+            WallpaperColors wallpaperColors = wallpaperData.primaryColors;
+
+            if (wallpaperColors != null
+                    && (wallpaperColors.getColorHints() & WallpaperColors.HINT_FROM_BITMAP) == 0
+                    && wallpaperData.mWallpaperDimAmount != 0f) {
+                int adjustedColorHints = wallpaperColors.getColorHints()
+                        & ~WallpaperColors.HINT_SUPPORTS_DARK_TEXT
+                        & ~WallpaperColors.HINT_SUPPORTS_DARK_THEME;
+                return new WallpaperColors(
+                        wallpaperColors.getPrimaryColor(), wallpaperColors.getSecondaryColor(),
+                        wallpaperColors.getTertiaryColor(), adjustedColorHints);
+            }
+            return wallpaperColors;
         }
     }
 
@@ -2782,6 +2805,7 @@
                     wallpaper.fromForegroundApp = fromForegroundApp;
                     wallpaper.cropHint.set(cropHint);
                     wallpaper.allowBackup = allowBackup;
+                    wallpaper.mWallpaperDimAmount = getWallpaperDimAmount();
                 }
                 return pfd;
             } finally {
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index ec4c58f..d526845 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1088,7 +1088,7 @@
             final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
             if (r != null && r.isState(RESUMED, PAUSING)) {
                 r.mDisplayContent.mAppTransition.overridePendingAppTransition(
-                        packageName, enterAnim, exitAnim, null, null,
+                        packageName, enterAnim, exitAnim, backgroundColor, null, null,
                         r.mOverrideTaskTransition);
                 r.mTransitionController.setOverrideAnimation(
                         TransitionInfo.AnimationOptions.makeCustomAnimOptions(packageName,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index fd58936..6ef6c7a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4516,6 +4516,7 @@
                         pendingOptions.getPackageName(),
                         pendingOptions.getCustomEnterResId(),
                         pendingOptions.getCustomExitResId(),
+                        pendingOptions.getCustomBackgroundColor(),
                         pendingOptions.getAnimationStartedListener(),
                         pendingOptions.getAnimationFinishedListener(),
                         pendingOptions.getOverrideTaskTransition());
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 5164bf0..fb3d17a 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2003,8 +2003,8 @@
      * @param targetTask the target task for launching activity, which could be different from
      *                   the one who hosting the embedding.
      */
-    private boolean canEmbedActivity(@NonNull TaskFragment taskFragment, ActivityRecord starting,
-            boolean newTask, Task targetTask) {
+    private boolean canEmbedActivity(@NonNull TaskFragment taskFragment,
+            @NonNull ActivityRecord starting, boolean newTask, Task targetTask) {
         final Task hostTask = taskFragment.getTask();
         if (hostTask == null) {
             return false;
@@ -2016,8 +2016,7 @@
             return true;
         }
 
-        // Not allowed embedding an activity of another app.
-        if (hostUid != starting.getUid()) {
+        if (!taskFragment.isAllowedToEmbedActivity(starting)) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java
index 5899a4e..a743091 100644
--- a/services/core/java/com/android/server/wm/AnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/AnimationAdapter.java
@@ -42,6 +42,22 @@
     boolean getShowWallpaper();
 
     /**
+     * @return Whether we should show a background behind the animating windows.
+     * @see Animation#getShowBackground()
+     */
+    default boolean getShowBackground() {
+        return false;
+    }
+
+    /**
+     * @return The background color to use during an animation if getShowBackground returns true.
+     * @see Animation#getBackgroundColor()
+     */
+    default int getBackgroundColor() {
+        return 0;
+    }
+
+    /**
      * Requests to start the animation.
      *
      * @param animationLeash The surface to run the animation on. See {@link SurfaceAnimator} for an
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 56adcfd..05efb29 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -93,6 +93,7 @@
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_AFTER_ANIM;
 import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
 
+import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
@@ -198,6 +199,7 @@
     private IRemoteCallback mAnimationFinishedCallback;
     private int mNextAppTransitionEnter;
     private int mNextAppTransitionExit;
+    private @ColorInt int mNextAppTransitionBackgroundColor;
     private int mNextAppTransitionInPlace;
     private boolean mNextAppTransitionIsSync;
 
@@ -829,6 +831,7 @@
                     break;
             }
             a = animAttr != 0 ? loadAnimationAttr(lp, animAttr, transit) : null;
+
             ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                     "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b "
                             + "Callers=%s",
@@ -836,6 +839,11 @@
                     Debug.getCallers(3));
         }
         setAppTransitionFinishedCallbackIfNeeded(a);
+
+        if (mNextAppTransitionBackgroundColor != 0) {
+            a.setBackgroundColor(mNextAppTransitionBackgroundColor);
+        }
+
         return a;
     }
 
@@ -861,14 +869,15 @@
     }
 
     void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
-            IRemoteCallback startedCallback, IRemoteCallback endedCallback,
-            boolean overrideTaskTransaction) {
+            @ColorInt int backgroundColor, IRemoteCallback startedCallback,
+            IRemoteCallback endedCallback, boolean overrideTaskTransaction) {
         if (canOverridePendingAppTransition()) {
             clear();
             mNextAppTransitionOverrideRequested = true;
             mNextAppTransitionPackage = packageName;
             mNextAppTransitionEnter = enterAnim;
             mNextAppTransitionExit = exitAnim;
+            mNextAppTransitionBackgroundColor = backgroundColor;
             postAnimationCallback();
             mNextAppTransitionCallback = startedCallback;
             mAnimationFinishedCallback = endedCallback;
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 5a2cf17..afa4f19 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -544,6 +544,11 @@
         return getActivityType() == ACTIVITY_TYPE_RECENTS;
     }
 
+    final boolean isActivityTypeHomeOrRecents() {
+        final int activityType = getActivityType();
+        return activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
+    }
+
     public boolean isActivityTypeAssistant() {
         return getActivityType() == ACTIVITY_TYPE_ASSISTANT;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2c58d92..818e4f6 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4083,7 +4083,7 @@
             final boolean renewImeSurface = mImeScreenshot == null
                     || mImeScreenshot.getWidth() != mInputMethodWindow.getFrame().width()
                     || mImeScreenshot.getHeight() != mInputMethodWindow.getFrame().height();
-            if (task != null && !task.isHomeOrRecentsRootTask()) {
+            if (task != null && !task.isActivityTypeHomeOrRecents()) {
                 SurfaceControl.ScreenshotHardwareBuffer imeBuffer = renewImeSurface
                         ? mWmService.mTaskSnapshotController.snapshotImeFromAttachedTask(task)
                         : null;
diff --git a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
index a3eb980..61f9fe2 100644
--- a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
@@ -51,6 +51,16 @@
     }
 
     @Override
+    public boolean getShowBackground() {
+        return mSpec.getShowBackground();
+    }
+
+    @Override
+    public int getBackgroundColor() {
+        return mSpec.getBackgroundColor();
+    }
+
+    @Override
     public void startAnimation(SurfaceControl animationLeash, Transaction t,
             @AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
         mAnimator.startAnimation(mSpec, animationLeash, t,
@@ -97,6 +107,20 @@
         }
 
         /**
+         * @see AnimationAdapter#getShowBackground
+         */
+        default boolean getShowBackground() {
+            return false;
+        }
+
+        /**
+         * @see AnimationAdapter#getBackgroundColor
+         */
+        default int getBackgroundColor() {
+            return 0;
+        }
+
+        /**
          * @see AnimationAdapter#getStatusBarTransitionsStartTime
          */
         default long calculateStatusBarTransitionStartTime() {
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index d4a7a5d..e259238 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -529,7 +529,7 @@
      */
     void notifyTaskPersisterLocked(Task task, boolean flush) {
         final Task rootTask = task != null ? task.getRootTask() : null;
-        if (rootTask != null && rootTask.isHomeOrRecentsRootTask()) {
+        if (rootTask != null && rootTask.isActivityTypeHomeOrRecents()) {
             // Never persist the home or recents task.
             return;
         }
@@ -563,7 +563,7 @@
 
     private static boolean shouldPersistTaskLocked(Task task) {
         final Task rootTask = task.getRootTask();
-        return task.isPersistable && (rootTask == null || !rootTask.isHomeOrRecentsRootTask());
+        return task.isPersistable && (rootTask == null || !rootTask.isActivityTypeHomeOrRecents());
     }
 
     void onSystemReadyLocked() {
@@ -994,7 +994,7 @@
             }
             final Task rootTask = task.getRootTask();
             if ((task.isPersistable || task.inRecents)
-                    && (rootTask == null || !rootTask.isHomeOrRecentsRootTask())) {
+                    && (rootTask == null || !rootTask.isActivityTypeHomeOrRecents())) {
                 if (TaskPersister.DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
                 persistentTaskIds.add(task.mTaskId);
             } else {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index c1835a0..30906e5 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -613,7 +613,7 @@
             final TaskFragment adjacentTask = task.getRootTask().getAdjacentTaskFragment();
             final boolean inSplitScreen = task.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
                     && adjacentTask != null;
-            if (task.isHomeOrRecentsRootTask()
+            if (task.isActivityTypeHomeOrRecents()
                     // Skip if the task is in split screen and in landscape.
                     || (inSplitScreen && isDisplayLandscape)
                     // Skip if the task is the top task in split screen.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b84ef77..6df54cd 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2711,7 +2711,7 @@
         // they extend past their root task and sysui uses the root task surface to control
         // cropping.
         // TODO(b/158242495): get rid of this when drag/drop can use surface bounds.
-        if (isActivityTypeHome() || isActivityTypeRecents()) {
+        if (isActivityTypeHomeOrRecents()) {
             // Make sure this is the top-most non-organizer root task (if not top-most, it means
             // another translucent task could be above this, so this needs to stay cropped.
             final Task rootTask = getRootTask();
@@ -3306,7 +3306,7 @@
         if (control != null) {
             // We let the transition to be controlled by RecentsAnimation, and callback task's
             // RemoteAnimationTarget for remote runner to animate.
-            if (enter && !isHomeOrRecentsRootTask()) {
+            if (enter && !isActivityTypeHomeOrRecents()) {
                 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
                         "applyAnimationUnchecked, control: %s, task: %s, transit: %s",
                         control, asTask(), AppTransition.appTransitionOldToString(transit));
@@ -4591,10 +4591,6 @@
                 !PRESERVE_WINDOWS);
     }
 
-    final boolean isHomeOrRecentsRootTask() {
-        return isActivityTypeHome() || isActivityTypeRecents();
-    }
-
     final boolean isOnHomeDisplay() {
         return getDisplayId() == DEFAULT_DISPLAY;
     }
@@ -5044,7 +5040,7 @@
 
         // The transition animation and starting window are not needed if {@code allowMoveToFront}
         // is false, because the activity won't be visible.
-        if ((!isHomeOrRecentsRootTask() || hasActivity()) && allowMoveToFront) {
+        if ((!isActivityTypeHomeOrRecents() || hasActivity()) && allowMoveToFront) {
             final DisplayContent dc = mDisplayContent;
             if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                     "Prepare open transition: starting " + r);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index a59d7b6..c880aba 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -25,6 +25,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
 import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -95,6 +96,7 @@
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.util.function.pooled.PooledPredicate;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -102,6 +104,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -505,6 +508,56 @@
     }
 
     /**
+     * Checks if the organized task fragment is allowed to have the specified activity, which is
+     * allowed if an activity allows embedding in untrusted mode, or if the trusted mode can be
+     * enabled.
+     * @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
+     */
+    boolean isAllowedToEmbedActivity(@NonNull ActivityRecord a) {
+        if ((a.info.flags & FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING)
+                == FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING) {
+            return true;
+        }
+
+        return isAllowedToEmbedActivityInTrustedMode(a);
+    }
+
+    /**
+     * Checks if the organized task fragment is allowed to embed activity in fully trusted mode,
+     * which means that all transactions are allowed. This is supported in the following cases:
+     * <li>the activity belongs to the same app as the organizer host;</li>
+     * <li>the activity has declared the organizer host as trusted explicitly via known
+     * certificate.</li>
+     */
+    private boolean isAllowedToEmbedActivityInTrustedMode(@NonNull ActivityRecord a) {
+        if (mTaskFragmentOrganizerUid == a.getUid()) {
+            // Activities from the same UID can be embedded freely by the host.
+            return true;
+        }
+
+        Set<String> knownActivityEmbeddingCerts = a.info.getKnownActivityEmbeddingCerts();
+        if (knownActivityEmbeddingCerts.isEmpty()) {
+            // An application must either declare that it allows untrusted embedding, or specify
+            // a set of app certificates that are allowed to embed it in trusted mode.
+            return false;
+        }
+
+        AndroidPackage hostPackage = mAtmService.getPackageManagerInternalLocked()
+                .getPackage(mTaskFragmentOrganizerUid);
+
+        return hostPackage != null && hostPackage.getSigningDetails().hasAncestorOrSelfWithDigest(
+                knownActivityEmbeddingCerts);
+    }
+
+    /**
+     * Checks if all activities in the task fragment are allowed to be embedded in trusted mode.
+     * @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
+     */
+    boolean isAllowedToBeEmbeddedInTrustedMode() {
+        return forAllActivities(this::isAllowedToEmbedActivityInTrustedMode);
+    }
+
+    /**
      * Returns the TaskFragment that is being organized, which could be this or the ascendant
      * TaskFragment.
      */
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 123ca88..19f921d 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -294,14 +294,28 @@
         }
     }
 
-    /** Gets the {@link RemoteAnimationDefinition} set on the given organizer if exists. */
+    /**
+     * Gets the {@link RemoteAnimationDefinition} set on the given organizer if exists. Returns
+     * {@code null} if it doesn't, or if the organizer has activity(ies) embedded in untrusted mode.
+     */
     @Nullable
     public RemoteAnimationDefinition getRemoteAnimationDefinition(
             ITaskFragmentOrganizer organizer) {
         synchronized (mGlobalLock) {
             final TaskFragmentOrganizerState organizerState =
                     mTaskFragmentOrganizerState.get(organizer.asBinder());
-            return organizerState != null ? organizerState.mRemoteAnimationDefinition : null;
+            if (organizerState == null) {
+                return null;
+            }
+            for (TaskFragment tf : organizerState.mOrganizedTaskFragments) {
+                if (!tf.isAllowedToBeEmbeddedInTrustedMode()) {
+                    // Disable client-driven animations for organizer if at least one of the
+                    // embedded task fragments is not embedding in trusted mode.
+                    // TODO(b/197364677): replace with a stub or Shell-driven one instead of skip?
+                    return null;
+                }
+            }
+            return organizerState.mRemoteAnimationDefinition;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 26871c3..8b8fd2c 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -825,7 +825,7 @@
         // the bottom of the screen, so we need to animate it.
         for (int i = 0; i < mTargets.size(); ++i) {
             final Task task = mTargets.get(i).asTask();
-            if (task == null || !task.isHomeOrRecentsRootTask()) continue;
+            if (task == null || !task.isActivityTypeHomeOrRecents()) continue;
             animate = task.isVisibleRequested();
             break;
         }
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 073a508..5bfd546 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -80,6 +80,16 @@
     }
 
     @Override
+    public boolean getShowBackground() {
+        return mAnimation.getShowBackground();
+    }
+
+    @Override
+    public int getBackgroundColor() {
+        return mAnimation.getBackgroundColor();
+    }
+
+    @Override
     public long getDuration() {
         return mAnimation.computeDurationHint();
     }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 1bd305e..e1746cc 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -39,6 +39,7 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.AppTransition.MAX_APP_TRANSITION_DURATION;
+import static com.android.server.wm.AppTransition.isActivityTransitOld;
 import static com.android.server.wm.AppTransition.isTaskTransitOld;
 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
@@ -99,6 +100,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.graphics.ColorUtils;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.wm.SurfaceAnimator.Animatable;
@@ -2790,7 +2792,7 @@
             @TransitionOldType int transit, boolean isVoiceInteraction,
             @Nullable ArrayList<WindowContainer> sources) {
         final Task task = asTask();
-        if (task != null && !enter && !task.isHomeOrRecentsRootTask()) {
+        if (task != null && !enter && !task.isActivityTypeHomeOrRecents()) {
             final InsetsControlTarget imeTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
             final boolean isImeLayeringTarget = imeTarget != null && imeTarget.getWindow() != null
                     && imeTarget.getWindow().getTask() == task;
@@ -2820,6 +2822,25 @@
                 }
             }
 
+            final ActivityRecord activityRecord = asActivityRecord();
+            if (activityRecord != null && isActivityTransitOld(transit)
+                    && adapter.getShowBackground()) {
+                final @ColorInt int backgroundColorForTransition;
+                if (adapter.getBackgroundColor() != 0) {
+                    // If available use the background color provided through getBackgroundColor
+                    // which if set originates from a call to overridePendingAppTransition.
+                    backgroundColorForTransition = adapter.getBackgroundColor();
+                } else {
+                    // Otherwise default to the window's background color if provided through
+                    // the theme as the background color for the animation - the top most window
+                    // with a valid background color and showBackground set takes precedence.
+                    final Task arTask = activityRecord.getTask();
+                    backgroundColorForTransition = ColorUtils.setAlphaComponent(
+                            arTask.getTaskDescription().getBackgroundColor(), 255);
+                }
+                animationRunnerBuilder.setTaskBackgroundColor(backgroundColorForTransition);
+            }
+
             animationRunnerBuilder.build()
                     .startAnimation(getPendingTransaction(), adapter, !isVisible(),
                             ANIMATION_TYPE_APP_TRANSITION, thumbnailAdapter);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 1d5c184..4c7891b 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -19,6 +19,8 @@
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.app.ActivityManager.isStartResultSuccessful;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.WindowContainerTransaction.Change.CHANGE_BOUNDS_TRANSACTION;
+import static android.window.WindowContainerTransaction.Change.CHANGE_BOUNDS_TRANSACTION_RECT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT;
@@ -1224,10 +1226,14 @@
         while (entries.hasNext()) {
             final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
             // Only allow to apply changes to TaskFragment that is created by this organizer.
-            enforceTaskFragmentOrganized(func, WindowContainer.fromBinder(entry.getKey()),
-                    organizer);
+            WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
+            enforceTaskFragmentOrganized(func, wc, organizer);
+            enforceTaskFragmentConfigChangeAllowed(func, wc, entry.getValue(), organizer);
         }
 
+        // TODO(b/197364677): Enforce safety of hierarchy operations in untrusted mode. E.g. one
+        // could first change a trusted TF, and then start/reparent untrusted activity there.
+
         // Hierarchy changes
         final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
         for (int i = hops.size() - 1; i >= 0; i--) {
@@ -1297,6 +1303,57 @@
         }
     }
 
+    /**
+     * Makes sure that SurfaceControl transactions and the ability to set bounds outside of the
+     * parent bounds are not allowed for embedding without full trust between the host and the
+     * target.
+     * TODO(b/197364677): Allow SC transactions when the client-driven animations are protected from
+     * tapjacking.
+     */
+    private void enforceTaskFragmentConfigChangeAllowed(String func, @Nullable WindowContainer wc,
+            WindowContainerTransaction.Change change, ITaskFragmentOrganizer organizer) {
+        if (wc == null) {
+            Slog.e(TAG, "Attempt to operate on task fragment that no longer exists");
+            return;
+        }
+        // Check if TaskFragment is embedded in fully trusted mode
+        if (wc.asTaskFragment().isAllowedToBeEmbeddedInTrustedMode()) {
+            // Fully trusted, no need to check further
+            return;
+        }
+
+        if (change == null) {
+            return;
+        }
+        final int changeMask = change.getChangeMask();
+        if ((changeMask & (CHANGE_BOUNDS_TRANSACTION | CHANGE_BOUNDS_TRANSACTION_RECT)) != 0) {
+            String msg = "Permission Denial: " + func + " from pid="
+                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+                    + " trying to apply SurfaceControl changes to TaskFragment in non-trusted "
+                    + "embedding mode, TaskFragmentOrganizer=" + organizer;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+        if (change.getWindowSetMask() == 0) {
+            // Nothing else to check.
+            return;
+        }
+        WindowConfiguration requestedWindowConfig = change.getConfiguration().windowConfiguration;
+        WindowContainer wcParent = wc.getParent();
+        if (wcParent == null) {
+            Slog.e(TAG, "Attempt to set bounds on task fragment that has no parent");
+            return;
+        }
+        if (!wcParent.getBounds().contains(requestedWindowConfig.getBounds())) {
+            String msg = "Permission Denial: " + func + " from pid="
+                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+                    + " trying to apply bounds outside of parent for non-trusted host,"
+                    + " TaskFragmentOrganizer=" + organizer;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+    }
+
     void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
             @Nullable IBinder errorCallbackToken, @NonNull CallerInfo caller) {
         final ActivityRecord ownerActivity =
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index cc663d9..11300ce 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -508,7 +508,8 @@
             AndroidPackage::shouldInheritKeyStoreKeys,
             ParsingPackage::setInheritKeyStoreKeys,
             true
-        )
+        ),
+        getter(AndroidPackage::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT"))
     )
 
     override fun initialObject() = PackageImpl.forParsing(
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
index a89b717..5180786 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
@@ -53,7 +53,7 @@
         ParsedActivity::getTaskAffinity,
         ParsedActivity::getTheme,
         ParsedActivity::getUiOptions,
-        ParsedActivity::isSupportsSizeChanges,
+        ParsedActivity::isSupportsSizeChanges
     )
 
     override fun mainComponentSubclassExtraParams() = listOf(
@@ -73,6 +73,7 @@
                     ActivityInfo.WindowLayout::minHeight
                 )
             }
-        )
+        ),
+        getter(ParsedActivity::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT"))
     )
 }
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 fb8749e..816dbdb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -23,6 +23,7 @@
 import static android.app.ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED;
 import static android.app.ActivityManager.RESTRICTION_LEVEL_EXEMPTED;
 import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET;
+import static android.app.ActivityManager.isLowRamDeviceStatic;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_SYSTEM;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED_BY_USER;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
@@ -46,6 +47,7 @@
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.android.internal.notification.SystemNotificationChannels.ABUSIVE_BACKGROUND_APPS;
+import static com.android.server.am.AppBatteryTracker.AppBatteryPolicy.getFloatArray;
 import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_INDEX_BACKGROUND;
 import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND;
 import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND_SERVICE;
@@ -108,6 +110,7 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.R;
 import com.android.server.AppStateTracker;
 import com.android.server.DeviceIdleInternal;
 import com.android.server.am.AppBatteryExemptionTracker.AppBatteryExemptionPolicy;
@@ -554,28 +557,34 @@
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_MONITOR_ENABLED,
                     DeviceConfig::getBoolean,
-                    AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_MONITOR_ENABLED);
+                    mContext.getResources().getBoolean(
+                            R.bool.config_bg_current_drain_monitor_enabled));
             bgCurrentDrainMonitor.set(true);
 
             bgCurrentDrainWindow = new DeviceConfigSession<>(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_WINDOW,
                     DeviceConfig::getLong,
-                    AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_WINDOW_MS);
+                    (long) mContext.getResources().getInteger(
+                            R.integer.config_bg_current_drain_window));
             bgCurrentDrainWindow.set(windowMs);
 
             bgCurrentDrainRestrictedBucketThreshold = new DeviceConfigSession<>(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET,
                     DeviceConfig::getFloat,
-                    AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_THRESHOLD);
+                    getFloatArray(mContext.getResources().obtainTypedArray(
+                            R.array.config_bg_current_drain_threshold_to_restricted_bucket))[
+                            isLowRamDeviceStatic() ? 1 : 0]);
             bgCurrentDrainRestrictedBucketThreshold.set(restrictBucketThreshold);
 
             bgCurrentDrainBgRestrictedThreshold = new DeviceConfigSession<>(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_BG_RESTRICTED,
                     DeviceConfig::getFloat,
-                    AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_THRESHOLD);
+                    getFloatArray(mContext.getResources().obtainTypedArray(
+                            R.array.config_bg_current_drain_threshold_to_bg_restricted))[
+                            isLowRamDeviceStatic() ? 1 : 0]);
             bgCurrentDrainBgRestrictedThreshold.set(bgRestrictedThreshold);
 
             mCurrentTimeMillis = 10_000L;
@@ -1282,64 +1291,76 @@
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_MONITOR_ENABLED,
                     DeviceConfig::getBoolean,
-                    AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_MONITOR_ENABLED);
+                    mContext.getResources().getBoolean(
+                            R.bool.config_bg_current_drain_monitor_enabled));
             bgCurrentDrainMonitor.set(true);
 
             bgCurrentDrainWindow = new DeviceConfigSession<>(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_WINDOW,
                     DeviceConfig::getLong,
-                    AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_WINDOW_MS);
+                    (long) mContext.getResources().getInteger(
+                            R.integer.config_bg_current_drain_window));
             bgCurrentDrainWindow.set(windowMs);
 
             bgCurrentDrainRestrictedBucketThreshold = new DeviceConfigSession<>(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET,
                     DeviceConfig::getFloat,
-                    AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_THRESHOLD);
+                    getFloatArray(mContext.getResources().obtainTypedArray(
+                            R.array.config_bg_current_drain_threshold_to_restricted_bucket))[
+                            isLowRamDeviceStatic() ? 1 : 0]);
             bgCurrentDrainRestrictedBucketThreshold.set(restrictBucketThreshold);
 
             bgCurrentDrainBgRestrictedThreshold = new DeviceConfigSession<>(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_BG_RESTRICTED,
                     DeviceConfig::getFloat,
-                    AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_THRESHOLD);
+                    getFloatArray(mContext.getResources().obtainTypedArray(
+                            R.array.config_bg_current_drain_threshold_to_bg_restricted))[
+                            isLowRamDeviceStatic() ? 1 : 0]);
             bgCurrentDrainBgRestrictedThreshold.set(bgRestrictedThreshold);
 
             bgCurrentDrainRestrictedBucketHighThreshold = new DeviceConfigSession<>(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_RESTRICTED_BUCKET,
                     DeviceConfig::getFloat,
-                    AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_HIGH_THRESHOLD);
+                    getFloatArray(mContext.getResources().obtainTypedArray(
+                            R.array.config_bg_current_drain_high_threshold_to_restricted_bucket))[
+                            isLowRamDeviceStatic() ? 1 : 0]);
             bgCurrentDrainRestrictedBucketHighThreshold.set(restrictBucketHighThreshold);
 
             bgCurrentDrainBgRestrictedHighThreshold = new DeviceConfigSession<>(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_BG_RESTRICTED,
                     DeviceConfig::getFloat,
-                    AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_HIGH_THRESHOLD);
+                    getFloatArray(mContext.getResources().obtainTypedArray(
+                            R.array.config_bg_current_drain_high_threshold_to_bg_restricted))[
+                            isLowRamDeviceStatic() ? 1 : 0]);
             bgCurrentDrainBgRestrictedHighThreshold.set(bgRestrictedHighThreshold);
 
             bgMediaPlaybackMinDurationThreshold = new DeviceConfigSession<>(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION,
                     DeviceConfig::getLong,
-                    AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION);
+                    (long) mContext.getResources().getInteger(
+                            R.integer.config_bg_current_drain_media_playback_min_duration));
             bgMediaPlaybackMinDurationThreshold.set(bgMediaPlaybackMinDuration);
 
             bgLocationMinDurationThreshold = new DeviceConfigSession<>(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_LOCATION_MIN_DURATION,
                     DeviceConfig::getLong,
-                    AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_LOCATION_MIN_DURATION);
+                    (long) mContext.getResources().getInteger(
+                            R.integer.config_bg_current_drain_location_min_duration));
             bgLocationMinDurationThreshold.set(bgLocationMinDuration);
 
             bgCurrentDrainEventDurationBasedThresholdEnabled = new DeviceConfigSession<>(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED,
                     DeviceConfig::getBoolean,
-                    AppBatteryPolicy
-                            .DEFAULT_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED);
+                    mContext.getResources().getBoolean(
+                            R.bool.config_bg_current_drain_event_duration_based_threshold_enabled));
             bgCurrentDrainEventDurationBasedThresholdEnabled.set(true);
 
             bgBatteryExemptionEnabled = new DeviceConfigSession<>(
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index 0b488b2..5fb3a4e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -33,6 +33,7 @@
 
 import static org.hamcrest.core.IsNot.not;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -445,6 +446,48 @@
                 mService.getWallpaperDimAmount(), dimAmount, 0.0);
     }
 
+    @Test
+    public void testGetAdjustedWallpaperColorsOnDimming() throws RemoteException {
+        final int testUserId = USER_SYSTEM;
+        mService.switchUser(testUserId, null);
+        mService.setWallpaperComponent(sDefaultWallpaperComponent);
+        WallpaperData wallpaper = mService.getCurrentWallpaperData(FLAG_SYSTEM, testUserId);
+
+        // Mock a wallpaper data with color hints that support dark text and dark theme
+        // but not HINT_FROM_BITMAP
+        wallpaper.primaryColors = new WallpaperColors(Color.valueOf(Color.WHITE), null, null,
+                WallpaperColors.HINT_SUPPORTS_DARK_TEXT | WallpaperColors.HINT_SUPPORTS_DARK_THEME);
+        mService.setWallpaperDimAmount(0.6f);
+        int colorHints = mService.getAdjustedWallpaperColorsOnDimming(wallpaper).getColorHints();
+        // Dimmed wallpaper not extracted from bitmap does not support dark text and dark theme
+        assertNotEquals(WallpaperColors.HINT_SUPPORTS_DARK_TEXT,
+                colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT);
+        assertNotEquals(WallpaperColors.HINT_SUPPORTS_DARK_THEME,
+                colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME);
+
+        // Remove dimming
+        mService.setWallpaperDimAmount(0f);
+        colorHints = mService.getAdjustedWallpaperColorsOnDimming(wallpaper).getColorHints();
+        // Undimmed wallpaper not extracted from bitmap does support dark text and dark theme
+        assertEquals(WallpaperColors.HINT_SUPPORTS_DARK_TEXT,
+                colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT);
+        assertEquals(WallpaperColors.HINT_SUPPORTS_DARK_THEME,
+                colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME);
+
+        // Mock a wallpaper data with color hints that support dark text and dark theme
+        // and was extracted from bitmap
+        wallpaper.primaryColors = new WallpaperColors(Color.valueOf(Color.WHITE), null, null,
+                WallpaperColors.HINT_SUPPORTS_DARK_TEXT | WallpaperColors.HINT_SUPPORTS_DARK_THEME
+                        | WallpaperColors.HINT_FROM_BITMAP);
+        mService.setWallpaperDimAmount(0.6f);
+        colorHints = mService.getAdjustedWallpaperColorsOnDimming(wallpaper).getColorHints();
+        // Dimmed wallpaper should still support dark text and dark theme
+        assertEquals(WallpaperColors.HINT_SUPPORTS_DARK_TEXT,
+                colorHints & WallpaperColors.HINT_SUPPORTS_DARK_TEXT);
+        assertEquals(WallpaperColors.HINT_SUPPORTS_DARK_THEME,
+                colorHints & WallpaperColors.HINT_SUPPORTS_DARK_THEME);
+    }
+
     // Verify that after continue switch user from userId 0 to lastUserId, the wallpaper data for
     // non-current user must not bind to wallpaper service.
     private void verifyNoConnectionBeforeLastUser(int lastUserId) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 31bdec1..952200d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -895,6 +895,7 @@
         assertEquals(a.networkSecurityConfigRes, that.networkSecurityConfigRes);
         assertEquals(a.taskAffinity, that.taskAffinity);
         assertEquals(a.permission, that.permission);
+        assertEquals(a.getKnownActivityEmbeddingCerts(), that.getKnownActivityEmbeddingCerts());
         assertEquals(a.processName, that.processName);
         assertEquals(a.className, that.className);
         assertEquals(a.manageSpaceActivityName, that.manageSpaceActivityName);
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 ef9494a..dfcab2b 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -102,6 +102,7 @@
 import static java.util.Collections.emptyList;
 import static java.util.Collections.singletonList;
 
+import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
@@ -241,6 +242,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
+@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
 @RunWithLooper
 public class NotificationManagerServiceTest extends UiServiceTestCase {
     private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
@@ -1338,7 +1340,8 @@
         mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
 
         NotificationManagerService.PostNotificationRunnable runnable =
-                mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+                mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                        r.getUid(), SystemClock.elapsedRealtime());
         runnable.run();
         waitForIdle();
 
@@ -1359,7 +1362,8 @@
         when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(IMPORTANCE_NONE);
 
         NotificationManagerService.PostNotificationRunnable runnable =
-                mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+                mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                        r.getUid(), SystemClock.elapsedRealtime());
         runnable.run();
         waitForIdle();
 
@@ -3921,7 +3925,8 @@
         NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
         mService.addEnqueuedNotification(r);
         NotificationManagerService.PostNotificationRunnable runnable =
-                mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+                mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                        r.getUid(), SystemClock.elapsedRealtime());
         runnable.run();
         waitForIdle();
 
@@ -3938,7 +3943,8 @@
         r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
         mService.addEnqueuedNotification(r);
         NotificationManagerService.PostNotificationRunnable runnable =
-                mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+                mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                        r.getUid(), SystemClock.elapsedRealtime());
         runnable.run();
         waitForIdle();
 
@@ -3954,7 +3960,8 @@
         mService.addEnqueuedNotification(r);
 
         NotificationManagerService.PostNotificationRunnable runnable =
-                mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+                mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                        r.getUid(), SystemClock.elapsedRealtime());
         runnable.run();
         waitForIdle();
 
@@ -3967,12 +3974,14 @@
         r.setCriticality(CriticalNotificationExtractor.CRITICAL_LOW);
         mService.addEnqueuedNotification(r);
         NotificationManagerService.PostNotificationRunnable runnable =
-                mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+                mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                        r.getUid(), SystemClock.elapsedRealtime());
         runnable.run();
 
         r = generateNotificationRecord(mTestNotificationChannel, 1, null, false);
         r.setCriticality(CriticalNotificationExtractor.CRITICAL);
-        runnable = mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+        runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                r.getUid(), SystemClock.elapsedRealtime());
         mService.addEnqueuedNotification(r);
 
         runnable.run();
@@ -4006,6 +4015,63 @@
     }
 
     @Test
+    public void testMediaStyleRemote_hasPermission() throws RemoteException {
+        String deviceName = "device";
+        when(mPackageManager.checkPermission(
+                eq(android.Manifest.permission.MEDIA_CONTENT_CONTROL), any(), anyInt()))
+                .thenReturn(PERMISSION_GRANTED);
+        Notification.MediaStyle style = new Notification.MediaStyle();
+        style.setRemotePlaybackInfo(deviceName, 0, null);
+        Notification.Builder nb = new Notification.Builder(mContext,
+                mTestNotificationChannel.getId())
+                .setStyle(style);
+
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "testMediaStyleRemoteHasPermission", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+        waitForIdle();
+
+        NotificationRecord posted = mService.findNotificationLocked(
+                PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
+        Bundle extras = posted.getNotification().extras;
+
+        assertTrue(extras.containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE));
+        assertEquals(deviceName, extras.getString(Notification.EXTRA_MEDIA_REMOTE_DEVICE));
+    }
+
+    @Test
+    public void testMediaStyleRemote_noPermission() throws RemoteException {
+        String deviceName = "device";
+        when(mPackageManager.checkPermission(
+                eq(android.Manifest.permission.MEDIA_CONTENT_CONTROL), any(), anyInt()))
+                .thenReturn(PERMISSION_DENIED);
+        Notification.MediaStyle style = new Notification.MediaStyle();
+        style.setRemotePlaybackInfo(deviceName, 0, null);
+        Notification.Builder nb = new Notification.Builder(mContext,
+                mTestNotificationChannel.getId())
+                .setStyle(style);
+
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "testMediaStyleRemoteNoPermission", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+        waitForIdle();
+
+        NotificationRecord posted = mService.findNotificationLocked(
+                PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
+
+        assertFalse(posted.getNotification().extras
+                .containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE));
+    }
+
+    @Test
     public void testGetNotificationCountLocked() {
         String sampleTagToExclude = null;
         int sampleIdToExclude = 0;
@@ -4416,6 +4482,8 @@
 
         NotificationManagerService.PostNotificationRunnable runnable =
                 mService.new PostNotificationRunnable(original.getKey(),
+                        original.getSbn().getPackageName(),
+                        original.getUid(),
                         SystemClock.elapsedRealtime());
         runnable.run();
         waitForIdle();
@@ -4438,6 +4506,8 @@
 
         NotificationManagerService.PostNotificationRunnable runnable =
                 mService.new PostNotificationRunnable(update.getKey(),
+                        update.getSbn().getPackageName(),
+                        update.getUid(),
                         SystemClock.elapsedRealtime());
         runnable.run();
         waitForIdle();
@@ -6533,7 +6603,8 @@
         assertNull(update.getSbn().getNotification().getSmallIcon());
 
         NotificationManagerService.PostNotificationRunnable runnable =
-                mService.new PostNotificationRunnable(update.getKey(),
+                mService.new PostNotificationRunnable(update.getKey(), r.getSbn().getPackageName(),
+                        r.getUid(),
                         SystemClock.elapsedRealtime());
         runnable.run();
         waitForIdle();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index fec5405..d922f40 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -657,7 +657,8 @@
         when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false);
 
         NotificationManagerService.PostNotificationRunnable runnable =
-                mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+                mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                        r.getUid(), SystemClock.elapsedRealtime());
         runnable.run();
         waitForIdle();
 
@@ -790,7 +791,8 @@
 
         mService.addEnqueuedNotification(r);
         NotificationManagerService.PostNotificationRunnable runnable =
-                mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+                mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                        r.getUid(), SystemClock.elapsedRealtime());
         runnable.run();
         waitForIdle();
 
@@ -806,7 +808,8 @@
         r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         mService.addEnqueuedNotification(r);
-        runnable = mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+        runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                r.getUid(), SystemClock.elapsedRealtime());
         runnable.run();
         waitForIdle();
 
@@ -822,7 +825,8 @@
         r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         mService.addEnqueuedNotification(r);
-        runnable = mService.new PostNotificationRunnable(r.getKey(), SystemClock.elapsedRealtime());
+        runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+                r.getUid(), SystemClock.elapsedRealtime());
         runnable.run();
         waitForIdle();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index c58bf3b..c8e48a4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -75,6 +75,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.SigningDetails;
 import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
@@ -84,9 +85,11 @@
 import android.service.voice.IVoiceInteractionSession;
 import android.util.Pair;
 import android.view.Gravity;
+import android.window.TaskFragmentOrganizerToken;
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
 import com.android.server.wm.utils.MockTracker;
 
@@ -94,6 +97,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
 /**
  * Tests for the {@link ActivityStarter} class.
  *
@@ -1109,7 +1116,7 @@
     }
 
     @Test
-    public void testStartActivityInner_inTaskFragment() {
+    public void testStartActivityInner_inTaskFragment_failsByDefault() {
         final ActivityStarter starter = prepareStarter(0, false);
         final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
         final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
@@ -1130,6 +1137,97 @@
                 /* restrictedBgActivity */false,
                 /* intentGrants */null);
 
+        assertFalse(taskFragment.hasChild());
+    }
+
+    @Test
+    public void testStartActivityInner_inTaskFragment_allowedForSameUid() {
+        final ActivityStarter starter = prepareStarter(0, false);
+        final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
+        final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final TaskFragment taskFragment = new TaskFragment(mAtm, sourceRecord.token,
+                true /* createdByOrganizer */);
+        sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
+
+        taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class),
+                targetRecord.getUid(), "test_process_name");
+
+        starter.startActivityInner(
+                /* r */targetRecord,
+                /* sourceRecord */ sourceRecord,
+                /* voiceSession */null,
+                /* voiceInteractor */ null,
+                /* startFlags */ 0,
+                /* doResume */true,
+                /* options */null,
+                /* inTask */null,
+                /* inTaskFragment */ taskFragment,
+                /* restrictedBgActivity */false,
+                /* intentGrants */null);
+
+        assertTrue(taskFragment.hasChild());
+    }
+
+    @Test
+    public void testStartActivityInner_inTaskFragment_allowedTrustedCertUid() {
+        final ActivityStarter starter = prepareStarter(0, false);
+        final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
+        final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final TaskFragment taskFragment = new TaskFragment(mAtm, sourceRecord.token,
+                true /* createdByOrganizer */);
+        sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
+
+        taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class),
+                12345, "test_process_name");
+        AndroidPackage androidPackage = mock(AndroidPackage.class);
+        doReturn(androidPackage).when(mMockPackageManager).getPackage(eq(12345));
+
+        Set<String> certs = new HashSet(Arrays.asList("test_cert1", "test_cert1"));
+        targetRecord.info.setKnownActivityEmbeddingCerts(certs);
+        SigningDetails signingDetails = mock(SigningDetails.class);
+        doReturn(true).when(signingDetails).hasAncestorOrSelfWithDigest(any());
+        doReturn(signingDetails).when(androidPackage).getSigningDetails();
+
+        starter.startActivityInner(
+                /* r */targetRecord,
+                /* sourceRecord */ sourceRecord,
+                /* voiceSession */null,
+                /* voiceInteractor */ null,
+                /* startFlags */ 0,
+                /* doResume */true,
+                /* options */null,
+                /* inTask */null,
+                /* inTaskFragment */ taskFragment,
+                /* restrictedBgActivity */false,
+                /* intentGrants */null);
+
+        assertTrue(taskFragment.hasChild());
+    }
+
+    @Test
+    public void testStartActivityInner_inTaskFragment_allowedForUntrustedEmbedding() {
+        final ActivityStarter starter = prepareStarter(0, false);
+        final ActivityRecord targetRecord = new ActivityBuilder(mAtm).build();
+        final ActivityRecord sourceRecord = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final TaskFragment taskFragment = new TaskFragment(mAtm, sourceRecord.token,
+                true /* createdByOrganizer */);
+        sourceRecord.getTask().addChild(taskFragment, POSITION_TOP);
+
+        targetRecord.info.flags |= ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
+
+        starter.startActivityInner(
+                /* r */targetRecord,
+                /* sourceRecord */ sourceRecord,
+                /* voiceSession */null,
+                /* voiceInteractor */ null,
+                /* startFlags */ 0,
+                /* doResume */true,
+                /* options */null,
+                /* inTask */null,
+                /* inTaskFragment */ taskFragment,
+                /* restrictedBgActivity */false,
+                /* intentGrants */null);
+
         assertTrue(taskFragment.hasChild());
     }
 
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index 30ed7c2..8d92520 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -524,7 +524,10 @@
      *     defined in {@code RESOLVABLE_ERROR_}. A subclass should override this method. Otherwise,
      *     this method does nothing and returns null by default.
      * @see android.telephony.euicc.EuiccManager#downloadSubscription
+     * @deprecated prefer {@link #onDownloadSubscription(int, int,
+     *     DownloadableSubscription, boolean, boolean, Bundle)}
      */
+    @Deprecated
     public DownloadSubscriptionResult onDownloadSubscription(int slotId,
             @NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
             boolean forceDeactivateSim, @Nullable Bundle resolvedBundle) {
@@ -534,6 +537,37 @@
     /**
      * Download the given subscription.
      *
+     * @param slotIndex Index of the SIM slot to use for the operation.
+     * @param portIndex Index of the port from the slot. portIndex is used when
+     *     switchAfterDownload is set to {@code true}, otherwise download is port agnostic.
+     * @param subscription The subscription to download.
+     * @param switchAfterDownload If true, the subscription should be enabled upon successful
+     *     download.
+     * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
+     *     eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM}
+     *     should be returned to allow the user to consent to this operation first.
+     * @param resolvedBundle The bundle containing information on resolved errors. It can contain
+     *     a string of confirmation code for the key {@link #EXTRA_RESOLUTION_CONFIRMATION_CODE},
+     *     and a boolean for key {@link #EXTRA_RESOLUTION_ALLOW_POLICY_RULES} indicating whether
+     *     the user allows profile policy rules or not.
+     * @return a DownloadSubscriptionResult instance including a result code, a resolvable errors
+     *     bit map, and original the card Id. The result code may be one of the predefined
+     *     {@code RESULT_} constants or any implementation-specific code starting with
+     *     {@link #RESULT_FIRST_USER}. The resolvable error bit map can be either 0 or values
+     *     defined in {@code RESOLVABLE_ERROR_}.
+     * @see android.telephony.euicc.EuiccManager#downloadSubscription
+     */
+    @NonNull
+    public DownloadSubscriptionResult onDownloadSubscription(int slotIndex, int portIndex,
+            @NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
+            boolean forceDeactivateSim, @NonNull Bundle resolvedBundle) {
+        // stub implementation, LPA needs to implement this
+        throw new UnsupportedOperationException("LPA must override onDownloadSubscription");
+    }
+
+    /**
+     * Download the given subscription.
+     *
      * @param slotId ID of the SIM slot to use for the operation.
      * @param subscription The subscription to download.
      * @param switchAfterDownload If true, the subscription should be enabled upon successful
@@ -701,7 +735,8 @@
      */
     private class IEuiccServiceWrapper extends IEuiccService.Stub {
         @Override
-        public void downloadSubscription(int slotId, DownloadableSubscription subscription,
+        public void downloadSubscription(int slotId, int portIndex,
+                DownloadableSubscription subscription,
                 boolean switchAfterDownload, boolean forceDeactivateSim, Bundle resolvedBundle,
                 IDownloadSubscriptionCallback callback) {
             mExecutor.execute(new Runnable() {
diff --git a/telephony/java/android/service/euicc/IEuiccService.aidl b/telephony/java/android/service/euicc/IEuiccService.aidl
index 030e11a..6b0397d 100644
--- a/telephony/java/android/service/euicc/IEuiccService.aidl
+++ b/telephony/java/android/service/euicc/IEuiccService.aidl
@@ -35,9 +35,9 @@
 
 /** @hide */
 oneway interface IEuiccService {
-    void downloadSubscription(int slotId, in DownloadableSubscription subscription,
-            boolean switchAfterDownload, boolean forceDeactivateSim, in Bundle resolvedBundle,
-            in IDownloadSubscriptionCallback callback);
+    void downloadSubscription(int slotId, int portIndex, in DownloadableSubscription subscription,
+                boolean switchAfterDownload, boolean forceDeactivateSim, in Bundle resolvedBundle,
+                in IDownloadSubscriptionCallback callback);
     void getDownloadableSubscriptionMetadata(int slotId, in DownloadableSubscription subscription,
             boolean forceDeactivateSim, in IGetDownloadableSubscriptionMetadataCallback callback);
     void getEid(int slotId, in IGetEidCallback callback);
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index 1ec83a3..b18bdff 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -69,7 +69,6 @@
         "test/**/*.proto",
     ],
     proto: {
-        plugin: "javastream",
+        type: "stream",
     },
-    static_libs: ["libprotobuf-java-lite"],
 }
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index d3eb8e0..9604475 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -1269,18 +1269,19 @@
      * support the NL80211_CMD_REG_CHANGED (otherwise it will find out on its own). The wificond
      * updates in internal state in response to this Country Code update.
      *
-     * @return true on success, false otherwise.
+     * @param newCountryCode new country code. An ISO-3166-alpha2 country code which is 2-Character
+     *                       alphanumeric.
      */
-    public boolean notifyCountryCodeChanged() {
-        try {
-            if (mWificond != null) {
-                mWificond.notifyCountryCodeChanged();
-                return true;
-            }
-        } catch (RemoteException e1) {
-            Log.e(TAG, "Failed to notify country code changed due to remote exception");
+    public void notifyCountryCodeChanged(@Nullable String newCountryCode) {
+        if (mWificond == null) {
+            new RemoteException("Wificond service doesn't exist!").rethrowFromSystemServer();
         }
-        return false;
+        try {
+            mWificond.notifyCountryCodeChanged();
+            Log.i(TAG, "Receive country code change to " + newCountryCode);
+        } catch (RemoteException re) {
+            re.rethrowFromSystemServer();
+        }
     }
 
     /**
diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
index 4032a7b..a750696 100644
--- a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
@@ -1143,17 +1143,17 @@
     @Test
     public void testNotifyCountryCodeChanged() throws Exception {
         doNothing().when(mWificond).notifyCountryCodeChanged();
-        assertTrue(mWificondControl.notifyCountryCodeChanged());
+        mWificondControl.notifyCountryCodeChanged(TEST_COUNTRY_CODE);
         verify(mWificond).notifyCountryCodeChanged();
     }
 
     /**
      * Tests notifyCountryCodeChanged with RemoteException
      */
-    @Test
+    @Test(expected = RuntimeException.class)
     public void testNotifyCountryCodeChangedRemoteException() throws Exception {
         doThrow(new RemoteException()).when(mWificond).notifyCountryCodeChanged();
-        assertFalse(mWificondControl.notifyCountryCodeChanged());
+        mWificondControl.notifyCountryCodeChanged(TEST_COUNTRY_CODE);
         verify(mWificond).notifyCountryCodeChanged();
     }