Merge "Adding tracing for DPC::updatePowerState for tracking its performance"
diff --git a/Android.bp b/Android.bp
index ef25ec2..30b38d3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -326,7 +326,6 @@
             "packages/modules/Connectivity/framework/aidl-export",
             "packages/modules/Media/apex/aidl/stable",
             "hardware/interfaces/graphics/common/aidl",
-            "frameworks/native/libs/permission/aidl",
         ],
     },
     dxflags: [
@@ -596,7 +595,6 @@
             "packages/modules/Connectivity/framework/aidl-export",
             "packages/modules/Media/apex/aidl/stable",
             "hardware/interfaces/graphics/common/aidl",
-            "frameworks/native/libs/permission/aidl",
         ],
     },
     // These are libs from framework-internal-utils that are required (i.e. being referenced)
diff --git a/core/api/current.txt b/core/api/current.txt
index 9ebd118..3efe2ef 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9126,7 +9126,6 @@
     method @Nullable public String getAttributionTag();
     method @Nullable public android.content.AttributionSource getNext();
     method @Nullable public String getPackageName();
-    method public int getPid();
     method public int getUid();
     method public boolean isTrusted(@NonNull android.content.Context);
     method @NonNull public static android.content.AttributionSource myAttributionSource();
@@ -9141,7 +9140,6 @@
     method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@Nullable String);
     method @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource);
     method @NonNull public android.content.AttributionSource.Builder setPackageName(@Nullable String);
-    method @NonNull public android.content.AttributionSource.Builder setPid(int);
   }
 
   public abstract class BroadcastReceiver {
@@ -32007,7 +32005,6 @@
     method @Deprecated public static final boolean supportsProcesses();
     field public static final int BLUETOOTH_UID = 1002; // 0x3ea
     field public static final int FIRST_APPLICATION_UID = 10000; // 0x2710
-    field public static final int INVALID_PID = -1; // 0xffffffff
     field public static final int INVALID_UID = -1; // 0xffffffff
     field public static final int LAST_APPLICATION_UID = 19999; // 0x4e1f
     field public static final int PHONE_UID = 1001; // 0x3e9
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 610cb94..e890005 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -172,6 +172,7 @@
     method @NonNull public static android.media.BluetoothProfileConnectionInfo createA2dpSinkInfo(int);
     method @NonNull public static android.media.BluetoothProfileConnectionInfo createHearingAidInfo(boolean);
     method @NonNull public static android.media.BluetoothProfileConnectionInfo createLeAudioInfo(boolean, boolean);
+    method @NonNull public static android.media.BluetoothProfileConnectionInfo createLeAudioOutputInfo(boolean, int);
     method public int describeContents();
     method public int getProfile();
     method public int getVolume();
diff --git a/core/java/android/app/AppOpInfo.java b/core/java/android/app/AppOpInfo.java
new file mode 100644
index 0000000..979c910
--- /dev/null
+++ b/core/java/android/app/AppOpInfo.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import static android.app.AppOpsManager.OP_NONE;
+
+import android.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * Information about a particular app op.
+ */
+class AppOpInfo {
+    /**
+     * A unique constant identifying this app op.
+     */
+    public final int code;
+
+    /**
+     * This maps each operation to the operation that serves as the
+     * switch to determine whether it is allowed.  Generally this is
+     * a 1:1 mapping, but for some things (like location) that have
+     * multiple low-level operations being tracked that should be
+     * presented to the user as one switch then this can be used to
+     * make them all controlled by the same single operation.
+     */
+    public final int switchCode;
+
+    /**
+     * This maps each operation to the public string constant for it.
+     */
+    public final String name;
+
+    /**
+     * This provides a simple name for each operation to be used
+     * in debug output.
+     */
+    public final String simpleName;
+
+    /**
+     * This optionally maps a permission to an operation.  If there
+     * is no permission associated with an operation, it is null.
+     */
+    public final String permission;
+
+    /**
+     * Specifies whether an Op should be restricted by a user restriction.
+     * Each Op should be filled with a restriction string from UserManager or
+     * null to specify it is not affected by any user restriction.
+     */
+    public final String restriction;
+
+    /**
+     * In which cases should an app be allowed to bypass the
+     * {@link AppOpsManager#setUserRestriction user restriction} for a certain app-op.
+     */
+    public final AppOpsManager.RestrictionBypass allowSystemRestrictionBypass;
+
+    /**
+     * This specifies the default mode for each operation.
+     */
+    public final int defaultMode;
+
+    /**
+     * This specifies whether each option is allowed to be reset
+     * when resetting all app preferences.  Disable reset for
+     * app ops that are under strong control of some part of the
+     * system (such as OP_WRITE_SMS, which should be allowed only
+     * for whichever app is selected as the current SMS app).
+     */
+    public final boolean disableReset;
+
+    /**
+     * This specifies whether each option is only allowed to be read
+     * by apps with manage appops permission.
+     */
+    public final boolean restrictRead;
+
+    AppOpInfo(int code,
+            int switchCode,
+            @NonNull String name,
+            @NonNull String simpleName,
+            String permission,
+            String restriction,
+            AppOpsManager.RestrictionBypass allowSystemRestrictionBypass,
+            int defaultMode,
+            boolean disableReset,
+            boolean restrictRead) {
+        if (code < OP_NONE) throw new IllegalArgumentException();
+        if (switchCode < OP_NONE) throw new IllegalArgumentException();
+        Objects.requireNonNull(name);
+        Objects.requireNonNull(simpleName);
+        this.code = code;
+        this.switchCode = switchCode;
+        this.name = name;
+        this.simpleName = simpleName;
+        this.permission = permission;
+        this.restriction = restriction;
+        this.allowSystemRestrictionBypass = allowSystemRestrictionBypass;
+        this.defaultMode = defaultMode;
+        this.disableReset = disableReset;
+        this.restrictRead = restrictRead;
+    }
+
+    static class Builder {
+        private int mCode;
+        private int mSwitchCode;
+        private String mName;
+        private String mSimpleName;
+        private String mPermission = null;
+        private String mRestriction = null;
+        private AppOpsManager.RestrictionBypass mAllowSystemRestrictionBypass = null;
+        private int mDefaultMode = AppOpsManager.MODE_DEFAULT;
+        private boolean mDisableReset = false;
+        private boolean mRestrictRead = false;
+
+        Builder(int code, @NonNull String name, @NonNull String simpleName) {
+            if (code < OP_NONE) throw new IllegalArgumentException();
+            Objects.requireNonNull(name);
+            Objects.requireNonNull(simpleName);
+            this.mCode = code;
+            this.mSwitchCode = code;
+            this.mName = name;
+            this.mSimpleName = simpleName;
+        }
+
+        public Builder setCode(int value) {
+            this.mCode = value;
+            return this;
+        }
+
+        public Builder setSwitchCode(int value) {
+            this.mSwitchCode = value;
+            return this;
+        }
+
+        public Builder setName(String value) {
+            this.mName = value;
+            return this;
+        }
+
+        public Builder setSimpleName(String value) {
+            this.mSimpleName = value;
+            return this;
+        }
+
+        public Builder setPermission(String value) {
+            this.mPermission = value;
+            return this;
+        }
+
+        public Builder setRestriction(String value) {
+            this.mRestriction = value;
+            return this;
+        }
+
+        public Builder setAllowSystemRestrictionBypass(
+                AppOpsManager.RestrictionBypass value) {
+            this.mAllowSystemRestrictionBypass = value;
+            return this;
+        }
+
+        public Builder setDefaultMode(int value) {
+            this.mDefaultMode = value;
+            return this;
+        }
+
+        public Builder setDisableReset(boolean value) {
+            this.mDisableReset = value;
+            return this;
+        }
+
+        public Builder setRestrictRead(boolean value) {
+            this.mRestrictRead = value;
+            return this;
+        }
+
+        public AppOpInfo build() {
+            return new AppOpInfo(mCode, mSwitchCode, mName, mSimpleName, mPermission, mRestriction,
+                mAllowSystemRestrictionBypass, mDefaultMode, mDisableReset, mRestrictRead);
+        }
+    }
+}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 2f282f7..2a5916d 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -867,8 +867,7 @@
     // when adding one of these:
     //  - increment _NUM_OP
     //  - define an OPSTR_* constant (marked as @SystemApi)
-    //  - add rows to sOpToSwitch, sOpToString, sOpNames, sOpPerms, sOpDefaultMode, sOpDisableReset,
-    //      sOpRestrictions, sOpAllowSystemRestrictionBypass
+    //  - add row to sAppOpInfos
     //  - add descriptive strings to Settings/res/values/arrays.xml
     //  - add the op to the appropriate template in AppOpsState.OpsTemplate (settings app)
 
@@ -1909,1163 +1908,379 @@
             OP_TURN_SCREEN_ON,
     };
 
-    /**
-     * This maps each operation to the operation that serves as the
-     * switch to determine whether it is allowed.  Generally this is
-     * a 1:1 mapping, but for some things (like location) that have
-     * multiple low-level operations being tracked that should be
-     * presented to the user as one switch then this can be used to
-     * make them all controlled by the same single operation.
-     */
-    private static int[] sOpToSwitch = new int[] {
-            OP_COARSE_LOCATION,                 // COARSE_LOCATION
-            OP_FINE_LOCATION,                   // FINE_LOCATION
-            OP_COARSE_LOCATION,                 // GPS
-            OP_VIBRATE,                         // VIBRATE
-            OP_READ_CONTACTS,                   // READ_CONTACTS
-            OP_WRITE_CONTACTS,                  // WRITE_CONTACTS
-            OP_READ_CALL_LOG,                   // READ_CALL_LOG
-            OP_WRITE_CALL_LOG,                  // WRITE_CALL_LOG
-            OP_READ_CALENDAR,                   // READ_CALENDAR
-            OP_WRITE_CALENDAR,                  // WRITE_CALENDAR
-            OP_COARSE_LOCATION,                 // WIFI_SCAN
-            OP_POST_NOTIFICATION,               // POST_NOTIFICATION
-            OP_COARSE_LOCATION,                 // NEIGHBORING_CELLS
-            OP_CALL_PHONE,                      // CALL_PHONE
-            OP_READ_SMS,                        // READ_SMS
-            OP_WRITE_SMS,                       // WRITE_SMS
-            OP_RECEIVE_SMS,                     // RECEIVE_SMS
-            OP_RECEIVE_SMS,                     // RECEIVE_EMERGECY_SMS
-            OP_RECEIVE_MMS,                     // RECEIVE_MMS
-            OP_RECEIVE_WAP_PUSH,                // RECEIVE_WAP_PUSH
-            OP_SEND_SMS,                        // SEND_SMS
-            OP_READ_SMS,                        // READ_ICC_SMS
-            OP_WRITE_SMS,                       // WRITE_ICC_SMS
-            OP_WRITE_SETTINGS,                  // WRITE_SETTINGS
-            OP_SYSTEM_ALERT_WINDOW,             // SYSTEM_ALERT_WINDOW
-            OP_ACCESS_NOTIFICATIONS,            // ACCESS_NOTIFICATIONS
-            OP_CAMERA,                          // CAMERA
-            OP_RECORD_AUDIO,                    // RECORD_AUDIO
-            OP_PLAY_AUDIO,                      // PLAY_AUDIO
-            OP_READ_CLIPBOARD,                  // READ_CLIPBOARD
-            OP_WRITE_CLIPBOARD,                 // WRITE_CLIPBOARD
-            OP_TAKE_MEDIA_BUTTONS,              // TAKE_MEDIA_BUTTONS
-            OP_TAKE_AUDIO_FOCUS,                // TAKE_AUDIO_FOCUS
-            OP_AUDIO_MASTER_VOLUME,             // AUDIO_MASTER_VOLUME
-            OP_AUDIO_VOICE_VOLUME,              // AUDIO_VOICE_VOLUME
-            OP_AUDIO_RING_VOLUME,               // AUDIO_RING_VOLUME
-            OP_AUDIO_MEDIA_VOLUME,              // AUDIO_MEDIA_VOLUME
-            OP_AUDIO_ALARM_VOLUME,              // AUDIO_ALARM_VOLUME
-            OP_AUDIO_NOTIFICATION_VOLUME,       // AUDIO_NOTIFICATION_VOLUME
-            OP_AUDIO_BLUETOOTH_VOLUME,          // AUDIO_BLUETOOTH_VOLUME
-            OP_WAKE_LOCK,                       // WAKE_LOCK
-            OP_COARSE_LOCATION,                 // MONITOR_LOCATION
-            OP_COARSE_LOCATION,                 // MONITOR_HIGH_POWER_LOCATION
-            OP_GET_USAGE_STATS,                 // GET_USAGE_STATS
-            OP_MUTE_MICROPHONE,                 // MUTE_MICROPHONE
-            OP_TOAST_WINDOW,                    // TOAST_WINDOW
-            OP_PROJECT_MEDIA,                   // PROJECT_MEDIA
-            OP_ACTIVATE_VPN,                    // ACTIVATE_VPN
-            OP_WRITE_WALLPAPER,                 // WRITE_WALLPAPER
-            OP_ASSIST_STRUCTURE,                // ASSIST_STRUCTURE
-            OP_ASSIST_SCREENSHOT,               // ASSIST_SCREENSHOT
-            OP_READ_PHONE_STATE,                // READ_PHONE_STATE
-            OP_ADD_VOICEMAIL,                   // ADD_VOICEMAIL
-            OP_USE_SIP,                         // USE_SIP
-            OP_PROCESS_OUTGOING_CALLS,          // PROCESS_OUTGOING_CALLS
-            OP_USE_FINGERPRINT,                 // USE_FINGERPRINT
-            OP_BODY_SENSORS,                    // BODY_SENSORS
-            OP_READ_CELL_BROADCASTS,            // READ_CELL_BROADCASTS
-            OP_MOCK_LOCATION,                   // MOCK_LOCATION
-            OP_READ_EXTERNAL_STORAGE,           // READ_EXTERNAL_STORAGE
-            OP_WRITE_EXTERNAL_STORAGE,          // WRITE_EXTERNAL_STORAGE
-            OP_TURN_SCREEN_ON,                  // TURN_SCREEN_ON
-            OP_GET_ACCOUNTS,                    // GET_ACCOUNTS
-            OP_RUN_IN_BACKGROUND,               // RUN_IN_BACKGROUND
-            OP_AUDIO_ACCESSIBILITY_VOLUME,      // AUDIO_ACCESSIBILITY_VOLUME
-            OP_READ_PHONE_NUMBERS,              // READ_PHONE_NUMBERS
-            OP_REQUEST_INSTALL_PACKAGES,        // REQUEST_INSTALL_PACKAGES
-            OP_PICTURE_IN_PICTURE,              // ENTER_PICTURE_IN_PICTURE_ON_HIDE
-            OP_INSTANT_APP_START_FOREGROUND,    // INSTANT_APP_START_FOREGROUND
-            OP_ANSWER_PHONE_CALLS,              // ANSWER_PHONE_CALLS
-            OP_RUN_ANY_IN_BACKGROUND,           // OP_RUN_ANY_IN_BACKGROUND
-            OP_CHANGE_WIFI_STATE,               // OP_CHANGE_WIFI_STATE
-            OP_REQUEST_DELETE_PACKAGES,         // OP_REQUEST_DELETE_PACKAGES
-            OP_BIND_ACCESSIBILITY_SERVICE,      // OP_BIND_ACCESSIBILITY_SERVICE
-            OP_ACCEPT_HANDOVER,                 // ACCEPT_HANDOVER
-            OP_MANAGE_IPSEC_TUNNELS,            // MANAGE_IPSEC_HANDOVERS
-            OP_START_FOREGROUND,                // START_FOREGROUND
-            OP_BLUETOOTH_SCAN,                  // BLUETOOTH_SCAN
-            OP_USE_BIOMETRIC,                   // BIOMETRIC
-            OP_ACTIVITY_RECOGNITION,            // ACTIVITY_RECOGNITION
-            OP_SMS_FINANCIAL_TRANSACTIONS,      // SMS_FINANCIAL_TRANSACTIONS
-            OP_READ_MEDIA_AUDIO,                // READ_MEDIA_AUDIO
-            OP_WRITE_MEDIA_AUDIO,               // WRITE_MEDIA_AUDIO
-            OP_READ_MEDIA_VIDEO,                // READ_MEDIA_VIDEO
-            OP_WRITE_MEDIA_VIDEO,               // WRITE_MEDIA_VIDEO
-            OP_READ_MEDIA_IMAGES,               // READ_MEDIA_IMAGES
-            OP_WRITE_MEDIA_IMAGES,              // WRITE_MEDIA_IMAGES
-            OP_LEGACY_STORAGE,                  // LEGACY_STORAGE
-            OP_ACCESS_ACCESSIBILITY,            // ACCESS_ACCESSIBILITY
-            OP_READ_DEVICE_IDENTIFIERS,         // READ_DEVICE_IDENTIFIERS
-            OP_ACCESS_MEDIA_LOCATION,           // ACCESS_MEDIA_LOCATION
-            OP_QUERY_ALL_PACKAGES,              // QUERY_ALL_PACKAGES
-            OP_MANAGE_EXTERNAL_STORAGE,         // MANAGE_EXTERNAL_STORAGE
-            OP_INTERACT_ACROSS_PROFILES,        //INTERACT_ACROSS_PROFILES
-            OP_ACTIVATE_PLATFORM_VPN,           // ACTIVATE_PLATFORM_VPN
-            OP_LOADER_USAGE_STATS,              // LOADER_USAGE_STATS
-            OP_DEPRECATED_1,                    // deprecated
-            OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, //AUTO_REVOKE_PERMISSIONS_IF_UNUSED
-            OP_AUTO_REVOKE_MANAGED_BY_INSTALLER, //OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
-            OP_NO_ISOLATED_STORAGE,             // NO_ISOLATED_STORAGE
-            OP_PHONE_CALL_MICROPHONE,           // OP_PHONE_CALL_MICROPHONE
-            OP_PHONE_CALL_CAMERA,               // OP_PHONE_CALL_CAMERA
-            OP_RECORD_AUDIO_HOTWORD,            // RECORD_AUDIO_HOTWORD
-            OP_MANAGE_ONGOING_CALLS,            // MANAGE_ONGOING_CALLS
-            OP_MANAGE_CREDENTIALS,              // MANAGE_CREDENTIALS
-            OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
-            OP_RECORD_AUDIO_OUTPUT,             // RECORD_AUDIO_OUTPUT
-            OP_SCHEDULE_EXACT_ALARM,            // SCHEDULE_EXACT_ALARM
-            OP_FINE_LOCATION,                   // OP_FINE_LOCATION_SOURCE
-            OP_COARSE_LOCATION,                 // OP_COARSE_LOCATION_SOURCE
-            OP_MANAGE_MEDIA,                    // MANAGE_MEDIA
-            OP_BLUETOOTH_CONNECT,               // OP_BLUETOOTH_CONNECT
-            OP_UWB_RANGING,                     // OP_UWB_RANGING
-            OP_ACTIVITY_RECOGNITION,            // OP_ACTIVITY_RECOGNITION_SOURCE
-            OP_BLUETOOTH_ADVERTISE,             // OP_BLUETOOTH_ADVERTISE
-            OP_RECORD_INCOMING_PHONE_AUDIO,     // OP_RECORD_INCOMING_PHONE_AUDIO
-            OP_NEARBY_WIFI_DEVICES,             // OP_NEARBY_WIFI_DEVICES
-            OP_ESTABLISH_VPN_SERVICE,           // OP_ESTABLISH_VPN_SERVICE
-            OP_ESTABLISH_VPN_MANAGER,           // OP_ESTABLISH_VPN_MANAGER
-            OP_ACCESS_RESTRICTED_SETTINGS,      // OP_ACCESS_RESTRICTED_SETTINGS
-            OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,      // RECEIVE_SOUNDTRIGGER_AUDIO
-    };
-
-    /**
-     * This maps each operation to the public string constant for it.
-     */
-    private static String[] sOpToString = new String[]{
-            OPSTR_COARSE_LOCATION,
-            OPSTR_FINE_LOCATION,
-            OPSTR_GPS,
-            OPSTR_VIBRATE,
-            OPSTR_READ_CONTACTS,
-            OPSTR_WRITE_CONTACTS,
-            OPSTR_READ_CALL_LOG,
-            OPSTR_WRITE_CALL_LOG,
-            OPSTR_READ_CALENDAR,
-            OPSTR_WRITE_CALENDAR,
-            OPSTR_WIFI_SCAN,
-            OPSTR_POST_NOTIFICATION,
-            OPSTR_NEIGHBORING_CELLS,
-            OPSTR_CALL_PHONE,
-            OPSTR_READ_SMS,
-            OPSTR_WRITE_SMS,
-            OPSTR_RECEIVE_SMS,
-            OPSTR_RECEIVE_EMERGENCY_BROADCAST,
-            OPSTR_RECEIVE_MMS,
-            OPSTR_RECEIVE_WAP_PUSH,
-            OPSTR_SEND_SMS,
-            OPSTR_READ_ICC_SMS,
-            OPSTR_WRITE_ICC_SMS,
-            OPSTR_WRITE_SETTINGS,
-            OPSTR_SYSTEM_ALERT_WINDOW,
-            OPSTR_ACCESS_NOTIFICATIONS,
-            OPSTR_CAMERA,
-            OPSTR_RECORD_AUDIO,
-            OPSTR_PLAY_AUDIO,
-            OPSTR_READ_CLIPBOARD,
-            OPSTR_WRITE_CLIPBOARD,
-            OPSTR_TAKE_MEDIA_BUTTONS,
-            OPSTR_TAKE_AUDIO_FOCUS,
-            OPSTR_AUDIO_MASTER_VOLUME,
-            OPSTR_AUDIO_VOICE_VOLUME,
-            OPSTR_AUDIO_RING_VOLUME,
-            OPSTR_AUDIO_MEDIA_VOLUME,
-            OPSTR_AUDIO_ALARM_VOLUME,
-            OPSTR_AUDIO_NOTIFICATION_VOLUME,
-            OPSTR_AUDIO_BLUETOOTH_VOLUME,
-            OPSTR_WAKE_LOCK,
-            OPSTR_MONITOR_LOCATION,
-            OPSTR_MONITOR_HIGH_POWER_LOCATION,
-            OPSTR_GET_USAGE_STATS,
-            OPSTR_MUTE_MICROPHONE,
-            OPSTR_TOAST_WINDOW,
-            OPSTR_PROJECT_MEDIA,
-            OPSTR_ACTIVATE_VPN,
-            OPSTR_WRITE_WALLPAPER,
-            OPSTR_ASSIST_STRUCTURE,
-            OPSTR_ASSIST_SCREENSHOT,
-            OPSTR_READ_PHONE_STATE,
-            OPSTR_ADD_VOICEMAIL,
-            OPSTR_USE_SIP,
-            OPSTR_PROCESS_OUTGOING_CALLS,
-            OPSTR_USE_FINGERPRINT,
-            OPSTR_BODY_SENSORS,
-            OPSTR_READ_CELL_BROADCASTS,
-            OPSTR_MOCK_LOCATION,
-            OPSTR_READ_EXTERNAL_STORAGE,
-            OPSTR_WRITE_EXTERNAL_STORAGE,
-            OPSTR_TURN_SCREEN_ON,
-            OPSTR_GET_ACCOUNTS,
-            OPSTR_RUN_IN_BACKGROUND,
-            OPSTR_AUDIO_ACCESSIBILITY_VOLUME,
-            OPSTR_READ_PHONE_NUMBERS,
-            OPSTR_REQUEST_INSTALL_PACKAGES,
-            OPSTR_PICTURE_IN_PICTURE,
-            OPSTR_INSTANT_APP_START_FOREGROUND,
-            OPSTR_ANSWER_PHONE_CALLS,
-            OPSTR_RUN_ANY_IN_BACKGROUND,
-            OPSTR_CHANGE_WIFI_STATE,
-            OPSTR_REQUEST_DELETE_PACKAGES,
-            OPSTR_BIND_ACCESSIBILITY_SERVICE,
-            OPSTR_ACCEPT_HANDOVER,
-            OPSTR_MANAGE_IPSEC_TUNNELS,
-            OPSTR_START_FOREGROUND,
-            OPSTR_BLUETOOTH_SCAN,
-            OPSTR_USE_BIOMETRIC,
-            OPSTR_ACTIVITY_RECOGNITION,
-            OPSTR_SMS_FINANCIAL_TRANSACTIONS,
-            OPSTR_READ_MEDIA_AUDIO,
-            OPSTR_WRITE_MEDIA_AUDIO,
-            OPSTR_READ_MEDIA_VIDEO,
-            OPSTR_WRITE_MEDIA_VIDEO,
-            OPSTR_READ_MEDIA_IMAGES,
-            OPSTR_WRITE_MEDIA_IMAGES,
-            OPSTR_LEGACY_STORAGE,
-            OPSTR_ACCESS_ACCESSIBILITY,
-            OPSTR_READ_DEVICE_IDENTIFIERS,
-            OPSTR_ACCESS_MEDIA_LOCATION,
-            OPSTR_QUERY_ALL_PACKAGES,
-            OPSTR_MANAGE_EXTERNAL_STORAGE,
-            OPSTR_INTERACT_ACROSS_PROFILES,
-            OPSTR_ACTIVATE_PLATFORM_VPN,
-            OPSTR_LOADER_USAGE_STATS,
-            "", // deprecated
-            OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
-            OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER,
-            OPSTR_NO_ISOLATED_STORAGE,
-            OPSTR_PHONE_CALL_MICROPHONE,
-            OPSTR_PHONE_CALL_CAMERA,
-            OPSTR_RECORD_AUDIO_HOTWORD,
-            OPSTR_MANAGE_ONGOING_CALLS,
-            OPSTR_MANAGE_CREDENTIALS,
-            OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
-            OPSTR_RECORD_AUDIO_OUTPUT,
-            OPSTR_SCHEDULE_EXACT_ALARM,
-            OPSTR_FINE_LOCATION_SOURCE,
-            OPSTR_COARSE_LOCATION_SOURCE,
-            OPSTR_MANAGE_MEDIA,
-            OPSTR_BLUETOOTH_CONNECT,
-            OPSTR_UWB_RANGING,
-            OPSTR_ACTIVITY_RECOGNITION_SOURCE,
-            OPSTR_BLUETOOTH_ADVERTISE,
-            OPSTR_RECORD_INCOMING_PHONE_AUDIO,
-            OPSTR_NEARBY_WIFI_DEVICES,
-            OPSTR_ESTABLISH_VPN_SERVICE,
-            OPSTR_ESTABLISH_VPN_MANAGER,
-            OPSTR_ACCESS_RESTRICTED_SETTINGS,
-            OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO,
-    };
-
-    /**
-     * This provides a simple name for each operation to be used
-     * in debug output.
-     */
-    private static String[] sOpNames = new String[] {
-            "COARSE_LOCATION",
-            "FINE_LOCATION",
-            "GPS",
-            "VIBRATE",
-            "READ_CONTACTS",
-            "WRITE_CONTACTS",
-            "READ_CALL_LOG",
-            "WRITE_CALL_LOG",
-            "READ_CALENDAR",
-            "WRITE_CALENDAR",
-            "WIFI_SCAN",
-            "POST_NOTIFICATION",
-            "NEIGHBORING_CELLS",
-            "CALL_PHONE",
-            "READ_SMS",
-            "WRITE_SMS",
-            "RECEIVE_SMS",
-            "RECEIVE_EMERGECY_SMS",
-            "RECEIVE_MMS",
-            "RECEIVE_WAP_PUSH",
-            "SEND_SMS",
-            "READ_ICC_SMS",
-            "WRITE_ICC_SMS",
-            "WRITE_SETTINGS",
-            "SYSTEM_ALERT_WINDOW",
-            "ACCESS_NOTIFICATIONS",
-            "CAMERA",
-            "RECORD_AUDIO",
-            "PLAY_AUDIO",
-            "READ_CLIPBOARD",
-            "WRITE_CLIPBOARD",
-            "TAKE_MEDIA_BUTTONS",
-            "TAKE_AUDIO_FOCUS",
-            "AUDIO_MASTER_VOLUME",
-            "AUDIO_VOICE_VOLUME",
-            "AUDIO_RING_VOLUME",
-            "AUDIO_MEDIA_VOLUME",
-            "AUDIO_ALARM_VOLUME",
-            "AUDIO_NOTIFICATION_VOLUME",
-            "AUDIO_BLUETOOTH_VOLUME",
-            "WAKE_LOCK",
-            "MONITOR_LOCATION",
-            "MONITOR_HIGH_POWER_LOCATION",
-            "GET_USAGE_STATS",
-            "MUTE_MICROPHONE",
-            "TOAST_WINDOW",
-            "PROJECT_MEDIA",
-            "ACTIVATE_VPN",
-            "WRITE_WALLPAPER",
-            "ASSIST_STRUCTURE",
-            "ASSIST_SCREENSHOT",
-            "READ_PHONE_STATE",
-            "ADD_VOICEMAIL",
-            "USE_SIP",
-            "PROCESS_OUTGOING_CALLS",
-            "USE_FINGERPRINT",
-            "BODY_SENSORS",
-            "READ_CELL_BROADCASTS",
-            "MOCK_LOCATION",
-            "READ_EXTERNAL_STORAGE",
-            "WRITE_EXTERNAL_STORAGE",
-            "TURN_ON_SCREEN",
-            "GET_ACCOUNTS",
-            "RUN_IN_BACKGROUND",
-            "AUDIO_ACCESSIBILITY_VOLUME",
-            "READ_PHONE_NUMBERS",
-            "REQUEST_INSTALL_PACKAGES",
-            "PICTURE_IN_PICTURE",
-            "INSTANT_APP_START_FOREGROUND",
-            "ANSWER_PHONE_CALLS",
-            "RUN_ANY_IN_BACKGROUND",
-            "CHANGE_WIFI_STATE",
-            "REQUEST_DELETE_PACKAGES",
-            "BIND_ACCESSIBILITY_SERVICE",
-            "ACCEPT_HANDOVER",
-            "MANAGE_IPSEC_TUNNELS",
-            "START_FOREGROUND",
-            "BLUETOOTH_SCAN",
-            "USE_BIOMETRIC",
-            "ACTIVITY_RECOGNITION",
-            "SMS_FINANCIAL_TRANSACTIONS",
-            "READ_MEDIA_AUDIO",
-            "WRITE_MEDIA_AUDIO",
-            "READ_MEDIA_VIDEO",
-            "WRITE_MEDIA_VIDEO",
-            "READ_MEDIA_IMAGES",
-            "WRITE_MEDIA_IMAGES",
-            "LEGACY_STORAGE",
-            "ACCESS_ACCESSIBILITY",
-            "READ_DEVICE_IDENTIFIERS",
-            "ACCESS_MEDIA_LOCATION",
-            "QUERY_ALL_PACKAGES",
-            "MANAGE_EXTERNAL_STORAGE",
-            "INTERACT_ACROSS_PROFILES",
-            "ACTIVATE_PLATFORM_VPN",
-            "LOADER_USAGE_STATS",
-            "deprecated",
-            "AUTO_REVOKE_PERMISSIONS_IF_UNUSED",
-            "AUTO_REVOKE_MANAGED_BY_INSTALLER",
-            "NO_ISOLATED_STORAGE",
-            "PHONE_CALL_MICROPHONE",
-            "PHONE_CALL_CAMERA",
-            "RECORD_AUDIO_HOTWORD",
-            "MANAGE_ONGOING_CALLS",
-            "MANAGE_CREDENTIALS",
-            "USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER",
-            "RECORD_AUDIO_OUTPUT",
-            "SCHEDULE_EXACT_ALARM",
-            "FINE_LOCATION_SOURCE",
-            "COARSE_LOCATION_SOURCE",
-            "MANAGE_MEDIA",
-            "BLUETOOTH_CONNECT",
-            "UWB_RANGING",
-            "ACTIVITY_RECOGNITION_SOURCE",
-            "BLUETOOTH_ADVERTISE",
-            "RECORD_INCOMING_PHONE_AUDIO",
-            "NEARBY_WIFI_DEVICES",
-            "ESTABLISH_VPN_SERVICE",
-            "ESTABLISH_VPN_MANAGER",
-            "ACCESS_RESTRICTED_SETTINGS",
-            "RECEIVE_SOUNDTRIGGER_AUDIO",
-    };
-
-    /**
-     * This optionally maps a permission to an operation.  If there
-     * is no permission associated with an operation, it is null.
-     */
-    @UnsupportedAppUsage
-    private static String[] sOpPerms = new String[] {
-            android.Manifest.permission.ACCESS_COARSE_LOCATION,
-            android.Manifest.permission.ACCESS_FINE_LOCATION,
-            null,
-            android.Manifest.permission.VIBRATE,
-            android.Manifest.permission.READ_CONTACTS,
-            android.Manifest.permission.WRITE_CONTACTS,
-            android.Manifest.permission.READ_CALL_LOG,
-            android.Manifest.permission.WRITE_CALL_LOG,
-            android.Manifest.permission.READ_CALENDAR,
-            android.Manifest.permission.WRITE_CALENDAR,
-            android.Manifest.permission.ACCESS_WIFI_STATE,
-            android.Manifest.permission.POST_NOTIFICATIONS,
-            null, // neighboring cells shares the coarse location perm
-            android.Manifest.permission.CALL_PHONE,
-            android.Manifest.permission.READ_SMS,
-            null, // no permission required for writing sms
-            android.Manifest.permission.RECEIVE_SMS,
-            android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST,
-            android.Manifest.permission.RECEIVE_MMS,
-            android.Manifest.permission.RECEIVE_WAP_PUSH,
-            android.Manifest.permission.SEND_SMS,
-            android.Manifest.permission.READ_SMS,
-            null, // no permission required for writing icc sms
-            android.Manifest.permission.WRITE_SETTINGS,
-            android.Manifest.permission.SYSTEM_ALERT_WINDOW,
-            android.Manifest.permission.ACCESS_NOTIFICATIONS,
-            android.Manifest.permission.CAMERA,
-            android.Manifest.permission.RECORD_AUDIO,
-            null, // no permission for playing audio
-            null, // no permission for reading clipboard
-            null, // no permission for writing clipboard
-            null, // no permission for taking media buttons
-            null, // no permission for taking audio focus
-            null, // no permission for changing global volume
-            null, // no permission for changing voice volume
-            null, // no permission for changing ring volume
-            null, // no permission for changing media volume
-            null, // no permission for changing alarm volume
-            null, // no permission for changing notification volume
-            null, // no permission for changing bluetooth volume
-            android.Manifest.permission.WAKE_LOCK,
-            null, // no permission for generic location monitoring
-            null, // no permission for high power location monitoring
-            android.Manifest.permission.PACKAGE_USAGE_STATS,
-            null, // no permission for muting/unmuting microphone
-            null, // no permission for displaying toasts
-            null, // no permission for projecting media
-            null, // no permission for activating vpn
-            null, // no permission for supporting wallpaper
-            null, // no permission for receiving assist structure
-            null, // no permission for receiving assist screenshot
-            Manifest.permission.READ_PHONE_STATE,
-            Manifest.permission.ADD_VOICEMAIL,
-            Manifest.permission.USE_SIP,
-            Manifest.permission.PROCESS_OUTGOING_CALLS,
-            Manifest.permission.USE_FINGERPRINT,
-            Manifest.permission.BODY_SENSORS,
-            Manifest.permission.READ_CELL_BROADCASTS,
-            null,
-            Manifest.permission.READ_EXTERNAL_STORAGE,
-            Manifest.permission.WRITE_EXTERNAL_STORAGE,
-            Manifest.permission.TURN_SCREEN_ON,
-            Manifest.permission.GET_ACCOUNTS,
-            null, // no permission for running in background
-            null, // no permission for changing accessibility volume
-            Manifest.permission.READ_PHONE_NUMBERS,
-            Manifest.permission.REQUEST_INSTALL_PACKAGES,
-            null, // no permission for entering picture-in-picture on hide
-            Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
-            Manifest.permission.ANSWER_PHONE_CALLS,
-            null, // no permission for OP_RUN_ANY_IN_BACKGROUND
-            Manifest.permission.CHANGE_WIFI_STATE,
-            Manifest.permission.REQUEST_DELETE_PACKAGES,
-            Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
-            Manifest.permission.ACCEPT_HANDOVER,
-            Manifest.permission.MANAGE_IPSEC_TUNNELS,
-            Manifest.permission.FOREGROUND_SERVICE,
-            Manifest.permission.BLUETOOTH_SCAN,
-            Manifest.permission.USE_BIOMETRIC,
-            Manifest.permission.ACTIVITY_RECOGNITION,
-            Manifest.permission.SMS_FINANCIAL_TRANSACTIONS,
-            Manifest.permission.READ_MEDIA_AUDIO,
-            null, // no permission for OP_WRITE_MEDIA_AUDIO
-            Manifest.permission.READ_MEDIA_VIDEO,
-            null, // no permission for OP_WRITE_MEDIA_VIDEO
-            Manifest.permission.READ_MEDIA_IMAGES,
-            null, // no permission for OP_WRITE_MEDIA_IMAGES
-            null, // no permission for OP_LEGACY_STORAGE
-            null, // no permission for OP_ACCESS_ACCESSIBILITY
-            null, // no direct permission for OP_READ_DEVICE_IDENTIFIERS
-            Manifest.permission.ACCESS_MEDIA_LOCATION,
-            null, // no permission for OP_QUERY_ALL_PACKAGES
-            Manifest.permission.MANAGE_EXTERNAL_STORAGE,
-            android.Manifest.permission.INTERACT_ACROSS_PROFILES,
-            null, // no permission for OP_ACTIVATE_PLATFORM_VPN
-            android.Manifest.permission.LOADER_USAGE_STATS,
-            null, // deprecated operation
-            null, // no permission for OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
-            null, // no permission for OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
-            null, // no permission for OP_NO_ISOLATED_STORAGE
-            null, // no permission for OP_PHONE_CALL_MICROPHONE
-            null, // no permission for OP_PHONE_CALL_CAMERA
-            null, // no permission for OP_RECORD_AUDIO_HOTWORD
-            Manifest.permission.MANAGE_ONGOING_CALLS,
-            null, // no permission for OP_MANAGE_CREDENTIALS
-            Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
-            null, // no permission for OP_RECORD_AUDIO_OUTPUT
-            Manifest.permission.SCHEDULE_EXACT_ALARM,
-            null, // no permission for OP_ACCESS_FINE_LOCATION_SOURCE,
-            null, // no permission for OP_ACCESS_COARSE_LOCATION_SOURCE,
-            Manifest.permission.MANAGE_MEDIA,
-            Manifest.permission.BLUETOOTH_CONNECT,
-            Manifest.permission.UWB_RANGING,
-            null, // no permission for OP_ACTIVITY_RECOGNITION_SOURCE,
-            Manifest.permission.BLUETOOTH_ADVERTISE,
-            null, // no permission for OP_RECORD_INCOMING_PHONE_AUDIO,
-            Manifest.permission.NEARBY_WIFI_DEVICES,
-            null, // no permission for OP_ESTABLISH_VPN_SERVICE
-            null, // no permission for OP_ESTABLISH_VPN_MANAGER
-            null, // no permission for OP_ACCESS_RESTRICTED_SETTINGS,
-            null, // no permission for OP_RECEIVE_SOUNDTRIGGER_AUDIO
-    };
-
-    /**
-     * Specifies whether an Op should be restricted by a user restriction.
-     * Each Op should be filled with a restriction string from UserManager or
-     * null to specify it is not affected by any user restriction.
-     */
-    private static String[] sOpRestrictions = new String[] {
-            UserManager.DISALLOW_SHARE_LOCATION, //COARSE_LOCATION
-            UserManager.DISALLOW_SHARE_LOCATION, //FINE_LOCATION
-            UserManager.DISALLOW_SHARE_LOCATION, //GPS
-            null, //VIBRATE
-            null, //READ_CONTACTS
-            null, //WRITE_CONTACTS
-            UserManager.DISALLOW_OUTGOING_CALLS, //READ_CALL_LOG
-            UserManager.DISALLOW_OUTGOING_CALLS, //WRITE_CALL_LOG
-            null, //READ_CALENDAR
-            null, //WRITE_CALENDAR
-            UserManager.DISALLOW_SHARE_LOCATION, //WIFI_SCAN
-            null, //POST_NOTIFICATION
-            null, //NEIGHBORING_CELLS
-            null, //CALL_PHONE
-            UserManager.DISALLOW_SMS, //READ_SMS
-            UserManager.DISALLOW_SMS, //WRITE_SMS
-            UserManager.DISALLOW_SMS, //RECEIVE_SMS
-            null, //RECEIVE_EMERGENCY_SMS
-            UserManager.DISALLOW_SMS, //RECEIVE_MMS
-            null, //RECEIVE_WAP_PUSH
-            UserManager.DISALLOW_SMS, //SEND_SMS
-            UserManager.DISALLOW_SMS, //READ_ICC_SMS
-            UserManager.DISALLOW_SMS, //WRITE_ICC_SMS
-            null, //WRITE_SETTINGS
-            UserManager.DISALLOW_CREATE_WINDOWS, //SYSTEM_ALERT_WINDOW
-            null, //ACCESS_NOTIFICATIONS
-            UserManager.DISALLOW_CAMERA, //CAMERA
-            UserManager.DISALLOW_RECORD_AUDIO, //RECORD_AUDIO
-            null, //PLAY_AUDIO
-            null, //READ_CLIPBOARD
-            null, //WRITE_CLIPBOARD
-            null, //TAKE_MEDIA_BUTTONS
-            null, //TAKE_AUDIO_FOCUS
-            UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_MASTER_VOLUME
-            UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_VOICE_VOLUME
-            UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_RING_VOLUME
-            UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_MEDIA_VOLUME
-            UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_ALARM_VOLUME
-            UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_NOTIFICATION_VOLUME
-            UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_BLUETOOTH_VOLUME
-            null, //WAKE_LOCK
-            UserManager.DISALLOW_SHARE_LOCATION, //MONITOR_LOCATION
-            UserManager.DISALLOW_SHARE_LOCATION, //MONITOR_HIGH_POWER_LOCATION
-            null, //GET_USAGE_STATS
-            UserManager.DISALLOW_UNMUTE_MICROPHONE, // MUTE_MICROPHONE
-            UserManager.DISALLOW_CREATE_WINDOWS, // TOAST_WINDOW
-            null, //PROJECT_MEDIA
-            null, // ACTIVATE_VPN
-            UserManager.DISALLOW_WALLPAPER, // WRITE_WALLPAPER
-            null, // ASSIST_STRUCTURE
-            null, // ASSIST_SCREENSHOT
-            null, // READ_PHONE_STATE
-            null, // ADD_VOICEMAIL
-            null, // USE_SIP
-            null, // PROCESS_OUTGOING_CALLS
-            null, // USE_FINGERPRINT
-            null, // BODY_SENSORS
-            null, // READ_CELL_BROADCASTS
-            null, // MOCK_LOCATION
-            null, // READ_EXTERNAL_STORAGE
-            null, // WRITE_EXTERNAL_STORAGE
-            null, // TURN_SCREEN_ON
-            null, // GET_ACCOUNTS
-            null, // RUN_IN_BACKGROUND
-            UserManager.DISALLOW_ADJUST_VOLUME, //AUDIO_ACCESSIBILITY_VOLUME
-            null, // READ_PHONE_NUMBERS
-            null, // REQUEST_INSTALL_PACKAGES
-            null, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
-            null, // INSTANT_APP_START_FOREGROUND
-            null, // ANSWER_PHONE_CALLS
-            null, // OP_RUN_ANY_IN_BACKGROUND
-            null, // OP_CHANGE_WIFI_STATE
-            null, // REQUEST_DELETE_PACKAGES
-            null, // OP_BIND_ACCESSIBILITY_SERVICE
-            null, // ACCEPT_HANDOVER
-            null, // MANAGE_IPSEC_TUNNELS
-            null, // START_FOREGROUND
-            null, // maybe should be UserManager.DISALLOW_SHARE_LOCATION, //BLUETOOTH_SCAN
-            null, // USE_BIOMETRIC
-            null, // ACTIVITY_RECOGNITION
-            UserManager.DISALLOW_SMS, // SMS_FINANCIAL_TRANSACTIONS
-            null, // READ_MEDIA_AUDIO
-            null, // WRITE_MEDIA_AUDIO
-            null, // READ_MEDIA_VIDEO
-            null, // WRITE_MEDIA_VIDEO
-            null, // READ_MEDIA_IMAGES
-            null, // WRITE_MEDIA_IMAGES
-            null, // LEGACY_STORAGE
-            null, // ACCESS_ACCESSIBILITY
-            null, // READ_DEVICE_IDENTIFIERS
-            null, // ACCESS_MEDIA_LOCATION
-            null, // QUERY_ALL_PACKAGES
-            null, // MANAGE_EXTERNAL_STORAGE
-            null, // INTERACT_ACROSS_PROFILES
-            null, // ACTIVATE_PLATFORM_VPN
-            null, // LOADER_USAGE_STATS
-            null, // deprecated operation
-            null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
-            null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
-            null, // NO_ISOLATED_STORAGE
-            null, // PHONE_CALL_MICROPHONE
-            null, // PHONE_CALL_MICROPHONE
-            null, // RECORD_AUDIO_HOTWORD
-            null, // MANAGE_ONGOING_CALLS
-            null, // MANAGE_CREDENTIALS
-            null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
-            null, // RECORD_AUDIO_OUTPUT
-            null, // SCHEDULE_EXACT_ALARM
-            null, // ACCESS_FINE_LOCATION_SOURCE
-            null, // ACCESS_COARSE_LOCATION_SOURCE
-            null, // MANAGE_MEDIA
-            null, // BLUETOOTH_CONNECT
-            null, // UWB_RANGING
-            null, // ACTIVITY_RECOGNITION_SOURCE
-            null, // BLUETOOTH_ADVERTISE
-            null, // RECORD_INCOMING_PHONE_AUDIO
-            null, // NEARBY_WIFI_DEVICES
-            null, // ESTABLISH_VPN_SERVICE
-            null, // ESTABLISH_VPN_MANAGER
-            null, // ACCESS_RESTRICTED_SETTINGS
-            null, // RECEIVE_SOUNDTRIGGER_AUDIO
-    };
-
-    /**
-     * In which cases should an app be allowed to bypass the {@link #setUserRestriction user
-     * restriction} for a certain app-op.
-     */
-    private static RestrictionBypass[] sOpAllowSystemRestrictionBypass = new RestrictionBypass[] {
-            new RestrictionBypass(true, false, false), //COARSE_LOCATION
-            new RestrictionBypass(true, false, false), //FINE_LOCATION
-            null, //GPS
-            null, //VIBRATE
-            null, //READ_CONTACTS
-            null, //WRITE_CONTACTS
-            null, //READ_CALL_LOG
-            null, //WRITE_CALL_LOG
-            null, //READ_CALENDAR
-            null, //WRITE_CALENDAR
-            new RestrictionBypass(false, true, false), //WIFI_SCAN
-            null, //POST_NOTIFICATION
-            null, //NEIGHBORING_CELLS
-            null, //CALL_PHONE
-            null, //READ_SMS
-            null, //WRITE_SMS
-            null, //RECEIVE_SMS
-            null, //RECEIVE_EMERGECY_SMS
-            null, //RECEIVE_MMS
-            null, //RECEIVE_WAP_PUSH
-            null, //SEND_SMS
-            null, //READ_ICC_SMS
-            null, //WRITE_ICC_SMS
-            null, //WRITE_SETTINGS
-            new RestrictionBypass(false, true, false), //SYSTEM_ALERT_WINDOW
-            null, //ACCESS_NOTIFICATIONS
-            null, //CAMERA
-            new RestrictionBypass(false, false, true), //RECORD_AUDIO
-            null, //PLAY_AUDIO
-            null, //READ_CLIPBOARD
-            null, //WRITE_CLIPBOARD
-            null, //TAKE_MEDIA_BUTTONS
-            null, //TAKE_AUDIO_FOCUS
-            null, //AUDIO_MASTER_VOLUME
-            null, //AUDIO_VOICE_VOLUME
-            null, //AUDIO_RING_VOLUME
-            null, //AUDIO_MEDIA_VOLUME
-            null, //AUDIO_ALARM_VOLUME
-            null, //AUDIO_NOTIFICATION_VOLUME
-            null, //AUDIO_BLUETOOTH_VOLUME
-            null, //WAKE_LOCK
-            null, //MONITOR_LOCATION
-            null, //MONITOR_HIGH_POWER_LOCATION
-            null, //GET_USAGE_STATS
-            null, //MUTE_MICROPHONE
-            new RestrictionBypass(false, true, false), //TOAST_WINDOW
-            null, //PROJECT_MEDIA
-            null, //ACTIVATE_VPN
-            null, //WALLPAPER
-            null, //ASSIST_STRUCTURE
-            null, //ASSIST_SCREENSHOT
-            null, //READ_PHONE_STATE
-            null, //ADD_VOICEMAIL
-            null, // USE_SIP
-            null, // PROCESS_OUTGOING_CALLS
-            null, // USE_FINGERPRINT
-            null, // BODY_SENSORS
-            null, // READ_CELL_BROADCASTS
-            null, // MOCK_LOCATION
-            null, // READ_EXTERNAL_STORAGE
-            null, // WRITE_EXTERNAL_STORAGE
-            null, // TURN_SCREEN_ON
-            null, // GET_ACCOUNTS
-            null, // RUN_IN_BACKGROUND
-            null, // AUDIO_ACCESSIBILITY_VOLUME
-            null, // READ_PHONE_NUMBERS
-            null, // REQUEST_INSTALL_PACKAGES
-            null, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
-            null, // INSTANT_APP_START_FOREGROUND
-            null, // ANSWER_PHONE_CALLS
-            null, // OP_RUN_ANY_IN_BACKGROUND
-            null, // OP_CHANGE_WIFI_STATE
-            null, // OP_REQUEST_DELETE_PACKAGES
-            null, // OP_BIND_ACCESSIBILITY_SERVICE
-            null, // ACCEPT_HANDOVER
-            null, // MANAGE_IPSEC_HANDOVERS
-            null, // START_FOREGROUND
-            new RestrictionBypass(false, true, false), // BLUETOOTH_SCAN
-            null, // USE_BIOMETRIC
-            null, // ACTIVITY_RECOGNITION
-            null, // SMS_FINANCIAL_TRANSACTIONS
-            null, // READ_MEDIA_AUDIO
-            null, // WRITE_MEDIA_AUDIO
-            null, // READ_MEDIA_VIDEO
-            null, // WRITE_MEDIA_VIDEO
-            null, // READ_MEDIA_IMAGES
-            null, // WRITE_MEDIA_IMAGES
-            null, // LEGACY_STORAGE
-            null, // ACCESS_ACCESSIBILITY
-            null, // READ_DEVICE_IDENTIFIERS
-            null, // ACCESS_MEDIA_LOCATION
-            null, // QUERY_ALL_PACKAGES
-            null, // MANAGE_EXTERNAL_STORAGE
-            null, // INTERACT_ACROSS_PROFILES
-            null, // ACTIVATE_PLATFORM_VPN
-            null, // LOADER_USAGE_STATS
-            null, // deprecated operation
-            null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
-            null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
-            null, // NO_ISOLATED_STORAGE
-            null, // PHONE_CALL_MICROPHONE
-            null, // PHONE_CALL_CAMERA
-            null, // RECORD_AUDIO_HOTWORD
-            null, // MANAGE_ONGOING_CALLS
-            null, // MANAGE_CREDENTIALS
-            null, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
-            null, // RECORD_AUDIO_OUTPUT
-            null, // SCHEDULE_EXACT_ALARM
-            null, // ACCESS_FINE_LOCATION_SOURCE
-            null, // ACCESS_COARSE_LOCATION_SOURCE
-            null, // MANAGE_MEDIA
-            null, // BLUETOOTH_CONNECT
-            null, // UWB_RANGING
-            null, // ACTIVITY_RECOGNITION_SOURCE
-            null, // BLUETOOTH_ADVERTISE
-            null, // RECORD_INCOMING_PHONE_AUDIO
-            null, // NEARBY_WIFI_DEVICES
-            null, // ESTABLISH_VPN_SERVICE
-            null, // ESTABLISH_VPN_MANAGER
-            null, // ACCESS_RESTRICTED_SETTINGS
-            null, // RECEIVE_SOUNDTRIGGER_AUDIO
-    };
-
-    /**
-     * This specifies the default mode for each operation.
-     */
-    private static int[] sOpDefaultMode = new int[] {
-            AppOpsManager.MODE_ALLOWED, // COARSE_LOCATION
-            AppOpsManager.MODE_ALLOWED, // FINE_LOCATION
-            AppOpsManager.MODE_ALLOWED, // GPS
-            AppOpsManager.MODE_ALLOWED, // VIBRATE
-            AppOpsManager.MODE_ALLOWED, // READ_CONTACTS
-            AppOpsManager.MODE_ALLOWED, // WRITE_CONTACTS
-            AppOpsManager.MODE_ALLOWED, // READ_CALL_LOG
-            AppOpsManager.MODE_ALLOWED, // WRITE_CALL_LOG
-            AppOpsManager.MODE_ALLOWED, // READ_CALENDAR
-            AppOpsManager.MODE_ALLOWED, // WRITE_CALENDAR
-            AppOpsManager.MODE_ALLOWED, // WIFI_SCAN
-            AppOpsManager.MODE_ALLOWED, // POST_NOTIFICATION
-            AppOpsManager.MODE_ALLOWED, // NEIGHBORING_CELLS
-            AppOpsManager.MODE_ALLOWED, // CALL_PHONE
-            AppOpsManager.MODE_ALLOWED, // READ_SMS
-            AppOpsManager.MODE_IGNORED, // WRITE_SMS
-            AppOpsManager.MODE_ALLOWED, // RECEIVE_SMS
-            AppOpsManager.MODE_ALLOWED, // RECEIVE_EMERGENCY_BROADCAST
-            AppOpsManager.MODE_ALLOWED, // RECEIVE_MMS
-            AppOpsManager.MODE_ALLOWED, // RECEIVE_WAP_PUSH
-            AppOpsManager.MODE_ALLOWED, // SEND_SMS
-            AppOpsManager.MODE_ALLOWED, // READ_ICC_SMS
-            AppOpsManager.MODE_ALLOWED, // WRITE_ICC_SMS
-            AppOpsManager.MODE_DEFAULT, // WRITE_SETTINGS
-            getSystemAlertWindowDefault(), // SYSTEM_ALERT_WINDOW
-            AppOpsManager.MODE_ALLOWED, // ACCESS_NOTIFICATIONS
-            AppOpsManager.MODE_ALLOWED, // CAMERA
-            AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO
-            AppOpsManager.MODE_ALLOWED, // PLAY_AUDIO
-            AppOpsManager.MODE_ALLOWED, // READ_CLIPBOARD
-            AppOpsManager.MODE_ALLOWED, // WRITE_CLIPBOARD
-            AppOpsManager.MODE_ALLOWED, // TAKE_MEDIA_BUTTONS
-            AppOpsManager.MODE_ALLOWED, // TAKE_AUDIO_FOCUS
-            AppOpsManager.MODE_ALLOWED, // AUDIO_MASTER_VOLUME
-            AppOpsManager.MODE_ALLOWED, // AUDIO_VOICE_VOLUME
-            AppOpsManager.MODE_ALLOWED, // AUDIO_RING_VOLUME
-            AppOpsManager.MODE_ALLOWED, // AUDIO_MEDIA_VOLUME
-            AppOpsManager.MODE_ALLOWED, // AUDIO_ALARM_VOLUME
-            AppOpsManager.MODE_ALLOWED, // AUDIO_NOTIFICATION_VOLUME
-            AppOpsManager.MODE_ALLOWED, // AUDIO_BLUETOOTH_VOLUME
-            AppOpsManager.MODE_ALLOWED, // WAKE_LOCK
-            AppOpsManager.MODE_ALLOWED, // MONITOR_LOCATION
-            AppOpsManager.MODE_ALLOWED, // MONITOR_HIGH_POWER_LOCATION
-            AppOpsManager.MODE_DEFAULT, // GET_USAGE_STATS
-            AppOpsManager.MODE_ALLOWED, // MUTE_MICROPHONE
-            AppOpsManager.MODE_ALLOWED, // TOAST_WINDOW
-            AppOpsManager.MODE_IGNORED, // PROJECT_MEDIA
-            AppOpsManager.MODE_IGNORED, // ACTIVATE_VPN
-            AppOpsManager.MODE_ALLOWED, // WRITE_WALLPAPER
-            AppOpsManager.MODE_ALLOWED, // ASSIST_STRUCTURE
-            AppOpsManager.MODE_ALLOWED, // ASSIST_SCREENSHOT
-            AppOpsManager.MODE_ALLOWED, // READ_PHONE_STATE
-            AppOpsManager.MODE_ALLOWED, // ADD_VOICEMAIL
-            AppOpsManager.MODE_ALLOWED, // USE_SIP
-            AppOpsManager.MODE_ALLOWED, // PROCESS_OUTGOING_CALLS
-            AppOpsManager.MODE_ALLOWED, // USE_FINGERPRINT
-            AppOpsManager.MODE_ALLOWED, // BODY_SENSORS
-            AppOpsManager.MODE_ALLOWED, // READ_CELL_BROADCASTS
-            AppOpsManager.MODE_ERRORED, // MOCK_LOCATION
-            AppOpsManager.MODE_ALLOWED, // READ_EXTERNAL_STORAGE
-            AppOpsManager.MODE_ALLOWED, // WRITE_EXTERNAL_STORAGE
-            AppOpsManager.MODE_ERRORED, // TURN_SCREEN_ON
-            AppOpsManager.MODE_ALLOWED, // GET_ACCOUNTS
-            AppOpsManager.MODE_ALLOWED, // RUN_IN_BACKGROUND
-            AppOpsManager.MODE_ALLOWED, // AUDIO_ACCESSIBILITY_VOLUME
-            AppOpsManager.MODE_ALLOWED, // READ_PHONE_NUMBERS
-            AppOpsManager.MODE_DEFAULT, // REQUEST_INSTALL_PACKAGES
-            AppOpsManager.MODE_ALLOWED, // PICTURE_IN_PICTURE
-            AppOpsManager.MODE_DEFAULT, // INSTANT_APP_START_FOREGROUND
-            AppOpsManager.MODE_ALLOWED, // ANSWER_PHONE_CALLS
-            AppOpsManager.MODE_ALLOWED, // RUN_ANY_IN_BACKGROUND
-            AppOpsManager.MODE_ALLOWED, // CHANGE_WIFI_STATE
-            AppOpsManager.MODE_ALLOWED, // REQUEST_DELETE_PACKAGES
-            AppOpsManager.MODE_ALLOWED, // BIND_ACCESSIBILITY_SERVICE
-            AppOpsManager.MODE_ALLOWED, // ACCEPT_HANDOVER
-            AppOpsManager.MODE_ERRORED, // MANAGE_IPSEC_TUNNELS
-            AppOpsManager.MODE_ALLOWED, // START_FOREGROUND
-            AppOpsManager.MODE_ALLOWED, // BLUETOOTH_SCAN
-            AppOpsManager.MODE_ALLOWED, // USE_BIOMETRIC
-            AppOpsManager.MODE_ALLOWED, // ACTIVITY_RECOGNITION
-            AppOpsManager.MODE_DEFAULT, // SMS_FINANCIAL_TRANSACTIONS
-            AppOpsManager.MODE_ALLOWED, // READ_MEDIA_AUDIO
-            AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_AUDIO
-            AppOpsManager.MODE_ALLOWED, // READ_MEDIA_VIDEO
-            AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_VIDEO
-            AppOpsManager.MODE_ALLOWED, // READ_MEDIA_IMAGES
-            AppOpsManager.MODE_ERRORED, // WRITE_MEDIA_IMAGES
-            AppOpsManager.MODE_DEFAULT, // LEGACY_STORAGE
-            AppOpsManager.MODE_ALLOWED, // ACCESS_ACCESSIBILITY
-            AppOpsManager.MODE_ERRORED, // READ_DEVICE_IDENTIFIERS
-            AppOpsManager.MODE_ALLOWED, // ALLOW_MEDIA_LOCATION
-            AppOpsManager.MODE_DEFAULT, // QUERY_ALL_PACKAGES
-            AppOpsManager.MODE_DEFAULT, // MANAGE_EXTERNAL_STORAGE
-            AppOpsManager.MODE_DEFAULT, // INTERACT_ACROSS_PROFILES
-            AppOpsManager.MODE_IGNORED, // ACTIVATE_PLATFORM_VPN
-            AppOpsManager.MODE_DEFAULT, // LOADER_USAGE_STATS
-            AppOpsManager.MODE_IGNORED, // deprecated operation
-            AppOpsManager.MODE_DEFAULT, // OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
-            AppOpsManager.MODE_ALLOWED, // OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
-            AppOpsManager.MODE_ERRORED, // OP_NO_ISOLATED_STORAGE
-            AppOpsManager.MODE_ALLOWED, // PHONE_CALL_MICROPHONE
-            AppOpsManager.MODE_ALLOWED, // PHONE_CALL_CAMERA
-            AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO_HOTWORD
-            AppOpsManager.MODE_DEFAULT, // MANAGE_ONGOING_CALLS
-            AppOpsManager.MODE_DEFAULT, // MANAGE_CREDENTIALS
-            AppOpsManager.MODE_DEFAULT, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
-            AppOpsManager.MODE_ALLOWED, // RECORD_AUDIO_OUTPUT
-            AppOpsManager.MODE_DEFAULT, // SCHEDULE_EXACT_ALARM
-            AppOpsManager.MODE_ALLOWED, // ACCESS_FINE_LOCATION_SOURCE
-            AppOpsManager.MODE_ALLOWED, // ACCESS_COARSE_LOCATION_SOURCE
-            AppOpsManager.MODE_DEFAULT, // MANAGE_MEDIA
-            AppOpsManager.MODE_ALLOWED, // BLUETOOTH_CONNECT
-            AppOpsManager.MODE_ALLOWED, // UWB_RANGING
-            AppOpsManager.MODE_ALLOWED, // ACTIVITY_RECOGNITION_SOURCE
-            AppOpsManager.MODE_ALLOWED, // BLUETOOTH_ADVERTISE
-            AppOpsManager.MODE_ALLOWED, // RECORD_INCOMING_PHONE_AUDIO
-            AppOpsManager.MODE_ALLOWED, // NEARBY_WIFI_DEVICES
-            AppOpsManager.MODE_ALLOWED, // ESTABLISH_VPN_SERVICE
-            AppOpsManager.MODE_ALLOWED, // ESTABLISH_VPN_MANAGER
-            AppOpsManager.MODE_ALLOWED, // ACCESS_RESTRICTED_SETTINGS,
-            AppOpsManager.MODE_ALLOWED, // RECEIVE_SOUNDTRIGGER_AUDIO
-    };
-
-    /**
-     * This specifies whether each option is allowed to be reset
-     * when resetting all app preferences.  Disable reset for
-     * app ops that are under strong control of some part of the
-     * system (such as OP_WRITE_SMS, which should be allowed only
-     * for whichever app is selected as the current SMS app).
-     */
-    private static boolean[] sOpDisableReset = new boolean[] {
-            false, // COARSE_LOCATION
-            false, // FINE_LOCATION
-            false, // GPS
-            false, // VIBRATE
-            false, // READ_CONTACTS
-            false, // WRITE_CONTACTS
-            false, // READ_CALL_LOG
-            false, // WRITE_CALL_LOG
-            false, // READ_CALENDAR
-            false, // WRITE_CALENDAR
-            false, // WIFI_SCAN
-            false, // POST_NOTIFICATION
-            false, // NEIGHBORING_CELLS
-            false, // CALL_PHONE
-            true, // READ_SMS
-            true, // WRITE_SMS
-            true, // RECEIVE_SMS
-            false, // RECEIVE_EMERGENCY_BROADCAST
-            false, // RECEIVE_MMS
-            true, // RECEIVE_WAP_PUSH
-            true, // SEND_SMS
-            false, // READ_ICC_SMS
-            false, // WRITE_ICC_SMS
-            false, // WRITE_SETTINGS
-            false, // SYSTEM_ALERT_WINDOW
-            false, // ACCESS_NOTIFICATIONS
-            false, // CAMERA
-            false, // RECORD_AUDIO
-            false, // PLAY_AUDIO
-            false, // READ_CLIPBOARD
-            false, // WRITE_CLIPBOARD
-            false, // TAKE_MEDIA_BUTTONS
-            false, // TAKE_AUDIO_FOCUS
-            false, // AUDIO_MASTER_VOLUME
-            false, // AUDIO_VOICE_VOLUME
-            false, // AUDIO_RING_VOLUME
-            false, // AUDIO_MEDIA_VOLUME
-            false, // AUDIO_ALARM_VOLUME
-            false, // AUDIO_NOTIFICATION_VOLUME
-            false, // AUDIO_BLUETOOTH_VOLUME
-            false, // WAKE_LOCK
-            false, // MONITOR_LOCATION
-            false, // MONITOR_HIGH_POWER_LOCATION
-            false, // GET_USAGE_STATS
-            false, // MUTE_MICROPHONE
-            false, // TOAST_WINDOW
-            false, // PROJECT_MEDIA
-            false, // ACTIVATE_VPN
-            false, // WRITE_WALLPAPER
-            false, // ASSIST_STRUCTURE
-            false, // ASSIST_SCREENSHOT
-            false, // READ_PHONE_STATE
-            false, // ADD_VOICEMAIL
-            false, // USE_SIP
-            false, // PROCESS_OUTGOING_CALLS
-            false, // USE_FINGERPRINT
-            false, // BODY_SENSORS
-            true, // READ_CELL_BROADCASTS
-            false, // MOCK_LOCATION
-            false, // READ_EXTERNAL_STORAGE
-            false, // WRITE_EXTERNAL_STORAGE
-            false, // TURN_SCREEN_ON
-            false, // GET_ACCOUNTS
-            false, // RUN_IN_BACKGROUND
-            false, // AUDIO_ACCESSIBILITY_VOLUME
-            false, // READ_PHONE_NUMBERS
-            false, // REQUEST_INSTALL_PACKAGES
-            false, // PICTURE_IN_PICTURE
-            false, // INSTANT_APP_START_FOREGROUND
-            false, // ANSWER_PHONE_CALLS
-            false, // RUN_ANY_IN_BACKGROUND
-            false, // CHANGE_WIFI_STATE
-            false, // REQUEST_DELETE_PACKAGES
-            false, // BIND_ACCESSIBILITY_SERVICE
-            false, // ACCEPT_HANDOVER
-            false, // MANAGE_IPSEC_TUNNELS
-            false, // START_FOREGROUND
-            false, // BLUETOOTH_SCAN
-            false, // USE_BIOMETRIC
-            false, // ACTIVITY_RECOGNITION
-            false, // SMS_FINANCIAL_TRANSACTIONS
-            false, // READ_MEDIA_AUDIO
-            false, // WRITE_MEDIA_AUDIO
-            false, // READ_MEDIA_VIDEO
-            true,  // WRITE_MEDIA_VIDEO
-            false, // READ_MEDIA_IMAGES
-            true,  // WRITE_MEDIA_IMAGES
-            true,  // LEGACY_STORAGE
-            false, // ACCESS_ACCESSIBILITY
-            false, // READ_DEVICE_IDENTIFIERS
-            false, // ACCESS_MEDIA_LOCATION
-            false, // QUERY_ALL_PACKAGES
-            false, // MANAGE_EXTERNAL_STORAGE
-            false, // INTERACT_ACROSS_PROFILES
-            false, // ACTIVATE_PLATFORM_VPN
-            false, // LOADER_USAGE_STATS
-            false, // deprecated operation
-            false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
-            false, // AUTO_REVOKE_MANAGED_BY_INSTALLER
-            true, // NO_ISOLATED_STORAGE
-            false, // PHONE_CALL_MICROPHONE
-            false, // PHONE_CALL_CAMERA
-            false, // RECORD_AUDIO_HOTWORD
-            true, // MANAGE_ONGOING_CALLS
-            false, // MANAGE_CREDENTIALS
-            true, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
-            false, // RECORD_AUDIO_OUTPUT
-            false, // SCHEDULE_EXACT_ALARM
-            false, // ACCESS_FINE_LOCATION_SOURCE
-            false, // ACCESS_COARSE_LOCATION_SOURCE
-            false, // MANAGE_MEDIA
-            false, // BLUETOOTH_CONNECT
-            false, // UWB_RANGING
-            false, // ACTIVITY_RECOGNITION_SOURCE
-            false, // BLUETOOTH_ADVERTISE
-            false, // RECORD_INCOMING_PHONE_AUDIO
-            false, // NEARBY_WIFI_DEVICES
-            false, // OP_ESTABLISH_VPN_SERVICE
-            false, // OP_ESTABLISH_VPN_MANAGER
-            true, // ACCESS_RESTRICTED_SETTINGS
-            false, // RECEIVE_SOUNDTRIGGER_AUDIO
-    };
-
-    /**
-     * This specifies whether each option is only allowed to be read
-     * by apps with manage appops permission.
-     */
-    private static boolean[] sOpRestrictRead = new boolean[] {
-            false, // COARSE_LOCATION
-            false, // FINE_LOCATION
-            false, // GPS
-            false, // VIBRATE
-            false, // READ_CONTACTS
-            false, // WRITE_CONTACTS
-            false, // READ_CALL_LOG
-            false, // WRITE_CALL_LOG
-            false, // READ_CALENDAR
-            false, // WRITE_CALENDAR
-            false, // WIFI_SCAN
-            false, // POST_NOTIFICATION
-            false, // NEIGHBORING_CELLS
-            false, // CALL_PHONE
-            false, // READ_SMS
-            false, // WRITE_SMS
-            false, // RECEIVE_SMS
-            false, // RECEIVE_EMERGENCY_BROADCAST
-            false, // RECEIVE_MMS
-            false, // RECEIVE_WAP_PUSH
-            false, // SEND_SMS
-            false, // READ_ICC_SMS
-            false, // WRITE_ICC_SMS
-            false, // WRITE_SETTINGS
-            false, // SYSTEM_ALERT_WINDOW
-            false, // ACCESS_NOTIFICATIONS
-            false, // CAMERA
-            false, // RECORD_AUDIO
-            false, // PLAY_AUDIO
-            false, // READ_CLIPBOARD
-            false, // WRITE_CLIPBOARD
-            false, // TAKE_MEDIA_BUTTONS
-            false, // TAKE_AUDIO_FOCUS
-            false, // AUDIO_MASTER_VOLUME
-            false, // AUDIO_VOICE_VOLUME
-            false, // AUDIO_RING_VOLUME
-            false, // AUDIO_MEDIA_VOLUME
-            false, // AUDIO_ALARM_VOLUME
-            false, // AUDIO_NOTIFICATION_VOLUME
-            false, // AUDIO_BLUETOOTH_VOLUME
-            false, // WAKE_LOCK
-            false, // MONITOR_LOCATION
-            false, // MONITOR_HIGH_POWER_LOCATION
-            false, // GET_USAGE_STATS
-            false, // MUTE_MICROPHONE
-            false, // TOAST_WINDOW
-            false, // PROJECT_MEDIA
-            false, // ACTIVATE_VPN
-            false, // WRITE_WALLPAPER
-            false, // ASSIST_STRUCTURE
-            false, // ASSIST_SCREENSHOT
-            false, // READ_PHONE_STATE
-            false, // ADD_VOICEMAIL
-            false, // USE_SIP
-            false, // PROCESS_OUTGOING_CALLS
-            false, // USE_FINGERPRINT
-            false, // BODY_SENSORS
-            false, // READ_CELL_BROADCASTS
-            false, // MOCK_LOCATION
-            false, // READ_EXTERNAL_STORAGE
-            false, // WRITE_EXTERNAL_STORAGE
-            false, // TURN_SCREEN_ON
-            false, // GET_ACCOUNTS
-            false, // RUN_IN_BACKGROUND
-            false, // AUDIO_ACCESSIBILITY_VOLUME
-            false, // READ_PHONE_NUMBERS
-            false, // REQUEST_INSTALL_PACKAGES
-            false, // PICTURE_IN_PICTURE
-            false, // INSTANT_APP_START_FOREGROUND
-            false, // ANSWER_PHONE_CALLS
-            false, // RUN_ANY_IN_BACKGROUND
-            false, // CHANGE_WIFI_STATE
-            false, // REQUEST_DELETE_PACKAGES
-            false, // BIND_ACCESSIBILITY_SERVICE
-            false, // ACCEPT_HANDOVER
-            false, // MANAGE_IPSEC_TUNNELS
-            false, // START_FOREGROUND
-            false, // BLUETOOTH_SCAN
-            false, // USE_BIOMETRIC
-            false, // ACTIVITY_RECOGNITION
-            false, // SMS_FINANCIAL_TRANSACTIONS
-            false, // READ_MEDIA_AUDIO
-            false, // WRITE_MEDIA_AUDIO
-            false, // READ_MEDIA_VIDEO
-            false,  // WRITE_MEDIA_VIDEO
-            false, // READ_MEDIA_IMAGES
-            false,  // WRITE_MEDIA_IMAGES
-            false,  // LEGACY_STORAGE
-            false, // ACCESS_ACCESSIBILITY
-            false, // READ_DEVICE_IDENTIFIERS
-            false, // ACCESS_MEDIA_LOCATION
-            false, // QUERY_ALL_PACKAGES
-            false, // MANAGE_EXTERNAL_STORAGE
-            false, // INTERACT_ACROSS_PROFILES
-            false, // ACTIVATE_PLATFORM_VPN
-            false, // LOADER_USAGE_STATS
-            false, // deprecated operation
-            false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
-            false, // AUTO_REVOKE_MANAGED_BY_INSTALLER
-            false, // NO_ISOLATED_STORAGE
-            false, // PHONE_CALL_MICROPHONE
-            false, // PHONE_CALL_CAMERA
-            false, // RECORD_AUDIO_HOTWORD
-            false, // MANAGE_ONGOING_CALLS
-            false, // MANAGE_CREDENTIALS
-            false, // USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER
-            false, // RECORD_AUDIO_OUTPUT
-            false, // SCHEDULE_EXACT_ALARM
-            false, // ACCESS_FINE_LOCATION_SOURCE
-            false, // ACCESS_COARSE_LOCATION_SOURCE
-            false, // MANAGE_MEDIA
-            false, // BLUETOOTH_CONNECT
-            false, // UWB_RANGING
-            false, // ACTIVITY_RECOGNITION_SOURCE
-            false, // BLUETOOTH_ADVERTISE
-            false, // RECORD_INCOMING_PHONE_AUDIO
-            false, // NEARBY_WIFI_DEVICES
-            false, // OP_ESTABLISH_VPN_SERVICE
-            false, // OP_ESTABLISH_VPN_MANAGER
-            true, // ACCESS_RESTRICTED_SETTINGS
-            false, // RECEIVE_SOUNDTRIGGER_AUDIO
+    static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{
+        new AppOpInfo.Builder(OP_COARSE_LOCATION, OPSTR_COARSE_LOCATION, "COARSE_LOCATION")
+            .setPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+            .setRestriction(UserManager.DISALLOW_SHARE_LOCATION)
+            .setAllowSystemRestrictionBypass(new RestrictionBypass(true, false, false))
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_FINE_LOCATION, OPSTR_FINE_LOCATION, "FINE_LOCATION")
+            .setPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
+            .setRestriction(UserManager.DISALLOW_SHARE_LOCATION)
+            .setAllowSystemRestrictionBypass(new RestrictionBypass(true, false, false))
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_GPS, OPSTR_GPS, "GPS")
+            .setSwitchCode(OP_COARSE_LOCATION)
+            .setRestriction(UserManager.DISALLOW_SHARE_LOCATION)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_VIBRATE, OPSTR_VIBRATE, "VIBRATE")
+            .setSwitchCode(OP_VIBRATE).setPermission(android.Manifest.permission.VIBRATE)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_READ_CONTACTS, OPSTR_READ_CONTACTS, "READ_CONTACTS")
+            .setPermission(android.Manifest.permission.READ_CONTACTS)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_WRITE_CONTACTS, OPSTR_WRITE_CONTACTS, "WRITE_CONTACTS")
+            .setPermission(android.Manifest.permission.WRITE_CONTACTS)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_READ_CALL_LOG, OPSTR_READ_CALL_LOG, "READ_CALL_LOG")
+            .setPermission(android.Manifest.permission.READ_CALL_LOG)
+            .setRestriction(UserManager.DISALLOW_OUTGOING_CALLS)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_WRITE_CALL_LOG, OPSTR_WRITE_CALL_LOG, "WRITE_CALL_LOG")
+            .setPermission(android.Manifest.permission.WRITE_CALL_LOG)
+            .setRestriction(UserManager.DISALLOW_OUTGOING_CALLS)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_READ_CALENDAR, OPSTR_READ_CALENDAR, "READ_CALENDAR")
+            .setPermission(android.Manifest.permission.READ_CALENDAR)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_WRITE_CALENDAR, OPSTR_WRITE_CALENDAR, "WRITE_CALENDAR")
+            .setPermission(android.Manifest.permission.WRITE_CALENDAR)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_WIFI_SCAN, OPSTR_WIFI_SCAN, "WIFI_SCAN")
+            .setSwitchCode(OP_COARSE_LOCATION)
+            .setPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
+            .setRestriction(UserManager.DISALLOW_SHARE_LOCATION)
+            .setAllowSystemRestrictionBypass(new RestrictionBypass(false, true, false))
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_POST_NOTIFICATION, OPSTR_POST_NOTIFICATION, "POST_NOTIFICATION")
+            .setPermission(android.Manifest.permission.POST_NOTIFICATIONS)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_NEIGHBORING_CELLS, OPSTR_NEIGHBORING_CELLS, "NEIGHBORING_CELLS")
+            .setSwitchCode(OP_COARSE_LOCATION).setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_CALL_PHONE, OPSTR_CALL_PHONE, "CALL_PHONE")
+            .setSwitchCode(OP_CALL_PHONE).setPermission(android.Manifest.permission.CALL_PHONE)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_READ_SMS, OPSTR_READ_SMS, "READ_SMS")
+            .setPermission(android.Manifest.permission.READ_SMS)
+            .setRestriction(UserManager.DISALLOW_SMS).setDefaultMode(AppOpsManager.MODE_ALLOWED)
+            .setDisableReset(true).build(),
+        new AppOpInfo.Builder(OP_WRITE_SMS, OPSTR_WRITE_SMS, "WRITE_SMS")
+            .setRestriction(UserManager.DISALLOW_SMS)
+            .setDefaultMode(AppOpsManager.MODE_IGNORED).setDisableReset(true).build(),
+        new AppOpInfo.Builder(OP_RECEIVE_SMS, OPSTR_RECEIVE_SMS, "RECEIVE_SMS")
+            .setPermission(android.Manifest.permission.RECEIVE_SMS)
+            .setRestriction(UserManager.DISALLOW_SMS)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).setDisableReset(true).build(),
+        new AppOpInfo.Builder(OP_RECEIVE_EMERGECY_SMS, OPSTR_RECEIVE_EMERGENCY_BROADCAST,
+                "RECEIVE_EMERGENCY_BROADCAST").setSwitchCode(OP_RECEIVE_SMS)
+            .setPermission(android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_RECEIVE_MMS, OPSTR_RECEIVE_MMS, "RECEIVE_MMS")
+            .setPermission(android.Manifest.permission.RECEIVE_MMS)
+            .setRestriction(UserManager.DISALLOW_SMS).setDefaultMode(AppOpsManager.MODE_ALLOWED)
+            .build(),
+        new AppOpInfo.Builder(OP_RECEIVE_WAP_PUSH, OPSTR_RECEIVE_WAP_PUSH, "RECEIVE_WAP_PUSH")
+            .setPermission(android.Manifest.permission.RECEIVE_WAP_PUSH)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).setDisableReset(true).build(),
+        new AppOpInfo.Builder(OP_SEND_SMS, OPSTR_SEND_SMS, "SEND_SMS")
+            .setPermission(android.Manifest.permission.SEND_SMS)
+            .setRestriction(UserManager.DISALLOW_SMS).setDefaultMode(AppOpsManager.MODE_ALLOWED)
+            .setDisableReset(true).build(),
+        new AppOpInfo.Builder(OP_READ_ICC_SMS, OPSTR_READ_ICC_SMS, "READ_ICC_SMS")
+            .setSwitchCode(OP_READ_SMS).setPermission(android.Manifest.permission.READ_SMS)
+            .setRestriction(UserManager.DISALLOW_SMS).setDefaultMode(AppOpsManager.MODE_ALLOWED)
+            .build(),
+        new AppOpInfo.Builder(OP_WRITE_ICC_SMS, OPSTR_WRITE_ICC_SMS, "WRITE_ICC_SMS")
+            .setSwitchCode(OP_WRITE_SMS).setRestriction(UserManager.DISALLOW_SMS)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_WRITE_SETTINGS, OPSTR_WRITE_SETTINGS, "WRITE_SETTINGS")
+            .setPermission(android.Manifest.permission.WRITE_SETTINGS).build(),
+        new AppOpInfo.Builder(OP_SYSTEM_ALERT_WINDOW, OPSTR_SYSTEM_ALERT_WINDOW,
+                "SYSTEM_ALERT_WINDOW")
+            .setPermission(android.Manifest.permission.SYSTEM_ALERT_WINDOW)
+            .setRestriction(UserManager.DISALLOW_CREATE_WINDOWS)
+            .setAllowSystemRestrictionBypass(new RestrictionBypass(false, true, false))
+            .setDefaultMode(getSystemAlertWindowDefault()).build(),
+        new AppOpInfo.Builder(OP_ACCESS_NOTIFICATIONS, OPSTR_ACCESS_NOTIFICATIONS,
+                "ACCESS_NOTIFICATIONS")
+            .setPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_CAMERA, OPSTR_CAMERA, "CAMERA")
+            .setPermission(android.Manifest.permission.CAMERA)
+            .setRestriction(UserManager.DISALLOW_CAMERA)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_RECORD_AUDIO, OPSTR_RECORD_AUDIO, "RECORD_AUDIO")
+            .setPermission(android.Manifest.permission.RECORD_AUDIO)
+            .setRestriction(UserManager.DISALLOW_RECORD_AUDIO)
+            .setAllowSystemRestrictionBypass(new RestrictionBypass(false, false, true))
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_PLAY_AUDIO, OPSTR_PLAY_AUDIO, "PLAY_AUDIO")
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_READ_CLIPBOARD, OPSTR_READ_CLIPBOARD, "READ_CLIPBOARD")
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_WRITE_CLIPBOARD, OPSTR_WRITE_CLIPBOARD, "WRITE_CLIPBOARD")
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_TAKE_MEDIA_BUTTONS, OPSTR_TAKE_MEDIA_BUTTONS, "TAKE_MEDIA_BUTTONS")
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED)
+            .build(),
+        new AppOpInfo.Builder(OP_TAKE_AUDIO_FOCUS, OPSTR_TAKE_AUDIO_FOCUS, "TAKE_AUDIO_FOCUS")
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_AUDIO_MASTER_VOLUME, OPSTR_AUDIO_MASTER_VOLUME,
+                "AUDIO_MASTER_VOLUME").setSwitchCode(OP_AUDIO_MASTER_VOLUME)
+            .setRestriction(UserManager.DISALLOW_ADJUST_VOLUME)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_AUDIO_VOICE_VOLUME, OPSTR_AUDIO_VOICE_VOLUME, "AUDIO_VOICE_VOLUME")
+            .setRestriction(UserManager.DISALLOW_ADJUST_VOLUME)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_AUDIO_RING_VOLUME, OPSTR_AUDIO_RING_VOLUME, "AUDIO_RING_VOLUME")
+            .setRestriction(UserManager.DISALLOW_ADJUST_VOLUME)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_AUDIO_MEDIA_VOLUME, OPSTR_AUDIO_MEDIA_VOLUME, "AUDIO_MEDIA_VOLUME")
+            .setRestriction(UserManager.DISALLOW_ADJUST_VOLUME)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_AUDIO_ALARM_VOLUME, OPSTR_AUDIO_ALARM_VOLUME, "AUDIO_ALARM_VOLUME")
+            .setRestriction(UserManager.DISALLOW_ADJUST_VOLUME)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_AUDIO_NOTIFICATION_VOLUME, OPSTR_AUDIO_NOTIFICATION_VOLUME,
+                "AUDIO_NOTIFICATION_VOLUME").setSwitchCode(OP_AUDIO_NOTIFICATION_VOLUME)
+            .setRestriction(UserManager.DISALLOW_ADJUST_VOLUME)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_AUDIO_BLUETOOTH_VOLUME, OPSTR_AUDIO_BLUETOOTH_VOLUME,
+                "AUDIO_BLUETOOTH_VOLUME").setRestriction(UserManager.DISALLOW_ADJUST_VOLUME)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_WAKE_LOCK, OPSTR_WAKE_LOCK, "WAKE_LOCK")
+            .setPermission(android.Manifest.permission.WAKE_LOCK)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_MONITOR_LOCATION, OPSTR_MONITOR_LOCATION, "MONITOR_LOCATION")
+            .setSwitchCode(OP_COARSE_LOCATION)
+            .setRestriction(UserManager.DISALLOW_SHARE_LOCATION)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_MONITOR_HIGH_POWER_LOCATION, OPSTR_MONITOR_HIGH_POWER_LOCATION,
+                "MONITOR_HIGH_POWER_LOCATION").setSwitchCode(OP_COARSE_LOCATION)
+            .setRestriction(UserManager.DISALLOW_SHARE_LOCATION)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_GET_USAGE_STATS, OPSTR_GET_USAGE_STATS, "GET_USAGE_STATS")
+            .setPermission(android.Manifest.permission.PACKAGE_USAGE_STATS).build(),
+        new AppOpInfo.Builder(OP_MUTE_MICROPHONE, OPSTR_MUTE_MICROPHONE, "MUTE_MICROPHONE")
+            .setRestriction(UserManager.DISALLOW_UNMUTE_MICROPHONE)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_TOAST_WINDOW, OPSTR_TOAST_WINDOW, "TOAST_WINDOW")
+            .setRestriction(UserManager.DISALLOW_CREATE_WINDOWS)
+            .setAllowSystemRestrictionBypass(new RestrictionBypass(false, true, false))
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_PROJECT_MEDIA, OPSTR_PROJECT_MEDIA, "PROJECT_MEDIA")
+            .setDefaultMode(AppOpsManager.MODE_IGNORED).build(),
+        new AppOpInfo.Builder(OP_ACTIVATE_VPN, OPSTR_ACTIVATE_VPN, "ACTIVATE_VPN")
+            .setDefaultMode(AppOpsManager.MODE_IGNORED).build(),
+        new AppOpInfo.Builder(OP_WRITE_WALLPAPER, OPSTR_WRITE_WALLPAPER, "WRITE_WALLPAPER")
+            .setRestriction(UserManager.DISALLOW_WALLPAPER)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_ASSIST_STRUCTURE, OPSTR_ASSIST_STRUCTURE, "ASSIST_STRUCTURE")
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_ASSIST_SCREENSHOT, OPSTR_ASSIST_SCREENSHOT, "ASSIST_SCREENSHOT")
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED)
+            .build(),
+        new AppOpInfo.Builder(OP_READ_PHONE_STATE, OPSTR_READ_PHONE_STATE, "READ_PHONE_STATE")
+            .setPermission(Manifest.permission.READ_PHONE_STATE)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_ADD_VOICEMAIL, OPSTR_ADD_VOICEMAIL, "ADD_VOICEMAIL")
+            .setPermission(Manifest.permission.ADD_VOICEMAIL)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_USE_SIP, OPSTR_USE_SIP, "USE_SIP")
+            .setPermission(Manifest.permission.USE_SIP)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_PROCESS_OUTGOING_CALLS, OPSTR_PROCESS_OUTGOING_CALLS,
+                "PROCESS_OUTGOING_CALLS").setSwitchCode(OP_PROCESS_OUTGOING_CALLS)
+            .setPermission(Manifest.permission.PROCESS_OUTGOING_CALLS)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_USE_FINGERPRINT, OPSTR_USE_FINGERPRINT, "USE_FINGERPRINT")
+            .setPermission(Manifest.permission.USE_FINGERPRINT)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_BODY_SENSORS, OPSTR_BODY_SENSORS, "BODY_SENSORS")
+            .setPermission(Manifest.permission.BODY_SENSORS)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_READ_CELL_BROADCASTS, OPSTR_READ_CELL_BROADCASTS,
+                "READ_CELL_BROADCASTS").setPermission(Manifest.permission.READ_CELL_BROADCASTS)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).setDisableReset(true).build(),
+        new AppOpInfo.Builder(OP_MOCK_LOCATION, OPSTR_MOCK_LOCATION, "MOCK_LOCATION")
+            .setDefaultMode(AppOpsManager.MODE_ERRORED).build(),
+        new AppOpInfo.Builder(OP_READ_EXTERNAL_STORAGE, OPSTR_READ_EXTERNAL_STORAGE,
+                "READ_EXTERNAL_STORAGE").setPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_WRITE_EXTERNAL_STORAGE, OPSTR_WRITE_EXTERNAL_STORAGE,
+                "WRITE_EXTERNAL_STORAGE").setPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_TURN_SCREEN_ON, OPSTR_TURN_SCREEN_ON, "TURN_SCREEN_ON")
+            .setPermission(Manifest.permission.TURN_SCREEN_ON)
+            .setDefaultMode(AppOpsManager.MODE_ERRORED).build(),
+        new AppOpInfo.Builder(OP_GET_ACCOUNTS, OPSTR_GET_ACCOUNTS, "GET_ACCOUNTS")
+            .setPermission(Manifest.permission.GET_ACCOUNTS)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_RUN_IN_BACKGROUND, OPSTR_RUN_IN_BACKGROUND, "RUN_IN_BACKGROUND")
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED)
+            .build(),
+        new AppOpInfo.Builder(OP_AUDIO_ACCESSIBILITY_VOLUME, OPSTR_AUDIO_ACCESSIBILITY_VOLUME,
+                "AUDIO_ACCESSIBILITY_VOLUME")
+            .setRestriction(UserManager.DISALLOW_ADJUST_VOLUME)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_READ_PHONE_NUMBERS, OPSTR_READ_PHONE_NUMBERS, "READ_PHONE_NUMBERS")
+            .setPermission(Manifest.permission.READ_PHONE_NUMBERS)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_REQUEST_INSTALL_PACKAGES, OPSTR_REQUEST_INSTALL_PACKAGES,
+                "REQUEST_INSTALL_PACKAGES").setSwitchCode(OP_REQUEST_INSTALL_PACKAGES)
+            .setPermission(Manifest.permission.REQUEST_INSTALL_PACKAGES).build(),
+        new AppOpInfo.Builder(OP_PICTURE_IN_PICTURE, OPSTR_PICTURE_IN_PICTURE, "PICTURE_IN_PICTURE")
+            .setSwitchCode(OP_PICTURE_IN_PICTURE).setDefaultMode(AppOpsManager.MODE_ALLOWED)
+            .build(),
+        new AppOpInfo.Builder(OP_INSTANT_APP_START_FOREGROUND, OPSTR_INSTANT_APP_START_FOREGROUND,
+                "INSTANT_APP_START_FOREGROUND")
+            .setPermission(Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE).build(),
+        new AppOpInfo.Builder(OP_ANSWER_PHONE_CALLS, OPSTR_ANSWER_PHONE_CALLS, "ANSWER_PHONE_CALLS")
+            .setSwitchCode(OP_ANSWER_PHONE_CALLS)
+            .setPermission(Manifest.permission.ANSWER_PHONE_CALLS)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_RUN_ANY_IN_BACKGROUND, OPSTR_RUN_ANY_IN_BACKGROUND,
+                "RUN_ANY_IN_BACKGROUND")
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_CHANGE_WIFI_STATE, OPSTR_CHANGE_WIFI_STATE, "CHANGE_WIFI_STATE")
+            .setSwitchCode(OP_CHANGE_WIFI_STATE)
+            .setPermission(Manifest.permission.CHANGE_WIFI_STATE)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_REQUEST_DELETE_PACKAGES, OPSTR_REQUEST_DELETE_PACKAGES,
+                "REQUEST_DELETE_PACKAGES")
+            .setPermission(Manifest.permission.REQUEST_DELETE_PACKAGES)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_BIND_ACCESSIBILITY_SERVICE, OPSTR_BIND_ACCESSIBILITY_SERVICE,
+                "BIND_ACCESSIBILITY_SERVICE")
+            .setPermission(Manifest.permission.BIND_ACCESSIBILITY_SERVICE)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_ACCEPT_HANDOVER, OPSTR_ACCEPT_HANDOVER, "ACCEPT_HANDOVER")
+            .setSwitchCode(OP_ACCEPT_HANDOVER)
+            .setPermission(Manifest.permission.ACCEPT_HANDOVER)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_MANAGE_IPSEC_TUNNELS, OPSTR_MANAGE_IPSEC_TUNNELS,
+                "MANAGE_IPSEC_TUNNELS")
+            .setPermission(Manifest.permission.MANAGE_IPSEC_TUNNELS)
+            .setDefaultMode(AppOpsManager.MODE_ERRORED).build(),
+        new AppOpInfo.Builder(OP_START_FOREGROUND, OPSTR_START_FOREGROUND, "START_FOREGROUND")
+            .setPermission(Manifest.permission.FOREGROUND_SERVICE)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_BLUETOOTH_SCAN, OPSTR_BLUETOOTH_SCAN, "BLUETOOTH_SCAN")
+            .setPermission(Manifest.permission.BLUETOOTH_SCAN)
+            .setAllowSystemRestrictionBypass(new RestrictionBypass(false, true, false))
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_USE_BIOMETRIC, OPSTR_USE_BIOMETRIC, "USE_BIOMETRIC")
+            .setPermission(Manifest.permission.USE_BIOMETRIC)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_ACTIVITY_RECOGNITION, OPSTR_ACTIVITY_RECOGNITION,
+                "ACTIVITY_RECOGNITION")
+            .setPermission(Manifest.permission.ACTIVITY_RECOGNITION)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_SMS_FINANCIAL_TRANSACTIONS, OPSTR_SMS_FINANCIAL_TRANSACTIONS,
+                "SMS_FINANCIAL_TRANSACTIONS")
+            .setPermission(Manifest.permission.SMS_FINANCIAL_TRANSACTIONS)
+            .setRestriction(UserManager.DISALLOW_SMS).build(),
+        new AppOpInfo.Builder(OP_READ_MEDIA_AUDIO, OPSTR_READ_MEDIA_AUDIO, "READ_MEDIA_AUDIO")
+            .setPermission(Manifest.permission.READ_MEDIA_AUDIO)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_WRITE_MEDIA_AUDIO, OPSTR_WRITE_MEDIA_AUDIO, "WRITE_MEDIA_AUDIO")
+            .setDefaultMode(AppOpsManager.MODE_ERRORED).build(),
+        new AppOpInfo.Builder(OP_READ_MEDIA_VIDEO, OPSTR_READ_MEDIA_VIDEO, "READ_MEDIA_VIDEO")
+            .setPermission(Manifest.permission.READ_MEDIA_VIDEO)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_WRITE_MEDIA_VIDEO, OPSTR_WRITE_MEDIA_VIDEO, "WRITE_MEDIA_VIDEO")
+            .setDefaultMode(AppOpsManager.MODE_ERRORED).setDisableReset(true).build(),
+        new AppOpInfo.Builder(OP_READ_MEDIA_IMAGES, OPSTR_READ_MEDIA_IMAGES, "READ_MEDIA_IMAGES")
+            .setPermission(Manifest.permission.READ_MEDIA_IMAGES)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_WRITE_MEDIA_IMAGES, OPSTR_WRITE_MEDIA_IMAGES, "WRITE_MEDIA_IMAGES")
+            .setDefaultMode(AppOpsManager.MODE_ERRORED).setDisableReset(true).build(),
+        new AppOpInfo.Builder(OP_LEGACY_STORAGE, OPSTR_LEGACY_STORAGE, "LEGACY_STORAGE")
+            .setDisableReset(true).build(),
+        new AppOpInfo.Builder(OP_ACCESS_ACCESSIBILITY, OPSTR_ACCESS_ACCESSIBILITY,
+                "ACCESS_ACCESSIBILITY").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_READ_DEVICE_IDENTIFIERS, OPSTR_READ_DEVICE_IDENTIFIERS,
+                "READ_DEVICE_IDENTIFIERS").setDefaultMode(AppOpsManager.MODE_ERRORED).build(),
+        new AppOpInfo.Builder(OP_ACCESS_MEDIA_LOCATION, OPSTR_ACCESS_MEDIA_LOCATION,
+                "ACCESS_MEDIA_LOCATION").setPermission(Manifest.permission.ACCESS_MEDIA_LOCATION)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_QUERY_ALL_PACKAGES, OPSTR_QUERY_ALL_PACKAGES, "QUERY_ALL_PACKAGES")
+            .build(),
+        new AppOpInfo.Builder(OP_MANAGE_EXTERNAL_STORAGE, OPSTR_MANAGE_EXTERNAL_STORAGE,
+                "MANAGE_EXTERNAL_STORAGE")
+            .setPermission(Manifest.permission.MANAGE_EXTERNAL_STORAGE).build(),
+        new AppOpInfo.Builder(OP_INTERACT_ACROSS_PROFILES, OPSTR_INTERACT_ACROSS_PROFILES,
+                "INTERACT_ACROSS_PROFILES")
+            .setPermission(android.Manifest.permission.INTERACT_ACROSS_PROFILES).build(),
+        new AppOpInfo.Builder(OP_ACTIVATE_PLATFORM_VPN, OPSTR_ACTIVATE_PLATFORM_VPN,
+                "ACTIVATE_PLATFORM_VPN").setDefaultMode(AppOpsManager.MODE_IGNORED).build(),
+        new AppOpInfo.Builder(OP_LOADER_USAGE_STATS, OPSTR_LOADER_USAGE_STATS, "LOADER_USAGE_STATS")
+            .setPermission(android.Manifest.permission.LOADER_USAGE_STATS).build(),
+        new AppOpInfo.Builder(OP_NONE, "", "").setDefaultMode(AppOpsManager.MODE_IGNORED).build(),
+        new AppOpInfo.Builder(OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
+                OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, "AUTO_REVOKE_PERMISSIONS_IF_UNUSED")
+            .build(),
+        new AppOpInfo.Builder(OP_AUTO_REVOKE_MANAGED_BY_INSTALLER,
+                OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER, "AUTO_REVOKE_MANAGED_BY_INSTALLER")
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_NO_ISOLATED_STORAGE, OPSTR_NO_ISOLATED_STORAGE,
+                "NO_ISOLATED_STORAGE").setDefaultMode(AppOpsManager.MODE_ERRORED)
+            .setDisableReset(true).build(),
+        new AppOpInfo.Builder(OP_PHONE_CALL_MICROPHONE, OPSTR_PHONE_CALL_MICROPHONE,
+                "PHONE_CALL_MICROPHONE").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_PHONE_CALL_CAMERA, OPSTR_PHONE_CALL_CAMERA, "PHONE_CALL_CAMERA")
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_RECORD_AUDIO_HOTWORD, OPSTR_RECORD_AUDIO_HOTWORD,
+                "RECORD_AUDIO_HOTWORD").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_MANAGE_ONGOING_CALLS, OPSTR_MANAGE_ONGOING_CALLS,
+                "MANAGE_ONGOING_CALLS").setPermission(Manifest.permission.MANAGE_ONGOING_CALLS)
+            .setDisableReset(true).build(),
+        new AppOpInfo.Builder(OP_MANAGE_CREDENTIALS, OPSTR_MANAGE_CREDENTIALS, "MANAGE_CREDENTIALS")
+            .build(),
+        new AppOpInfo.Builder(OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
+                OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, "USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER")
+            .setPermission(Manifest.permission.USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER)
+            .setDisableReset(true).build(),
+        new AppOpInfo.Builder(OP_RECORD_AUDIO_OUTPUT, OPSTR_RECORD_AUDIO_OUTPUT,
+                "RECORD_AUDIO_OUTPUT").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_SCHEDULE_EXACT_ALARM, OPSTR_SCHEDULE_EXACT_ALARM,
+                "SCHEDULE_EXACT_ALARM").setPermission(Manifest.permission.SCHEDULE_EXACT_ALARM)
+            .build(),
+        new AppOpInfo.Builder(OP_FINE_LOCATION_SOURCE, OPSTR_FINE_LOCATION_SOURCE,
+                "FINE_LOCATION_SOURCE").setSwitchCode(OP_FINE_LOCATION)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_COARSE_LOCATION_SOURCE, OPSTR_COARSE_LOCATION_SOURCE,
+                "COARSE_LOCATION_SOURCE").setSwitchCode(OP_COARSE_LOCATION)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_MANAGE_MEDIA, OPSTR_MANAGE_MEDIA, "MANAGE_MEDIA")
+            .setPermission(Manifest.permission.MANAGE_MEDIA).build(),
+        new AppOpInfo.Builder(OP_BLUETOOTH_CONNECT, OPSTR_BLUETOOTH_CONNECT, "BLUETOOTH_CONNECT")
+            .setPermission(Manifest.permission.BLUETOOTH_CONNECT)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_UWB_RANGING, OPSTR_UWB_RANGING, "UWB_RANGING")
+            .setPermission(Manifest.permission.UWB_RANGING)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_ACTIVITY_RECOGNITION_SOURCE, OPSTR_ACTIVITY_RECOGNITION_SOURCE,
+                "ACTIVITY_RECOGNITION_SOURCE")
+            .setSwitchCode(OP_ACTIVITY_RECOGNITION).setDefaultMode(AppOpsManager.MODE_ALLOWED)
+            .build(),
+        new AppOpInfo.Builder(OP_BLUETOOTH_ADVERTISE, OPSTR_BLUETOOTH_ADVERTISE,
+                "BLUETOOTH_ADVERTISE").setPermission(Manifest.permission.BLUETOOTH_ADVERTISE)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_RECORD_INCOMING_PHONE_AUDIO, OPSTR_RECORD_INCOMING_PHONE_AUDIO,
+                "RECORD_INCOMING_PHONE_AUDIO").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_NEARBY_WIFI_DEVICES, OPSTR_NEARBY_WIFI_DEVICES,
+                "NEARBY_WIFI_DEVICES").setPermission(Manifest.permission.NEARBY_WIFI_DEVICES)
+            .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_ESTABLISH_VPN_SERVICE, OPSTR_ESTABLISH_VPN_SERVICE,
+                "ESTABLISH_VPN_SERVICE").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_ESTABLISH_VPN_MANAGER, OPSTR_ESTABLISH_VPN_MANAGER,
+                "ESTABLISH_VPN_MANAGER").setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+        new AppOpInfo.Builder(OP_ACCESS_RESTRICTED_SETTINGS, OPSTR_ACCESS_RESTRICTED_SETTINGS,
+                "ACCESS_RESTRICTED_SETTINGS").setDefaultMode(AppOpsManager.MODE_ALLOWED)
+            .setDisableReset(true).setRestrictRead(true).build(),
+        new AppOpInfo.Builder(OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO,
+                "RECEIVE_SOUNDTRIGGER_AUDIO").setDefaultMode(AppOpsManager.MODE_ALLOWED).build()
     };
 
     /**
@@ -3101,46 +2316,18 @@
     private static final @ShouldCollectNoteOp byte[] sAppOpsToNote = new byte[_NUM_OP];
 
     static {
-        if (sOpToSwitch.length != _NUM_OP) {
-            throw new IllegalStateException("sOpToSwitch length " + sOpToSwitch.length
+        if (sAppOpInfos.length != _NUM_OP) {
+            throw new IllegalStateException("mAppOpInfos length " + sAppOpInfos.length
                     + " should be " + _NUM_OP);
         }
-        if (sOpToString.length != _NUM_OP) {
-            throw new IllegalStateException("sOpToString length " + sOpToString.length
-                    + " should be " + _NUM_OP);
-        }
-        if (sOpNames.length != _NUM_OP) {
-            throw new IllegalStateException("sOpNames length " + sOpNames.length
-                    + " should be " + _NUM_OP);
-        }
-        if (sOpPerms.length != _NUM_OP) {
-            throw new IllegalStateException("sOpPerms length " + sOpPerms.length
-                    + " should be " + _NUM_OP);
-        }
-        if (sOpDefaultMode.length != _NUM_OP) {
-            throw new IllegalStateException("sOpDefaultMode length " + sOpDefaultMode.length
-                    + " should be " + _NUM_OP);
-        }
-        if (sOpDisableReset.length != _NUM_OP) {
-            throw new IllegalStateException("sOpDisableReset length " + sOpDisableReset.length
-                    + " should be " + _NUM_OP);
-        }
-        if (sOpRestrictions.length != _NUM_OP) {
-            throw new IllegalStateException("sOpRestrictions length " + sOpRestrictions.length
-                    + " should be " + _NUM_OP);
-        }
-        if (sOpAllowSystemRestrictionBypass.length != _NUM_OP) {
-            throw new IllegalStateException("sOpAllowSYstemRestrictionsBypass length "
-                    + sOpRestrictions.length + " should be " + _NUM_OP);
-        }
         for (int i=0; i<_NUM_OP; i++) {
-            if (sOpToString[i] != null) {
-                sOpStrToOp.put(sOpToString[i], i);
+            if (sAppOpInfos[i].name != null) {
+                sOpStrToOp.put(sAppOpInfos[i].name, i);
             }
         }
         for (int op : RUNTIME_AND_APPOP_PERMISSIONS_OPS) {
-            if (sOpPerms[op] != null) {
-                sPermToOp.put(sOpPerms[op], op);
+            if (sAppOpInfos[op].permission != null) {
+                sPermToOp.put(sAppOpInfos[op].permission, op);
             }
         }
 
@@ -3170,7 +2357,7 @@
      */
     @UnsupportedAppUsage
     public static int opToSwitch(int op) {
-        return sOpToSwitch[op];
+        return sAppOpInfos[op].switchCode;
     }
 
     /**
@@ -3180,7 +2367,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     public static String opToName(int op) {
         if (op == OP_NONE) return "NONE";
-        return op < sOpNames.length ? sOpNames[op] : ("Unknown(" + op + ")");
+        return op < sAppOpInfos.length ? sAppOpInfos[op].simpleName : ("Unknown(" + op + ")");
     }
 
     /**
@@ -3189,15 +2376,15 @@
      * @hide
      */
     public static @NonNull String opToPublicName(int op) {
-        return sOpToString[op];
+        return sAppOpInfos[op].name;
     }
 
     /**
      * @hide
      */
     public static int strDebugOpToOp(String op) {
-        for (int i=0; i<sOpNames.length; i++) {
-            if (sOpNames[i].equals(op)) {
+        for (int i = 0; i < sAppOpInfos.length; i++) {
+            if (sAppOpInfos[i].simpleName.equals(op)) {
                 return i;
             }
         }
@@ -3211,7 +2398,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     @TestApi
     public static String opToPermission(int op) {
-        return sOpPerms[op];
+        return sAppOpInfos[op].permission;
     }
 
     /**
@@ -3232,7 +2419,7 @@
      * @hide
      */
     public static String opToRestriction(int op) {
-        return sOpRestrictions[op];
+        return sAppOpInfos[op].restriction;
     }
 
     /**
@@ -3254,7 +2441,7 @@
      * @hide
      */
     public static RestrictionBypass opAllowSystemBypassRestriction(int op) {
-        return sOpAllowSystemRestrictionBypass[op];
+        return sAppOpInfos[op].allowSystemRestrictionBypass;
     }
 
     /**
@@ -3262,7 +2449,7 @@
      * @hide
      */
     public static @Mode int opToDefaultMode(int op) {
-        return sOpDefaultMode[op];
+        return sAppOpInfos[op].defaultMode;
     }
 
     /**
@@ -3295,7 +2482,7 @@
      * @hide
      */
     public static boolean opRestrictsRead(int op) {
-        return sOpRestrictRead[op];
+        return sAppOpInfos[op].restrictRead;
     }
 
     /**
@@ -3303,7 +2490,7 @@
      * @hide
      */
     public static boolean opAllowsReset(int op) {
-        return !sOpDisableReset[op];
+        return !sAppOpInfos[op].disableReset;
     }
 
     /**
@@ -4436,7 +3623,7 @@
          * @return This entry's op string name, such as {@link #OPSTR_COARSE_LOCATION}.
          */
         public @NonNull String getOpStr() {
-            return sOpToString[mOp];
+            return sAppOpInfos[mOp].name;
         }
 
         /**
@@ -6553,7 +5740,7 @@
             if (mHistoricalOps == null) {
                 mHistoricalOps = new ArrayMap<>();
             }
-            final String opStr = sOpToString[opCode];
+            final String opStr = sAppOpInfos[opCode].name;
             HistoricalOp op = mHistoricalOps.get(opStr);
             if (op == null) {
                 op = new HistoricalOp(opCode);
@@ -6898,7 +6085,7 @@
          * @return The op name.
          */
         public @NonNull String getOpName() {
-            return sOpToString[mOp];
+            return sAppOpInfos[mOp].name;
         }
 
         /** @hide */
@@ -7912,7 +7099,7 @@
         if (opCode == null) {
             return null;
         }
-        return sOpToString[opCode];
+        return sAppOpInfos[opCode].name;
     }
 
     /**
@@ -8007,8 +7194,8 @@
                         if (callback instanceof OnOpChangedInternalListener) {
                             ((OnOpChangedInternalListener)callback).onOpChanged(op, packageName);
                         }
-                        if (sOpToString[op] != null) {
-                            callback.onOpChanged(sOpToString[op], packageName);
+                        if (sAppOpInfos[op].name != null) {
+                            callback.onOpChanged(sAppOpInfos[op].name, packageName);
                         }
                     }
                 };
@@ -8096,8 +7283,8 @@
                             ((OnOpActiveChangedInternalListener) callback).onOpActiveChanged(op,
                                     uid, packageName, active);
                         }
-                        if (sOpToString[op] != null) {
-                            callback.onOpActiveChanged(sOpToString[op], uid, packageName,
+                        if (sAppOpInfos[op].name != null) {
+                            callback.onOpActiveChanged(sAppOpInfos[op].name, uid, packageName,
                                     attributionTag, active, attributionFlags, attributionChainId);
                         }
                     });
@@ -8273,7 +7460,8 @@
     }
 
     private String buildSecurityExceptionMsg(int op, int uid, String packageName) {
-        return packageName + " from uid " + uid + " not allowed to perform " + sOpNames[op];
+        return packageName + " from uid " + uid + " not allowed to perform " +
+            sAppOpInfos[op].simpleName;
     }
 
     /**
@@ -8578,9 +7766,9 @@
     public int noteProxyOp(int op, @Nullable String proxiedPackageName, int proxiedUid,
             @Nullable String proxiedAttributionTag, @Nullable String message) {
         return noteProxyOp(op, new AttributionSource(mContext.getAttributionSource(),
-                new AttributionSource(proxiedUid, /*pid*/ -1, proxiedPackageName,
-                        proxiedAttributionTag, mContext.getAttributionSource().getToken())),
-                        message, /*skipProxyOperation*/ false);
+                new AttributionSource(proxiedUid, proxiedPackageName, proxiedAttributionTag,
+                        mContext.getAttributionSource().getToken())), message,
+                        /*skipProxyOperation*/ false);
     }
 
     /**
@@ -8633,7 +7821,7 @@
                     + attributionSource.getUid() + " or calling package "
                     + attributionSource.getNextPackageName() + " from uid "
                     + attributionSource.getNextUid() + " not allowed to perform "
-                    + sOpNames[op]);
+                    + sAppOpInfos[op].simpleName);
         }
         return mode;
     }
@@ -8664,7 +7852,7 @@
     public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName,
             int proxiedUid, @Nullable String proxiedAttributionTag, @Nullable String message) {
         return noteProxyOpNoThrow(strOpToOp(op), new AttributionSource(
-                mContext.getAttributionSource(), new AttributionSource(proxiedUid, /*pid*/ -1,
+                mContext.getAttributionSource(), new AttributionSource(proxiedUid,
                         proxiedPackageName, proxiedAttributionTag, mContext.getAttributionSource()
                         .getToken())), message,/*skipProxyOperation*/ false);
     }
@@ -9076,9 +8264,9 @@
     public int startProxyOp(@NonNull String op, int proxiedUid, @NonNull String proxiedPackageName,
             @Nullable String proxiedAttributionTag, @Nullable String message) {
         return startProxyOp(op, new AttributionSource(mContext.getAttributionSource(),
-                new AttributionSource(proxiedUid, /*pid*/ -1, proxiedPackageName,
-                        proxiedAttributionTag, mContext.getAttributionSource().getToken())),
-                        message, /*skipProxyOperation*/ false);
+                new AttributionSource(proxiedUid, proxiedPackageName, proxiedAttributionTag,
+                        mContext.getAttributionSource().getToken())), message,
+                        /*skipProxyOperation*/ false);
     }
 
     /**
@@ -9124,7 +8312,7 @@
             @Nullable String message) {
         return startProxyOpNoThrow(AppOpsManager.strOpToOp(op), new AttributionSource(
                 mContext.getAttributionSource(), new AttributionSource(proxiedUid,
-                        /*pid*/ -1, proxiedPackageName, proxiedAttributionTag,
+                        proxiedPackageName, proxiedAttributionTag,
                         mContext.getAttributionSource().getToken())), message,
                         /*skipProxyOperation*/ false);
     }
@@ -9270,9 +8458,8 @@
     public void finishProxyOp(@NonNull String op, int proxiedUid,
             @NonNull String proxiedPackageName, @Nullable String proxiedAttributionTag) {
         finishProxyOp(op, new AttributionSource(mContext.getAttributionSource(),
-                new AttributionSource(proxiedUid, /*pid*/ -1, proxiedPackageName,
-                        proxiedAttributionTag, mContext.getAttributionSource().getToken())),
-                        /*skipProxyOperation*/ false);
+                new AttributionSource(proxiedUid, proxiedPackageName,  proxiedAttributionTag,
+                        mContext.getAttributionSource().getToken())), /*skipProxyOperation*/ false);
     }
 
     /**
@@ -10085,10 +9272,13 @@
      */
     @SystemApi
     public static String[] getOpStrs() {
-        return Arrays.copyOf(sOpToString, sOpToString.length);
+        String[] opStrs = new String[sAppOpInfos.length];
+        for(int i = 0; i < sAppOpInfos.length; i++) {
+            opStrs[i] = sAppOpInfos[i].name;
+        }
+        return opStrs;
     }
 
-
     /**
      * @return number of App ops
      * @hide
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index c1a2183..4c7bc6d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3227,9 +3227,7 @@
             @Nullable AttributionSource nextAttributionSource,
             @Nullable Set<String> renouncedPermissions) {
         AttributionSource attributionSource = new AttributionSource(Process.myUid(),
-                Process.myPid(), mOpPackageName, attributionTag,
-                (renouncedPermissions != null) ? renouncedPermissions.toArray(new String[0]) : null,
-                nextAttributionSource);
+                mOpPackageName, attributionTag, renouncedPermissions, nextAttributionSource);
         // If we want to access protected data on behalf of another app we need to
         // tell the OS that we opt in to participate in the attribution chain.
         if (nextAttributionSource != null) {
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 0e89b25..b9ad595 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -124,8 +124,13 @@
     /**
      * The maximum length for text fields in a NotificationChannel. Fields will be truncated at this
      * limit.
+     * @hide
      */
-    private static final int MAX_TEXT_LENGTH = 1000;
+    public static final int MAX_TEXT_LENGTH = 1000;
+    /**
+     * @hide
+     */
+    public static final int MAX_VIBRATION_LENGTH = 1000;
 
     private static final String TAG_CHANNEL = "channel";
     private static final String ATT_NAME = "name";
@@ -283,17 +288,17 @@
      */
     protected NotificationChannel(Parcel in) {
         if (in.readByte() != 0) {
-            mId = in.readString();
+            mId = getTrimmedString(in.readString());
         } else {
             mId = null;
         }
         if (in.readByte() != 0) {
-            mName = in.readString();
+            mName = getTrimmedString(in.readString());
         } else {
             mName = null;
         }
         if (in.readByte() != 0) {
-            mDesc = in.readString();
+            mDesc = getTrimmedString(in.readString());
         } else {
             mDesc = null;
         }
@@ -302,18 +307,22 @@
         mLockscreenVisibility = in.readInt();
         if (in.readByte() != 0) {
             mSound = Uri.CREATOR.createFromParcel(in);
+            mSound = Uri.parse(getTrimmedString(mSound.toString()));
         } else {
             mSound = null;
         }
         mLights = in.readByte() != 0;
         mVibration = in.createLongArray();
+        if (mVibration != null && mVibration.length > MAX_VIBRATION_LENGTH) {
+            mVibration = Arrays.copyOf(mVibration, MAX_VIBRATION_LENGTH);
+        }
         mUserLockedFields = in.readInt();
         mFgServiceShown = in.readByte() != 0;
         mVibrationEnabled = in.readByte() != 0;
         mShowBadge = in.readByte() != 0;
         mDeleted = in.readByte() != 0;
         if (in.readByte() != 0) {
-            mGroup = in.readString();
+            mGroup = getTrimmedString(in.readString());
         } else {
             mGroup = null;
         }
@@ -322,8 +331,8 @@
         mBlockableSystem = in.readBoolean();
         mAllowBubbles = in.readInt();
         mOriginalImportance = in.readInt();
-        mParentId = in.readString();
-        mConversationId = in.readString();
+        mParentId = getTrimmedString(in.readString());
+        mConversationId = getTrimmedString(in.readString());
         mDemoted = in.readBoolean();
         mImportantConvo = in.readBoolean();
         mDeletedTime = in.readLong();
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index f97415c..807bd57 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -20,6 +20,7 @@
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
+import android.content.pm.ParceledListSlice;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -43,8 +44,9 @@
     /**
      * The maximum length for text fields in a NotificationChannelGroup. Fields will be truncated at
      * this limit.
+     * @hide
      */
-    private static final int MAX_TEXT_LENGTH = 1000;
+    public static final int MAX_TEXT_LENGTH = 1000;
 
     private static final String TAG_GROUP = "channelGroup";
     private static final String ATT_NAME = "name";
@@ -66,7 +68,7 @@
     private CharSequence mName;
     private String mDescription;
     private boolean mBlocked;
-    private List<NotificationChannel> mChannels = new ArrayList<>();
+    private ParceledListSlice<NotificationChannel> mChannels;
     // Bitwise representation of fields that have been changed by the user
     private int mUserLockedFields;
 
@@ -90,17 +92,19 @@
      */
     protected NotificationChannelGroup(Parcel in) {
         if (in.readByte() != 0) {
-            mId = in.readString();
+            mId = getTrimmedString(in.readString());
         } else {
             mId = null;
         }
         mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mName = getTrimmedString(mName.toString());
         if (in.readByte() != 0) {
-            mDescription = in.readString();
+            mDescription = getTrimmedString(in.readString());
         } else {
             mDescription = null;
         }
-        in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader(), android.app.NotificationChannel.class);
+        mChannels = in.readParcelable(
+                NotificationChannelGroup.class.getClassLoader(), ParceledListSlice.class);
         mBlocked = in.readBoolean();
         mUserLockedFields = in.readInt();
     }
@@ -120,14 +124,14 @@
         } else {
             dest.writeByte((byte) 0);
         }
-        TextUtils.writeToParcel(mName, dest, flags);
+        TextUtils.writeToParcel(mName.toString(), dest, flags);
         if (mDescription != null) {
             dest.writeByte((byte) 1);
             dest.writeString(mDescription);
         } else {
             dest.writeByte((byte) 0);
         }
-        dest.writeParcelableList(mChannels, flags);
+        dest.writeParcelable(mChannels, flags);
         dest.writeBoolean(mBlocked);
         dest.writeInt(mUserLockedFields);
     }
@@ -157,7 +161,7 @@
      * Returns the list of channels that belong to this group
      */
     public List<NotificationChannel> getChannels() {
-        return mChannels;
+        return mChannels == null ? new ArrayList<>() : mChannels.getList();
     }
 
     /**
@@ -191,15 +195,8 @@
     /**
      * @hide
      */
-    public void addChannel(NotificationChannel channel) {
-        mChannels.add(channel);
-    }
-
-    /**
-     * @hide
-     */
     public void setChannels(List<NotificationChannel> channels) {
-        mChannels = channels;
+        mChannels = new ParceledListSlice<>(channels);
     }
 
     /**
@@ -334,7 +331,7 @@
         proto.write(NotificationChannelGroupProto.NAME, mName.toString());
         proto.write(NotificationChannelGroupProto.DESCRIPTION, mDescription);
         proto.write(NotificationChannelGroupProto.IS_BLOCKED, mBlocked);
-        for (NotificationChannel channel : mChannels) {
+        for (NotificationChannel channel : mChannels.getList()) {
             channel.dumpDebug(proto, NotificationChannelGroupProto.CHANNELS);
         }
         proto.end(token);
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 272e235..3f2fa21 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -100,28 +100,22 @@
     @TestApi
     public AttributionSource(int uid, @Nullable String packageName,
             @Nullable String attributionTag) {
-        this(uid, Process.INVALID_PID, packageName, attributionTag, sDefaultToken);
-    }
-
-    /** @hide */
-    public AttributionSource(int uid, int pid, @Nullable String packageName,
-            @Nullable String attributionTag) {
-        this(uid, pid, packageName, attributionTag, sDefaultToken);
+        this(uid, packageName, attributionTag, sDefaultToken);
     }
 
     /** @hide */
     @TestApi
     public AttributionSource(int uid, @Nullable String packageName,
             @Nullable String attributionTag, @NonNull IBinder token) {
-        this(uid, Process.INVALID_PID, packageName, attributionTag, token,
-                /*renouncedPermissions*/ null, /*next*/ null);
+        this(uid, packageName, attributionTag, token, /*renouncedPermissions*/ null,
+                /*next*/ null);
     }
 
     /** @hide */
-    public AttributionSource(int uid, int pid, @Nullable String packageName,
-            @Nullable String attributionTag, @NonNull IBinder token) {
-        this(uid, pid, packageName, attributionTag, token, /*renouncedPermissions*/ null,
-                /*next*/ null);
+    public AttributionSource(int uid, @Nullable String packageName,
+            @Nullable String attributionTag, @NonNull IBinder token,
+            @Nullable AttributionSource next) {
+        this(uid, packageName, attributionTag, token, /*renouncedPermissions*/ null, next);
     }
 
     /** @hide */
@@ -129,33 +123,26 @@
     public AttributionSource(int uid, @Nullable String packageName,
             @Nullable String attributionTag, @Nullable Set<String> renouncedPermissions,
             @Nullable AttributionSource next) {
-        this(uid, Process.INVALID_PID, packageName, attributionTag, sDefaultToken,
-                (renouncedPermissions != null)
-                ? renouncedPermissions.toArray(new String[0]) : null, /*next*/ next);
+        this(uid, packageName, attributionTag, (renouncedPermissions != null)
+                ? renouncedPermissions.toArray(new String[0]) : null, next);
     }
 
     /** @hide */
     public AttributionSource(@NonNull AttributionSource current, @Nullable AttributionSource next) {
-        this(current.getUid(), current.getPid(), current.getPackageName(),
-                current.getAttributionTag(), current.getToken(),
-                current.mAttributionSourceState.renouncedPermissions, next);
+        this(current.getUid(), current.getPackageName(), current.getAttributionTag(),
+                current.getToken(), current.mAttributionSourceState.renouncedPermissions, next);
     }
 
-    /** @hide */
-    public AttributionSource(int uid, int pid, @Nullable String packageName,
-            @Nullable String attributionTag, @Nullable String[] renouncedPermissions,
-            @Nullable AttributionSource next) {
-        this(uid, pid, packageName, attributionTag, sDefaultToken, renouncedPermissions, next);
+    AttributionSource(int uid, @Nullable String packageName, @Nullable String attributionTag,
+            @Nullable String[] renouncedPermissions, @Nullable AttributionSource next) {
+        this(uid, packageName, attributionTag, sDefaultToken, renouncedPermissions, next);
     }
 
-    /** @hide */
-    public AttributionSource(int uid, int pid, @Nullable String packageName,
-            @Nullable String attributionTag, @NonNull IBinder token,
-            @Nullable String[] renouncedPermissions,
+    AttributionSource(int uid, @Nullable String packageName, @Nullable String attributionTag,
+            @NonNull IBinder token, @Nullable String[] renouncedPermissions,
             @Nullable AttributionSource next) {
         mAttributionSourceState = new AttributionSourceState();
         mAttributionSourceState.uid = uid;
-        mAttributionSourceState.pid = pid;
         mAttributionSourceState.token = token;
         mAttributionSourceState.packageName = packageName;
         mAttributionSourceState.attributionTag = attributionTag;
@@ -169,17 +156,7 @@
 
         // Since we just unpacked this object as part of it transiting a Binder
         // call, this is the perfect time to enforce that its UID and PID can be trusted
-        enforceCallingUid();
-
-        // If this object is being constructed as part of a oneway Binder call, getCallingPid will
-        // return 0 instead of the true PID. In that case, invalidate the PID by setting it to
-        // INVALID_PID (-1).
-        final int callingPid = Binder.getCallingPid();
-        if (callingPid == 0) {
-            mAttributionSourceState.pid = Process.INVALID_PID;
-        }
-
-        enforceCallingPid();
+        enforceCallingUidAndPid();
     }
 
     /** @hide */
@@ -189,19 +166,19 @@
 
     /** @hide */
     public AttributionSource withNextAttributionSource(@Nullable AttributionSource next) {
-        return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),
-                getToken(), mAttributionSourceState.renouncedPermissions, next);
+        return new AttributionSource(getUid(), getPackageName(), getAttributionTag(),
+                mAttributionSourceState.renouncedPermissions, next);
     }
 
     /** @hide */
     public AttributionSource withPackageName(@Nullable String packageName) {
-        return new AttributionSource(getUid(), getPid(), packageName, getAttributionTag(),
-               getToken(), mAttributionSourceState.renouncedPermissions, getNext());
+        return new AttributionSource(getUid(), packageName, getAttributionTag(),
+                mAttributionSourceState.renouncedPermissions, getNext());
     }
 
     /** @hide */
     public AttributionSource withToken(@NonNull Binder token) {
-        return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),
+        return new AttributionSource(getUid(), getPackageName(), getAttributionTag(),
                 token, mAttributionSourceState.renouncedPermissions, getNext());
     }
 
@@ -245,7 +222,6 @@
         }
         try {
             return new AttributionSource.Builder(uid)
-                .setPid(Process.myPid())
                 .setPackageName(AppGlobals.getPackageManager().getPackagesForUid(uid)[0])
                 .build();
         } catch (Exception ignored) {
@@ -283,6 +259,18 @@
     }
 
     /**
+     * If you are handling an IPC and you don't trust the caller you need to validate whether the
+     * attribution source is one for the calling app to prevent the caller to pass you a source from
+     * another app without including themselves in the attribution chain.
+     *
+     * @throws SecurityException if the attribution source cannot be trusted to be from the caller.
+     */
+    private void enforceCallingUidAndPid() {
+        enforceCallingUid();
+        enforceCallingPid();
+    }
+
+    /**
      * If you are handling an IPC and you don't trust the caller you need to validate
      * whether the attribution source is one for the calling app to prevent the caller
      * to pass you a source from another app without including themselves in the
@@ -318,10 +306,7 @@
     }
 
     /**
-     * Validate that the pid being claimed for the calling app is not spoofed.
-     *
-     * Note that the PID may be unavailable, for example if we're in a oneway Binder call. In this
-     * case, calling enforceCallingPid is guaranteed to fail. The caller should anticipate this.
+     * Validate that the pid being claimed for the calling app is not spoofed
      *
      * @throws SecurityException if the attribution source cannot be trusted to be from the caller.
      * @hide
@@ -329,12 +314,8 @@
     @TestApi
     public void enforceCallingPid() {
         if (!checkCallingPid()) {
-            if (Binder.getCallingPid() == 0) {
-                throw new SecurityException("Calling pid unavailable due to oneway Binder call.");
-            } else {
-                throw new SecurityException("Calling pid: " + Binder.getCallingPid()
-                        + " doesn't match source pid: " + mAttributionSourceState.pid);
-            }
+            throw new SecurityException("Calling pid: " + Binder.getCallingPid()
+                    + " doesn't match source pid: " + mAttributionSourceState.pid);
         }
     }
 
@@ -345,8 +326,7 @@
      */
     private boolean checkCallingPid() {
         final int callingPid = Binder.getCallingPid();
-        if (mAttributionSourceState.pid != Process.INVALID_PID
-                && callingPid != mAttributionSourceState.pid) {
+        if (mAttributionSourceState.pid != -1 && callingPid != mAttributionSourceState.pid) {
             return false;
         }
         return true;
@@ -463,13 +443,6 @@
     }
 
     /**
-     * The PID that is accessing the permission protected data.
-     */
-    public int getPid() {
-        return mAttributionSourceState.pid;
-    }
-
-    /**
      * The package that is accessing the permission protected data.
      */
     public @Nullable String getPackageName() {
@@ -577,7 +550,6 @@
                 throw new IllegalArgumentException("current AttributionSource can not be null");
             }
             mAttributionSourceState.uid = current.getUid();
-            mAttributionSourceState.pid = current.getPid();
             mAttributionSourceState.packageName = current.getPackageName();
             mAttributionSourceState.attributionTag = current.getAttributionTag();
             mAttributionSourceState.token = current.getToken();
@@ -586,24 +558,11 @@
         }
 
         /**
-         * The PID of the process that is accessing the permission protected data.
-         *
-         * If not called, pid will default to Process.INVALID_PID (-1). This indicates that the PID
-         * data is missing. Supplying a PID is not required, but recommended when accessible.
-         */
-        public @NonNull Builder setPid(int value) {
-            checkNotUsed();
-            mBuilderFieldsSet |= 0x2;
-            mAttributionSourceState.pid = value;
-            return this;
-        }
-
-        /**
          * The package that is accessing the permission protected data.
          */
         public @NonNull Builder setPackageName(@Nullable String value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x4;
+            mBuilderFieldsSet |= 0x2;
             mAttributionSourceState.packageName = value;
             return this;
         }
@@ -613,7 +572,7 @@
          */
         public @NonNull Builder setAttributionTag(@Nullable String value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x8;
+            mBuilderFieldsSet |= 0x4;
             mAttributionSourceState.attributionTag = value;
             return this;
         }
@@ -646,7 +605,7 @@
         @RequiresPermission(android.Manifest.permission.RENOUNCE_PERMISSIONS)
         public @NonNull Builder setRenouncedPermissions(@Nullable Set<String> value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x10;
+            mBuilderFieldsSet |= 0x8;
             mAttributionSourceState.renouncedPermissions = (value != null)
                     ? value.toArray(new String[0]) : null;
             return this;
@@ -657,7 +616,7 @@
          */
         public @NonNull Builder setNext(@Nullable AttributionSource value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x20;
+            mBuilderFieldsSet |= 0x10;
             mAttributionSourceState.next = (value != null) ? new AttributionSourceState[]
                     {value.mAttributionSourceState} : mAttributionSourceState.next;
             return this;
@@ -669,18 +628,15 @@
             mBuilderFieldsSet |= 0x40; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x2) == 0) {
-                mAttributionSourceState.pid = Process.INVALID_PID;
-            }
-            if ((mBuilderFieldsSet & 0x4) == 0) {
                 mAttributionSourceState.packageName = null;
             }
-            if ((mBuilderFieldsSet & 0x8) == 0) {
+            if ((mBuilderFieldsSet & 0x4) == 0) {
                 mAttributionSourceState.attributionTag = null;
             }
-            if ((mBuilderFieldsSet & 0x10) == 0) {
+            if ((mBuilderFieldsSet & 0x8) == 0) {
                 mAttributionSourceState.renouncedPermissions = null;
             }
-            if ((mBuilderFieldsSet & 0x20) == 0) {
+            if ((mBuilderFieldsSet & 0x10) == 0) {
                 mAttributionSourceState.next = null;
             }
 
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index 0e3217d..8d3452e 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -152,7 +152,7 @@
             @NonNull String permission, int pid, int uid, @Nullable String packageName,
             @Nullable String attributionTag, @Nullable String message, boolean startDataDelivery) {
         return checkPermissionForDataDelivery(context, permission, pid, new AttributionSource(uid,
-                pid, packageName, attributionTag), message, startDataDelivery);
+                packageName, attributionTag), message, startDataDelivery);
     }
 
     /**
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
index be57372..d6f191e 100644
--- a/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
@@ -37,6 +37,7 @@
 import android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest;
 import android.os.PersistableBundle;
 import android.util.ArraySet;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.vcn.util.PersistableBundleUtils;
@@ -58,6 +59,8 @@
  */
 @VisibleForTesting(visibility = Visibility.PRIVATE)
 public final class IkeSessionParamsUtils {
+    private static final String TAG = IkeSessionParamsUtils.class.getSimpleName();
+
     private static final String SERVER_HOST_NAME_KEY = "SERVER_HOST_NAME_KEY";
     private static final String SA_PROPOSALS_KEY = "SA_PROPOSALS_KEY";
     private static final String LOCAL_ID_KEY = "LOCAL_ID_KEY";
@@ -72,6 +75,13 @@
     private static final String NATT_KEEPALIVE_DELAY_SEC_KEY = "NATT_KEEPALIVE_DELAY_SEC_KEY";
     private static final String IKE_OPTIONS_KEY = "IKE_OPTIONS_KEY";
 
+    // TODO: b/243181760 Use the IKE API when they are exposed
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static final int IKE_OPTION_AUTOMATIC_ADDRESS_FAMILY_SELECTION = 6;
+
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static final int IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES = 7;
+
     private static final Set<Integer> IKE_OPTIONS = new ArraySet<>();
 
     static {
@@ -80,6 +90,26 @@
         IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_MOBIKE);
         IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_FORCE_PORT_4500);
         IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_INITIAL_CONTACT);
+        IKE_OPTIONS.add(IkeSessionParams.IKE_OPTION_REKEY_MOBILITY);
+        IKE_OPTIONS.add(IKE_OPTION_AUTOMATIC_ADDRESS_FAMILY_SELECTION);
+        IKE_OPTIONS.add(IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES);
+    }
+
+    /**
+     * Check if an IKE option is supported in the IPsec module installed on the device
+     *
+     * <p>This method ensures caller to safely access options that are added between dessert
+     * releases.
+     */
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    public static boolean isIkeOptionValid(int option) {
+        try {
+            new IkeSessionParams.Builder().addIkeOption(option);
+            return true;
+        } catch (IllegalArgumentException e) {
+            Log.d(TAG, "Option not supported; discarding: " + option);
+            return false;
+        }
     }
 
     /** Serializes an IkeSessionParams to a PersistableBundle. */
@@ -130,7 +160,7 @@
         // IKE_OPTION is defined in IKE module and added in the IkeSessionParams
         final List<Integer> enabledIkeOptions = new ArrayList<>();
         for (int option : IKE_OPTIONS) {
-            if (params.hasIkeOption(option)) {
+            if (isIkeOptionValid(option) && params.hasIkeOption(option)) {
                 enabledIkeOptions.add(option);
             }
         }
@@ -205,12 +235,16 @@
 
         // Clear IKE Options that are by default enabled
         for (int option : IKE_OPTIONS) {
-            builder.removeIkeOption(option);
+            if (isIkeOptionValid(option)) {
+                builder.removeIkeOption(option);
+            }
         }
 
         final int[] optionArray = in.getIntArray(IKE_OPTIONS_KEY);
         for (int option : optionArray) {
-            builder.addIkeOption(option);
+            if (isIkeOptionValid(option)) {
+                builder.addIkeOption(option);
+            }
         }
 
         return builder.build();
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 62ee408..fa6b118 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -76,6 +76,7 @@
     String getUserAccount(int userId);
     void setUserAccount(int userId, String accountName);
     long getUserCreationTime(int userId);
+    boolean isUserSwitcherEnabled(int mUserId);
     boolean isRestricted(int userId);
     boolean canHaveRestrictedProfile(int userId);
     int getUserSerialNumber(int userId);
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 095d53e..14082f3 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -364,11 +364,6 @@
     public static final int LAST_APPLICATION_CACHE_GID = 29999;
 
     /**
-     * An invalid PID value.
-     */
-    public static final int INVALID_PID = -1;
-
-    /**
      * Standard priority of application threads.
      * Use with {@link #setThreadPriority(int)} and
      * {@link #setThreadPriority(int, int)}, <b>not</b> with the normal
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index ef04f64..ca0af2c 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -5246,23 +5246,13 @@
     })
     @UserHandleAware
     public boolean isUserSwitcherEnabled(boolean showEvenIfNotActionable) {
-        if (!supportsMultipleUsers()) {
-            return false;
-        }
-        if (hasUserRestrictionForUser(DISALLOW_USER_SWITCH, mUserId)) {
-            return false;
-        }
-        // If Demo Mode is on, don't show user switcher
-        if (isDeviceInDemoMode(mContext)) {
-            return false;
-        }
-        // Check the Settings.Global.USER_SWITCHER_ENABLED that the user can toggle on/off.
-        final boolean userSwitcherSettingOn = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.USER_SWITCHER_ENABLED,
-                Resources.getSystem().getBoolean(R.bool.config_showUserSwitcherByDefault) ? 1 : 0)
-                != 0;
-        if (!userSwitcherSettingOn) {
-            return false;
+
+        try {
+            if (!mService.isUserSwitcherEnabled(mUserId)) {
+                return false;
+            }
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
         }
 
         // The feature is enabled. But is it worth showing?
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e67b67d..8a50e79 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10661,6 +10661,13 @@
         public static final String MEDIA_CONTROLS_RESUME = "qs_media_resumption";
 
         /**
+         * Whether to enable media controls on lock screen.
+         * When enabled, media controls will appear on lock screen.
+         * @hide
+         */
+        public static final String MEDIA_CONTROLS_LOCK_SCREEN = "media_controls_lock_screen";
+
+        /**
          * Controls whether contextual suggestions can be shown in the media controls.
          * @hide
          */
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9890614..b59cf7f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5872,4 +5872,10 @@
 
     <!-- The number of tasks to scan to get the visibility of Home -->
     <integer name="config_maxScanTasksForHomeVisibility">10</integer>
+
+    <!-- Device state that corresponds to rear display mode, feature provided
+         through Jetpack WindowManager
+         TODO(b/236022708) Move rear display state to device state config file
+    -->
+    <integer name="config_deviceStateRearDisplay">-1</integer>
 </resources>
diff --git a/core/res/res/values/locale_config.xml b/core/res/res/values/locale_config.xml
index e9b42d3..78ec145 100644
--- a/core/res/res/values/locale_config.xml
+++ b/core/res/res/values/locale_config.xml
@@ -23,7 +23,7 @@
         <item>ak-GH</item> <!-- Akan (Ghana) -->
         <item>am-ET</item> <!-- Amharic (Ethiopia) -->
         <item>ar-AE</item> <!-- Arabic (United Arab Emirates) -->
-        <item>ar-AE-u-nu-latn</item> <!-- Arabic (United Arab Emirates, Western Digits) -->
+        <item>ar-AE-u-nu-arab</item> <!-- Arabic (United Arab Emirates, Arabic Digits) -->
         <item>ar-BH</item> <!-- Arabic (Bahrain) -->
         <item>ar-BH-u-nu-latn</item> <!-- Arabic (Bahrain, Western Digits) -->
         <item>ar-DJ</item> <!-- Arabic (Djibouti) -->
@@ -190,6 +190,7 @@
         <item>en-MS</item> <!-- English (Montserrat) -->
         <item>en-MT</item> <!-- English (Malta) -->
         <item>en-MU</item> <!-- English (Mauritius) -->
+        <item>en-MV</item> <!-- English (Maldives) -->
         <item>en-MW</item> <!-- English (Malawi) -->
         <item>en-MY</item> <!-- English (Malaysia) -->
         <item>en-NA</item> <!-- English (Namibia) -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0654fff..d60fc20 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4830,6 +4830,7 @@
   <java-symbol type="drawable" name="ic_swap_horiz" />
   <java-symbol type="array" name="config_deviceStatesAvailableForAppRequests" />
   <java-symbol type="array" name="config_serviceStateLocationAllowedPackages" />
+  <java-symbol type="integer" name="config_deviceStateRearDisplay"/>
 
   <!-- For app language picker -->
   <java-symbol type="string" name="system_locale_title" />
diff --git a/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java b/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java
new file mode 100644
index 0000000..2a3da05
--- /dev/null
+++ b/core/tests/coretests/src/android/app/NotificationChannelGroupTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import static junit.framework.TestCase.assertEquals;
+
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.common.base.Strings;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Field;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NotificationChannelGroupTest {
+    private final String CLASS = "android.app.NotificationChannelGroup";
+
+    @Test
+    public void testLongStringFields() {
+        NotificationChannelGroup group = new NotificationChannelGroup("my_group_01", "groupName");
+
+        try {
+            String longString = Strings.repeat("A", 65536);
+            Field mName = Class.forName(CLASS).getDeclaredField("mName");
+            mName.setAccessible(true);
+            mName.set(group, longString);
+            Field mId = Class.forName(CLASS).getDeclaredField("mId");
+            mId.setAccessible(true);
+            mId.set(group, longString);
+            Field mDescription = Class.forName(CLASS).getDeclaredField("mDescription");
+            mDescription.setAccessible(true);
+            mDescription.set(group, longString);
+        } catch (NoSuchFieldException e) {
+            e.printStackTrace();
+        } catch (ClassNotFoundException e) {
+            e.printStackTrace();
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        }
+
+        Parcel parcel = Parcel.obtain();
+        group.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        NotificationChannelGroup fromParcel =
+                NotificationChannelGroup.CREATOR.createFromParcel(parcel);
+        assertEquals(NotificationChannelGroup.MAX_TEXT_LENGTH, fromParcel.getId().length());
+        assertEquals(NotificationChannelGroup.MAX_TEXT_LENGTH, fromParcel.getName().length());
+        assertEquals(NotificationChannelGroup.MAX_TEXT_LENGTH,
+                fromParcel.getDescription().length());
+    }
+}
diff --git a/core/tests/coretests/src/android/app/NotificationChannelTest.java b/core/tests/coretests/src/android/app/NotificationChannelTest.java
new file mode 100644
index 0000000..647bfe8
--- /dev/null
+++ b/core/tests/coretests/src/android/app/NotificationChannelTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import static junit.framework.TestCase.assertEquals;
+
+import android.net.Uri;
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.common.base.Strings;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Field;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NotificationChannelTest {
+    private final String CLASS = "android.app.NotificationChannel";
+
+    @Test
+    public void testLongStringFields() {
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+
+        try {
+            String longString = Strings.repeat("A", 65536);
+            Field mName = Class.forName(CLASS).getDeclaredField("mName");
+            mName.setAccessible(true);
+            mName.set(channel, longString);
+            Field mId = Class.forName(CLASS).getDeclaredField("mId");
+            mId.setAccessible(true);
+            mId.set(channel, longString);
+            Field mDesc = Class.forName(CLASS).getDeclaredField("mDesc");
+            mDesc.setAccessible(true);
+            mDesc.set(channel, longString);
+            Field mParentId = Class.forName(CLASS).getDeclaredField("mParentId");
+            mParentId.setAccessible(true);
+            mParentId.set(channel, longString);
+            Field mGroup = Class.forName(CLASS).getDeclaredField("mGroup");
+            mGroup.setAccessible(true);
+            mGroup.set(channel, longString);
+            Field mConversationId = Class.forName(CLASS).getDeclaredField("mConversationId");
+            mConversationId.setAccessible(true);
+            mConversationId.set(channel, longString);
+        } catch (NoSuchFieldException e) {
+            e.printStackTrace();
+        } catch (ClassNotFoundException e) {
+            e.printStackTrace();
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        }
+
+        Parcel parcel = Parcel.obtain();
+        channel.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        NotificationChannel fromParcel = NotificationChannel.CREATOR.createFromParcel(parcel);
+        assertEquals(NotificationChannel.MAX_TEXT_LENGTH, fromParcel.getId().length());
+        assertEquals(NotificationChannel.MAX_TEXT_LENGTH, fromParcel.getName().length());
+        assertEquals(NotificationChannel.MAX_TEXT_LENGTH,
+                fromParcel.getDescription().length());
+        assertEquals(NotificationChannel.MAX_TEXT_LENGTH,
+                fromParcel.getParentChannelId().length());
+        assertEquals(NotificationChannel.MAX_TEXT_LENGTH,
+                fromParcel.getGroup().length());
+        assertEquals(NotificationChannel.MAX_TEXT_LENGTH,
+                fromParcel.getConversationId().length());
+    }
+
+    @Test
+    public void testLongAlertFields() {
+        NotificationChannel channel = new NotificationChannel("id", "name", 3);
+
+        channel.setSound(Uri.parse("content://" + Strings.repeat("A",65536)),
+                Notification.AUDIO_ATTRIBUTES_DEFAULT);
+        channel.setVibrationPattern(new long[65550/2]);
+
+        Parcel parcel = Parcel.obtain();
+        channel.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        NotificationChannel fromParcel = NotificationChannel.CREATOR.createFromParcel(parcel);
+        assertEquals(NotificationChannel.MAX_VIBRATION_LENGTH,
+                fromParcel.getVibrationPattern().length);
+        assertEquals(NotificationChannel.MAX_TEXT_LENGTH,
+                fromParcel.getSound().toString().length());
+    }
+}
diff --git a/core/tests/coretests/src/android/hardware/input/OWNERS b/core/tests/coretests/src/android/hardware/input/OWNERS
new file mode 100644
index 0000000..3f8a602
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/input/OWNERS
@@ -0,0 +1,2 @@
+include /core/java/android/hardware/input/OWNERS
+
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index b341a4e..c93b733 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -26,10 +26,21 @@
      @Retention(RetentionPolicy.SOURCE)
      @IntDef(value = {
              UNKNOWN,
+             /**
+              * Since some APIs accept either ImageFormat or PixelFormat (and the two
+              * enums do not overlap since they're both partial versions of the
+              * internal format enum), add PixelFormat values here so linting
+              * tools won't complain when method arguments annotated with
+              * ImageFormat are provided with PixelFormat values.
+              */
+             PixelFormat.RGBA_8888,
+             PixelFormat.RGBX_8888,
+             PixelFormat.RGB_888,
              RGB_565,
              YV12,
              Y8,
              Y16,
+             YCBCR_P010,
              NV16,
              NV21,
              YUY2,
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index bdf703c..7e9c418 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -20,11 +20,14 @@
 import android.content.Context;
 
 import androidx.annotation.NonNull;
+import androidx.window.extensions.area.WindowAreaComponent;
+import androidx.window.extensions.area.WindowAreaComponentImpl;
 import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
 import androidx.window.extensions.embedding.SplitController;
 import androidx.window.extensions.layout.WindowLayoutComponent;
 import androidx.window.extensions.layout.WindowLayoutComponentImpl;
 
+
 /**
  * The reference implementation of {@link WindowExtensions} that implements the initial API version.
  */
@@ -33,10 +36,12 @@
     private final Object mLock = new Object();
     private volatile WindowLayoutComponent mWindowLayoutComponent;
     private volatile SplitController mSplitController;
+    private volatile WindowAreaComponent mWindowAreaComponent;
 
+    // TODO(b/241126279) Introduce constants to better version functionality
     @Override
     public int getVendorApiLevel() {
-        return 1;
+        return 2;
     }
 
     /**
@@ -75,4 +80,23 @@
         }
         return mSplitController;
     }
+
+    /**
+     * Returns a reference implementation of {@link WindowAreaComponent} if available,
+     * {@code null} otherwise. The implementation must match the API level reported in
+     * {@link WindowExtensions#getWindowAreaComponent()}.
+     * @return {@link WindowAreaComponent} OEM implementation.
+     */
+    public WindowAreaComponent getWindowAreaComponent() {
+        if (mWindowAreaComponent == null) {
+            synchronized (mLock) {
+                if (mWindowAreaComponent == null) {
+                    Context context = ActivityThread.currentApplication();
+                    mWindowAreaComponent =
+                            new WindowAreaComponentImpl(context);
+                }
+            }
+        }
+        return mWindowAreaComponent;
+    }
 }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
new file mode 100644
index 0000000..3adae70
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -0,0 +1,255 @@
+/*
+ * 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 androidx.window.extensions.area;
+
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+
+import android.app.Activity;
+import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateRequest;
+import android.util.ArraySet;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Reference implementation of androidx.window.extensions.area OEM interface for use with
+ * WindowManager Jetpack.
+ *
+ * This component currently supports Rear Display mode with the ability to add and remove
+ * status listeners for this mode.
+ *
+ * The public methods in this class are thread-safe.
+ **/
+public class WindowAreaComponentImpl implements WindowAreaComponent,
+        DeviceStateManager.DeviceStateCallback {
+
+    private final Object mLock = new Object();
+
+    private final DeviceStateManager mDeviceStateManager;
+    private final Executor mExecutor;
+
+    @GuardedBy("mLock")
+    private final ArraySet<Consumer<Integer>> mRearDisplayStatusListeners = new ArraySet<>();
+    private final int mRearDisplayState;
+    @WindowAreaSessionState
+    private int mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_INACTIVE;
+
+    @GuardedBy("mLock")
+    private int mCurrentDeviceState = INVALID_DEVICE_STATE;
+    @GuardedBy("mLock")
+    private int mCurrentDeviceBaseState = INVALID_DEVICE_STATE;
+    @GuardedBy("mLock")
+    private DeviceStateRequest mDeviceStateRequest;
+
+    public WindowAreaComponentImpl(@NonNull Context context) {
+        mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
+        mExecutor = context.getMainExecutor();
+
+        // TODO(b/236022708) Move rear display state to device state config file
+        mRearDisplayState = context.getResources().getInteger(
+                R.integer.config_deviceStateRearDisplay);
+
+        mDeviceStateManager.registerCallback(mExecutor, this);
+    }
+
+    /**
+     * Adds a listener interested in receiving updates on the RearDisplayStatus
+     * of the device. Because this is being called from the OEM provided
+     * extensions, we will post the result of the listener on the executor
+     * provided by the developer at the initial call site.
+     *
+     * Depending on the initial state of the device, we will return either
+     * {@link WindowAreaComponent#STATUS_AVAILABLE} or
+     * {@link WindowAreaComponent#STATUS_UNAVAILABLE} if the feature is supported or not in that
+     * state respectively. When the rear display feature is triggered, we update the status to be
+     * {@link WindowAreaComponent#STATUS_UNAVAILABLE}. TODO(b/240727590) Prefix with AREA_
+     *
+     * TODO(b/239833099) Add a STATUS_ACTIVE option to let apps know if a feature is currently
+     * enabled.
+     *
+     * @param consumer {@link Consumer} interested in receiving updates to the status of
+     * rear display mode.
+     */
+    public void addRearDisplayStatusListener(
+            @NonNull Consumer<@WindowAreaStatus Integer> consumer) {
+        synchronized (mLock) {
+            mRearDisplayStatusListeners.add(consumer);
+
+            // If current device state is still invalid, we haven't gotten our initial value yet
+            if (mCurrentDeviceState == INVALID_DEVICE_STATE) {
+                return;
+            }
+            consumer.accept(getCurrentStatus());
+        }
+    }
+
+    /**
+     * Removes a listener no longer interested in receiving updates.
+     * @param consumer no longer interested in receiving updates to RearDisplayStatus
+     */
+    public void removeRearDisplayStatusListener(
+            @NonNull Consumer<@WindowAreaStatus Integer> consumer) {
+        synchronized (mLock) {
+            mRearDisplayStatusListeners.remove(consumer);
+        }
+    }
+
+    /**
+     * Creates and starts a rear display session and provides updates to the
+     * callback provided. Because this is being called from the OEM provided
+     * extensions, we will post the result of the listener on the executor
+     * provided by the developer at the initial call site.
+     *
+     * When we enable rear display mode, we submit a request to {@link DeviceStateManager}
+     * to override the device state to the state that corresponds to RearDisplay
+     * mode. When the {@link DeviceStateRequest} is activated, we let the
+     * consumer know that the session is active by sending
+     * {@link WindowAreaComponent#SESSION_STATE_ACTIVE}.
+     *
+     * @param activity to provide updates to the client on
+     * the status of the Session
+     * @param rearDisplaySessionCallback to provide updates to the client on
+     * the status of the Session
+     */
+    public void startRearDisplaySession(@NonNull Activity activity,
+            @NonNull Consumer<@WindowAreaSessionState Integer> rearDisplaySessionCallback) {
+        synchronized (mLock) {
+            if (mDeviceStateRequest != null) {
+                // Rear display session is already active
+                throw new IllegalStateException(
+                        "Unable to start new rear display session as one is already active");
+            }
+            mDeviceStateRequest = DeviceStateRequest.newBuilder(mRearDisplayState).build();
+            mDeviceStateManager.requestState(
+                    mDeviceStateRequest,
+                    mExecutor,
+                    new DeviceStateRequestCallbackAdapter(rearDisplaySessionCallback)
+            );
+        }
+    }
+
+    /**
+     * Ends the current rear display session and provides updates to the
+     * callback provided. Because this is being called from the OEM provided
+     * extensions, we will post the result of the listener on the executor
+     * provided by the developer.
+     */
+    public void endRearDisplaySession() {
+        synchronized (mLock) {
+            if (mDeviceStateRequest != null || isRearDisplayActive()) {
+                mDeviceStateRequest = null;
+                mDeviceStateManager.cancelStateRequest();
+            } else {
+                throw new IllegalStateException(
+                        "Unable to cancel a rear display session as there is no active session");
+            }
+        }
+    }
+
+    @Override
+    public void onBaseStateChanged(int state) {
+        synchronized (mLock) {
+            mCurrentDeviceBaseState = state;
+            if (state == mCurrentDeviceState) {
+                updateStatusConsumers(getCurrentStatus());
+            }
+        }
+    }
+
+    @Override
+    public void onStateChanged(int state) {
+        synchronized (mLock) {
+            mCurrentDeviceState = state;
+            updateStatusConsumers(getCurrentStatus());
+        }
+    }
+
+    @GuardedBy("mLock")
+    private int getCurrentStatus() {
+        if (mRearDisplaySessionStatus == WindowAreaComponent.SESSION_STATE_ACTIVE
+                || isRearDisplayActive()) {
+            return WindowAreaComponent.STATUS_UNAVAILABLE;
+        }
+        return WindowAreaComponent.STATUS_AVAILABLE;
+    }
+
+    /**
+     * Helper method to determine if a rear display session is currently active by checking
+     * if the current device configuration matches that of rear display. This would be true
+     * if there is a device override currently active (base state != current state) and the current
+     * state is that which corresponds to {@code mRearDisplayState}
+     * @return {@code true} if the device is in rear display mode and {@code false} if not
+     */
+    @GuardedBy("mLock")
+    private boolean isRearDisplayActive() {
+        return (mCurrentDeviceState != mCurrentDeviceBaseState) && (mCurrentDeviceState
+                == mRearDisplayState);
+    }
+
+    @GuardedBy("mLock")
+    private void updateStatusConsumers(@WindowAreaStatus int windowAreaStatus) {
+        synchronized (mLock) {
+            for (int i = 0; i < mRearDisplayStatusListeners.size(); i++) {
+                mRearDisplayStatusListeners.valueAt(i).accept(windowAreaStatus);
+            }
+        }
+    }
+
+    /**
+     * Callback for the {@link DeviceStateRequest} to be notified of when the request has been
+     * activated or cancelled. This callback provides information to the client library
+     * on the status of the RearDisplay session through {@code mRearDisplaySessionCallback}
+     */
+    private class DeviceStateRequestCallbackAdapter implements DeviceStateRequest.Callback {
+
+        private final Consumer<Integer> mRearDisplaySessionCallback;
+
+        DeviceStateRequestCallbackAdapter(@NonNull Consumer<Integer> callback) {
+            mRearDisplaySessionCallback = callback;
+        }
+
+        @Override
+        public void onRequestActivated(@NonNull DeviceStateRequest request) {
+            synchronized (mLock) {
+                if (request.equals(mDeviceStateRequest)) {
+                    mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_ACTIVE;
+                    mRearDisplaySessionCallback.accept(mRearDisplaySessionStatus);
+                    updateStatusConsumers(getCurrentStatus());
+                }
+            }
+        }
+
+        @Override
+        public void onRequestCanceled(DeviceStateRequest request) {
+            synchronized (mLock) {
+                if (request.equals(mDeviceStateRequest)) {
+                    mDeviceStateRequest = null;
+                }
+                mRearDisplaySessionStatus = WindowAreaComponent.SESSION_STATE_INACTIVE;
+                mRearDisplaySessionCallback.accept(mRearDisplaySessionStatus);
+                updateStatusConsumers(getCurrentStatus());
+            }
+        }
+    }
+}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index 918e514..e9a1721 100644
--- a/libs/WindowManager/Jetpack/window-extensions-release.aar
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 85c8ebf..83ba909 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -80,7 +80,10 @@
     public static final int PARALLAX_DISMISSING = 1;
     public static final int PARALLAX_ALIGN_CENTER = 2;
 
-    private static final int FLING_ANIMATION_DURATION = 250;
+    private static final int FLING_RESIZE_DURATION = 250;
+    private static final int FLING_SWITCH_DURATION = 350;
+    private static final int FLING_ENTER_DURATION = 350;
+    private static final int FLING_EXIT_DURATION = 350;
 
     private final int mDividerWindowWidth;
     private final int mDividerInsets;
@@ -93,6 +96,9 @@
     private final Rect mBounds1 = new Rect();
     // Bounds2 final position should be always at bottom or right
     private final Rect mBounds2 = new Rect();
+    // The temp bounds outside of display bounds for side stage when split screen inactive to avoid
+    // flicker next time active split screen.
+    private final Rect mInvisibleBounds = new Rect();
     private final Rect mWinBounds1 = new Rect();
     private final Rect mWinBounds2 = new Rect();
     private final SplitLayoutHandler mSplitLayoutHandler;
@@ -141,6 +147,10 @@
         resetDividerPosition();
 
         mDimNonImeSide = resources.getBoolean(R.bool.config_dimNonImeAttachedSide);
+
+        mInvisibleBounds.set(mRootBounds);
+        mInvisibleBounds.offset(isLandscape() ? mRootBounds.right : 0,
+                isLandscape() ? 0 : mRootBounds.bottom);
     }
 
     private int getDividerInsets(Resources resources, Display display) {
@@ -239,6 +249,12 @@
         rect.offset(-mRootBounds.left, -mRootBounds.top);
     }
 
+    /** Gets bounds size equal to root bounds but outside of screen, used for position side stage
+     * when split inactive to avoid flicker when next time active. */
+    public void getInvisibleBounds(Rect rect) {
+        rect.set(mInvisibleBounds);
+    }
+
     /** Returns leash of the current divider bar. */
     @Nullable
     public SurfaceControl getDividerLeash() {
@@ -284,6 +300,10 @@
         mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null);
         initDividerPosition(mTempRect);
 
+        mInvisibleBounds.set(mRootBounds);
+        mInvisibleBounds.offset(isLandscape() ? mRootBounds.right : 0,
+                isLandscape() ? 0 : mRootBounds.bottom);
+
         return true;
     }
 
@@ -405,6 +425,13 @@
         mFreezeDividerWindow = freezeDividerWindow;
     }
 
+    /** Update current layout as divider put on start or end position. */
+    public void setDividerAtBorder(boolean start) {
+        final int pos = start ? mDividerSnapAlgorithm.getDismissStartTarget().position
+                : mDividerSnapAlgorithm.getDismissEndTarget().position;
+        setDividePosition(pos, false /* applyLayoutChange */);
+    }
+
     /**
      * Updates bounds with the passing position. Usually used to update recording bounds while
      * performing animation or dragging divider bar to resize the splits.
@@ -449,17 +476,17 @@
     public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
         switch (snapTarget.flag) {
             case FLAG_DISMISS_START:
-                flingDividePosition(currentPosition, snapTarget.position,
+                flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
                         () -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */,
                                 EXIT_REASON_DRAG_DIVIDER));
                 break;
             case FLAG_DISMISS_END:
-                flingDividePosition(currentPosition, snapTarget.position,
+                flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
                         () -> mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */,
                                 EXIT_REASON_DRAG_DIVIDER));
                 break;
             default:
-                flingDividePosition(currentPosition, snapTarget.position,
+                flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
                         () -> setDividePosition(snapTarget.position, true /* applyLayoutChange */));
                 break;
         }
@@ -516,12 +543,20 @@
     public void flingDividerToDismiss(boolean toEnd, int reason) {
         final int target = toEnd ? mDividerSnapAlgorithm.getDismissEndTarget().position
                 : mDividerSnapAlgorithm.getDismissStartTarget().position;
-        flingDividePosition(getDividePosition(), target,
+        flingDividePosition(getDividePosition(), target, FLING_EXIT_DURATION,
                 () -> mSplitLayoutHandler.onSnappedToDismiss(toEnd, reason));
     }
 
+    /** Fling divider from current position to center position. */
+    public void flingDividerToCenter() {
+        final int pos = mDividerSnapAlgorithm.getMiddleTarget().position;
+        flingDividePosition(getDividePosition(), pos, FLING_ENTER_DURATION,
+                () -> setDividePosition(pos, true /* applyLayoutChange */));
+    }
+
     @VisibleForTesting
-    void flingDividePosition(int from, int to, @Nullable Runnable flingFinishedCallback) {
+    void flingDividePosition(int from, int to, int duration,
+            @Nullable Runnable flingFinishedCallback) {
         if (from == to) {
             // No animation run, still callback to stop resizing.
             mSplitLayoutHandler.onLayoutSizeChanged(this);
@@ -531,7 +566,7 @@
         }
         ValueAnimator animator = ValueAnimator
                 .ofInt(from, to)
-                .setDuration(FLING_ANIMATION_DURATION);
+                .setDuration(duration);
         animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         animator.addUpdateListener(
                 animation -> updateDivideBounds((int) animation.getAnimatedValue()));
@@ -588,7 +623,7 @@
 
         AnimatorSet set = new AnimatorSet();
         set.playTogether(animator1, animator2, animator3);
-        set.setDuration(FLING_ANIMATION_DURATION);
+        set.setDuration(FLING_SWITCH_DURATION);
         set.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
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 7e83d2f..21fc01e 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
@@ -25,6 +25,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -488,13 +489,6 @@
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
 
-        // If split still not active, apply windows bounds first to avoid surface reset to
-        // wrong pos by SurfaceAnimator from wms.
-        // TODO(b/223325631): check  is it still necessary after improve enter transition done.
-        if (!mMainStage.isActive()) {
-            updateWindowBounds(mSplitLayout, wct);
-        }
-
         wct.sendPendingIntent(intent, fillInIntent, options);
         mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
     }
@@ -641,7 +635,7 @@
             wct.startTask(sideTaskId, sideOptions);
         }
         // Using legacy transitions, so we can't use blast sync since it conflicts.
-        mTaskOrganizer.applyTransaction(wct);
+        mSyncQueue.queue(wct);
         mSyncQueue.runInSync(t -> {
             setDividerVisibility(true, t);
             updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
@@ -893,10 +887,13 @@
         mShouldUpdateRecents = false;
         mIsDividerRemoteAnimating = false;
 
+        mSplitLayout.getInvisibleBounds(mTempRect1);
         if (childrenToTop == null) {
             mSideStage.removeAllTasks(wct, false /* toTop */);
             mMainStage.deactivate(wct, false /* toTop */);
             wct.reorder(mRootTaskInfo.token, false /* onTop */);
+            wct.setForceTranslucent(mRootTaskInfo.token, true);
+            wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
             onTransitionAnimationComplete();
         } else {
             // Expand to top side split as full screen for fading out decor animation and dismiss
@@ -907,27 +904,32 @@
                     ? mSideStage : mMainStage;
             tempFullStage.resetBounds(wct);
             wct.setSmallestScreenWidthDp(tempFullStage.mRootTaskInfo.token,
-                    mRootTaskInfo.configuration.smallestScreenWidthDp);
+                    SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
             dismissStage.dismiss(wct, false /* toTop */);
         }
         mSyncQueue.queue(wct);
         mSyncQueue.runInSync(t -> {
             t.setWindowCrop(mMainStage.mRootLeash, null)
                     .setWindowCrop(mSideStage.mRootLeash, null);
-            t.setPosition(mMainStage.mRootLeash, 0, 0)
-                    .setPosition(mSideStage.mRootLeash, 0, 0);
             t.hide(mMainStage.mDimLayer).hide(mSideStage.mDimLayer);
             setDividerVisibility(false, t);
 
-            // In this case, exit still under progress, fade out the split decor after first WCT
-            // done and do remaining WCT after animation finished.
-            if (childrenToTop != null) {
+            if (childrenToTop == null) {
+                t.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.right);
+            } else {
+                // In this case, exit still under progress, fade out the split decor after first WCT
+                // done and do remaining WCT after animation finished.
                 childrenToTop.fadeOutDecor(() -> {
                     WindowContainerTransaction finishedWCT = new WindowContainerTransaction();
                     mIsExiting = false;
                     childrenToTop.dismiss(finishedWCT, true /* toTop */);
                     finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
-                    mTaskOrganizer.applyTransaction(finishedWCT);
+                    finishedWCT.setForceTranslucent(mRootTaskInfo.token, true);
+                    finishedWCT.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
+                    mSyncQueue.queue(finishedWCT);
+                    mSyncQueue.runInSync(at -> {
+                        at.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.right);
+                    });
                     onTransitionAnimationComplete();
                 });
             }
@@ -996,6 +998,7 @@
         mMainStage.activate(wct, true /* includingTopTask */);
         updateWindowBounds(mSplitLayout, wct);
         wct.reorder(mRootTaskInfo.token, true);
+        wct.setForceTranslucent(mRootTaskInfo.token, false);
     }
 
     void finishEnterSplitScreen(SurfaceControl.Transaction t) {
@@ -1221,7 +1224,13 @@
         // Make the stages adjacent to each other so they occlude what's behind them.
         wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
         wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
-        mTaskOrganizer.applyTransaction(wct);
+        wct.setForceTranslucent(mRootTaskInfo.token, true);
+        mSplitLayout.getInvisibleBounds(mTempRect1);
+        wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
+        mSyncQueue.queue(wct);
+        mSyncQueue.runInSync(t -> {
+            t.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.top);
+        });
     }
 
     private void onRootTaskVanished() {
@@ -1377,10 +1386,17 @@
             // TODO (b/238697912) : Add the validation to prevent entering non-recovered status
             final WindowContainerTransaction wct = new WindowContainerTransaction();
             mSplitLayout.init();
-            prepareEnterSplitScreen(wct);
+            mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
+            mMainStage.activate(wct, true /* includingTopTask */);
+            updateWindowBounds(mSplitLayout, wct);
+            wct.reorder(mRootTaskInfo.token, true);
+            wct.setForceTranslucent(mRootTaskInfo.token, false);
             mSyncQueue.queue(wct);
-            mSyncQueue.runInSync(t ->
-                    updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */));
+            mSyncQueue.runInSync(t -> {
+                updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
+
+                mSplitLayout.flingDividerToCenter();
+            });
         }
         if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
             mShouldUpdateRecents = true;
@@ -1822,6 +1838,7 @@
             // properly for the animation itself.
             mSplitLayout.release();
             mSplitLayout.resetDividerPosition();
+            mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT;
             mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
         }
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 95725bb..695550d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -159,7 +159,8 @@
     }
 
     private void waitDividerFlingFinished() {
-        verify(mSplitLayout).flingDividePosition(anyInt(), anyInt(), mRunnableCaptor.capture());
+        verify(mSplitLayout).flingDividePosition(anyInt(), anyInt(), anyInt(),
+                mRunnableCaptor.capture());
         mRunnableCaptor.getValue().run();
     }
 
diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
index a0bc5aa..2aeb42c 100644
--- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp
@@ -16,7 +16,8 @@
 
 #include "TestSceneBase.h"
 
-#include <SkColorMatrixFilter.h>
+#include <SkColorFilter.h>
+#include <SkColorMatrix.h>
 #include <SkGradientShader.h>
 
 class SimpleColorMatrixAnimation;
diff --git a/media/Android.bp b/media/Android.bp
index d28a21c..ec243bf 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -113,7 +113,7 @@
             min_sdk_version: "29",
             apex_available: [
                 "//apex_available:platform",
-                "com.android.bluetooth",
+                "com.android.btservices",
             ],
         },
     },
diff --git a/media/java/android/media/BluetoothProfileConnectionInfo.java b/media/java/android/media/BluetoothProfileConnectionInfo.java
index f3a65a1..a316c21 100644
--- a/media/java/android/media/BluetoothProfileConnectionInfo.java
+++ b/media/java/android/media/BluetoothProfileConnectionInfo.java
@@ -126,7 +126,6 @@
     }
 
     /**
-     * @hide
      * Factory method for <code>BluetoothProfileConnectionInfo</code> for an LE output device
      * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY}
      *     intent will not be sent.
diff --git a/media/java/android/media/tv/AdResponse.java b/media/java/android/media/tv/AdResponse.java
index 0c20954..08c66ab 100644
--- a/media/java/android/media/tv/AdResponse.java
+++ b/media/java/android/media/tv/AdResponse.java
@@ -25,7 +25,7 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * An advertisement request which can be sent to TV interactive App service to inform AD status.
+ * An advertisement response which can be sent to TV interactive App service to inform AD status.
  */
 public final class AdResponse implements Parcelable {
     /** @hide */
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
index 9f92887..a72f34c 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionWrapper.java
@@ -217,7 +217,7 @@
             case DO_DISPATCH_SURFACE_CHANGED: {
                 SomeArgs args = (SomeArgs) msg.obj;
                 mSessionImpl.dispatchSurfaceChanged(
-                        (Integer) args.arg1, (Integer) args.arg2, (Integer) args.arg3);
+                        (Integer) args.argi1, (Integer) args.argi2, (Integer) args.argi3);
                 args.recycle();
                 break;
             }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/OWNERS b/packages/SettingsLib/src/com/android/settingslib/media/OWNERS
new file mode 100644
index 0000000..d40f322
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/OWNERS
@@ -0,0 +1,2 @@
+# Default reviewers for this and subdirectories.
+shaoweishen@google.com
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index bc9490f..5aae1ce 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -182,6 +182,7 @@
         Settings.Secure.PEOPLE_STRIP,
         Settings.Secure.MEDIA_CONTROLS_RESUME,
         Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION,
+        Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
         Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 2c99d71..67ea024 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -277,6 +277,7 @@
         VALIDATORS.put(Secure.PEOPLE_STRIP, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.MEDIA_CONTROLS_RESUME, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.MEDIA_CONTROLS_RECOMMENDATION, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.MEDIA_CONTROLS_LOCK_SCREEN, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
                 new InclusiveIntegerRangeValidator(
                         Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index fd7554f..528af2e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -376,9 +376,11 @@
             Setting newSetting = new Setting(name, oldSetting.getValue(), null,
                     oldSetting.getPackageName(), oldSetting.getTag(), false,
                     oldSetting.getId());
-            mSettings.put(name, newSetting);
-            updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue,
+            int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue,
                     newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue());
+            checkNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize);
+            mSettings.put(name, newSetting);
+            updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize);
             scheduleWriteIfNeededLocked();
         }
     }
@@ -410,6 +412,12 @@
         Setting oldState = mSettings.get(name);
         String oldValue = (oldState != null) ? oldState.value : null;
         String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null;
+        String newDefaultValue = makeDefault ? value : oldDefaultValue;
+
+        int newSize = getNewMemoryUsagePerPackageLocked(packageName, oldValue, value,
+                oldDefaultValue, newDefaultValue);
+        checkNewMemoryUsagePerPackageLocked(packageName, newSize);
+
         Setting newState;
 
         if (oldState != null) {
@@ -430,8 +438,7 @@
 
         addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState);
 
-        updateMemoryUsagePerPackageLocked(packageName, oldValue, value,
-                oldDefaultValue, newState.getDefaultValue());
+        updateMemoryUsagePerPackageLocked(packageName, newSize);
 
         scheduleWriteIfNeededLocked();
 
@@ -552,13 +559,14 @@
         }
 
         Setting oldState = mSettings.remove(name);
+        int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName, oldState.value,
+                null, oldState.defaultValue, null);
 
         FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, /* value= */ "",
                 /* newValue= */ "", oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
                 FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED);
 
-        updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value,
-                null, oldState.defaultValue, null);
+        updateMemoryUsagePerPackageLocked(oldState.packageName, newSize);
 
         addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState);
 
@@ -579,16 +587,18 @@
         Setting oldSetting = new Setting(setting);
         String oldValue = setting.getValue();
         String oldDefaultValue = setting.getDefaultValue();
+        String newValue = oldDefaultValue;
+        String newDefaultValue = oldDefaultValue;
+
+        int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, oldValue,
+                newValue, oldDefaultValue, newDefaultValue);
+        checkNewMemoryUsagePerPackageLocked(setting.packageName, newSize);
 
         if (!setting.reset()) {
             return false;
         }
 
-        String newValue = setting.getValue();
-        String newDefaultValue = setting.getDefaultValue();
-
-        updateMemoryUsagePerPackageLocked(setting.packageName, oldValue,
-                newValue, oldDefaultValue, newDefaultValue);
+        updateMemoryUsagePerPackageLocked(setting.packageName, newSize);
 
         addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting);
 
@@ -696,38 +706,49 @@
     }
 
     @GuardedBy("mLock")
-    private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue,
+    private boolean isExemptFromMemoryUsageCap(String packageName) {
+        return mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED
+                || SYSTEM_PACKAGE_NAME.equals(packageName);
+    }
+
+    @GuardedBy("mLock")
+    private void checkNewMemoryUsagePerPackageLocked(String packageName, int newSize)
+            throws IllegalStateException {
+        if (isExemptFromMemoryUsageCap(packageName)) {
+            return;
+        }
+        if (newSize > mMaxBytesPerAppPackage) {
+            throw new IllegalStateException("You are adding too many system settings. "
+                    + "You should stop using system settings for app specific data"
+                    + " package: " + packageName);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private int getNewMemoryUsagePerPackageLocked(String packageName, String oldValue,
             String newValue, String oldDefaultValue, String newDefaultValue) {
-        if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) {
-            return;
+        if (isExemptFromMemoryUsageCap(packageName)) {
+            return 0;
         }
-
-        if (SYSTEM_PACKAGE_NAME.equals(packageName)) {
-            return;
-        }
-
+        final Integer currentSize = mPackageToMemoryUsage.get(packageName);
         final int oldValueSize = (oldValue != null) ? oldValue.length() : 0;
         final int newValueSize = (newValue != null) ? newValue.length() : 0;
         final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0;
         final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0;
         final int deltaSize = newValueSize + newDefaultValueSize
                 - oldValueSize - oldDefaultValueSize;
+        return Math.max((currentSize != null) ? currentSize + deltaSize : deltaSize, 0);
+    }
 
-        Integer currentSize = mPackageToMemoryUsage.get(packageName);
-        final int newSize = Math.max((currentSize != null)
-                ? currentSize + deltaSize : deltaSize, 0);
-
-        if (newSize > mMaxBytesPerAppPackage) {
-            throw new IllegalStateException("You are adding too many system settings. "
-                    + "You should stop using system settings for app specific data"
-                    + " package: " + packageName);
+    @GuardedBy("mLock")
+    private void updateMemoryUsagePerPackageLocked(String packageName, int newSize) {
+        if (isExemptFromMemoryUsageCap(packageName)) {
+            return;
         }
-
         if (DEBUG) {
             Slog.i(LOG_TAG, "Settings for package: " + packageName
                     + " size: " + newSize + " bytes.");
         }
-
         mPackageToMemoryUsage.put(packageName, newSize);
     }
 
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 69eb713..a637efa 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -20,6 +20,8 @@
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
+import com.google.common.base.Strings;
+
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -276,4 +278,39 @@
         settingsState.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING);
         return settingsState;
     }
+
+    public void testInsertSetting_memoryUsage() {
+        SettingsState settingsState = getSettingStateObject();
+        // No exception should be thrown when there is no cap
+        settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
+                null, false, "p1");
+        settingsState.deleteSettingLocked(SETTING_NAME);
+
+        settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
+                SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
+        // System package doesn't have memory usage limit
+        settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
+                null, false, SYSTEM_PACKAGE);
+        settingsState.deleteSettingLocked(SETTING_NAME);
+
+        // Should not throw if usage is under the cap
+        settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 19999),
+                null, false, "p1");
+        settingsState.deleteSettingLocked(SETTING_NAME);
+        try {
+            settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
+                    null, false, "p1");
+            fail("Should throw because it exceeded per package memory usage");
+        } catch (IllegalStateException ex) {
+            assertTrue(ex.getMessage().contains("p1"));
+        }
+        try {
+            settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
+                    null, false, "p1");
+            fail("Should throw because it exceeded per package memory usage");
+        } catch (IllegalStateException ex) {
+            assertTrue(ex.getMessage().contains("p1"));
+        }
+        assertTrue(settingsState.getSettingLocked(SETTING_NAME).isNull());
+    }
 }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6edf13a..652e281 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -394,6 +394,11 @@
                   android:label="@string/screenshot_scroll_label"
                   android:finishOnTaskLaunch="true" />
 
+        <service android:name=".screenshot.ScreenshotProxyService"
+                 android:permission="com.android.systemui.permission.SELF"
+                 android:exported="false" />
+
+
         <service android:name=".screenrecord.RecordingService" />
 
         <receiver android:name=".SysuiRestartReceiver"
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 61fac29..4a6164c 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -14,11 +14,10 @@
 brycelee@google.com
 ccassidy@google.com
 chrisgollner@google.com
-cinek@google.com
-cwren@google.com
 dupin@google.com
 ethibodeau@google.com
 evanlaird@google.com
+florenceyang@google.com
 gwasserman@google.com
 hwwang@google.com
 hyunyoungs@google.com
@@ -27,36 +26,36 @@
 jbolinger@google.com
 jdemeulenaere@google.com
 jeffdq@google.com
+jernej@google.com
+jglazier@google.com
 jjaggi@google.com
 jonmiranda@google.com
 joshtrask@google.com
 juliacr@google.com
 juliatuttle@google.com
 justinkoh@google.com
-kchyn@google.com
 kozynski@google.com
 kprevas@google.com
 lynhan@google.com
 madym@google.com
 mankoff@google.com
-mett@google.com
 mkephart@google.com
 mpietal@google.com
 mrcasey@google.com
 mrenouf@google.com
-nesciosquid@google.com
 nickchameyev@google.com
 nicomazz@google.com
 ogunwale@google.com
 peanutbutter@google.com
+peskal@google.com
 pinyaoting@google.com
 pixel@google.com
+pomini@google.com
 rahulbanerjee@google.com
 roosa@google.com
 santie@google.com
 shanh@google.com
 snoeberger@google.com
-sreyasr@google.com
 steell@google.com
 sfufa@google.com
 stwu@google.com
@@ -70,15 +69,10 @@
 victortulias@google.com
 winsonc@google.com
 wleshner@google.com
-yurilin@google.com
 xuqiu@google.com
+yuandizhou@google.com
+yurilin@google.com
 zakcohen@google.com
-jernej@google.com
-jglazier@google.com
-peskal@google.com
-
-#Android Auto
-hseog@google.com
 
 #Android TV
 rgl@google.com
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index 1008481..1ac1e3a 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -17,6 +17,7 @@
 import android.graphics.drawable.Drawable
 import android.view.View
 import com.android.systemui.plugins.annotations.ProvidesInterface
+import com.android.systemui.shared.regionsampling.RegionDarkness
 import java.io.PrintWriter
 import java.util.Locale
 import java.util.TimeZone
@@ -116,12 +117,3 @@
     val clockId: ClockId,
     val name: String
 )
-
-/**
- * Enum for whether clock region is dark or light.
- */
-enum class RegionDarkness(val isDark: Boolean) {
-    DEFAULT(false),
-    DARK(true),
-    LIGHT(false)
-}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/shared/regionsampling/RegionDarkness.kt b/packages/SystemUI/plugin/src/com/android/systemui/shared/regionsampling/RegionDarkness.kt
new file mode 100644
index 0000000..344fdb8
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/shared/regionsampling/RegionDarkness.kt
@@ -0,0 +1,10 @@
+package com.android.systemui.shared.regionsampling
+
+/**
+ * Enum for whether clock region is dark or light.
+ */
+enum class RegionDarkness(val isDark: Boolean) {
+    DEFAULT(false),
+    DARK(true),
+    LIGHT(false)
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 8f1959e..a21a78b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -50,7 +50,12 @@
     defStyleRes: Int = 0
 ) : TextView(context, attrs, defStyleAttr, defStyleRes) {
 
-    private var lastMeasureCall: CharSequence = ""
+    private var lastMeasureCall: CharSequence? = null
+    private var lastDraw: CharSequence? = null
+    private var lastTextUpdate: CharSequence? = null
+    private var lastOnTextChanged: CharSequence? = null
+    private var lastInvalidate: CharSequence? = null
+    private var lastTimeZoneChange: CharSequence? = null
 
     private val time = Calendar.getInstance()
 
@@ -142,7 +147,6 @@
         // relayout if the text didn't actually change.
         if (!TextUtils.equals(text, formattedText)) {
             text = formattedText
-
             // Because the TextLayout may mutate under the hood as a result of the new text, we
             // notify the TextAnimator that it may have changed and request a measure/layout. A
             // crash will occur on the next invocation of setTextStyle if the layout is mutated
@@ -151,18 +155,19 @@
                 textAnimator?.updateLayout(layout)
             }
             requestLayout()
+            lastTextUpdate = getTimestamp()
         }
     }
 
     fun onTimeZoneChanged(timeZone: TimeZone?) {
         time.timeZone = timeZone
         refreshFormat()
+        lastTimeZoneChange = "${getTimestamp()} timeZone=${time.timeZone}"
     }
 
     @SuppressLint("DrawAllocation")
     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec)
-        lastMeasureCall = DateFormat.format(descFormat, System.currentTimeMillis())
         val animator = textAnimator
         if (animator == null) {
             textAnimator = TextAnimator(layout) { invalidate() }
@@ -171,13 +176,34 @@
         } else {
             animator.updateLayout(layout)
         }
+        lastMeasureCall = getTimestamp()
     }
 
     override fun onDraw(canvas: Canvas) {
+        lastDraw = getTimestamp()
         // intentionally doesn't call super.onDraw here or else the text will be rendered twice
         textAnimator?.draw(canvas)
     }
 
+    override fun invalidate() {
+        super.invalidate()
+        lastInvalidate = getTimestamp()
+    }
+
+    private fun getTimestamp(): CharSequence {
+        return "${DateFormat.format("HH:mm:ss", System.currentTimeMillis())} text=$text"
+    }
+
+    override fun onTextChanged(
+            text: CharSequence,
+            start: Int,
+            lengthBefore: Int,
+            lengthAfter: Int
+    ) {
+        super.onTextChanged(text, start, lengthBefore, lengthAfter)
+        lastOnTextChanged = "${getTimestamp()}"
+    }
+
     fun setLineSpacingScale(scale: Float) {
         lineSpacingScale = scale
         setLineSpacing(0f, lineSpacingScale)
@@ -370,7 +396,12 @@
         pw.println("    measuredWidth=$measuredWidth")
         pw.println("    measuredHeight=$measuredHeight")
         pw.println("    singleLineInternal=$isSingleLineInternal")
+        pw.println("    lastTextUpdate=$lastTextUpdate")
+        pw.println("    lastOnTextChanged=$lastOnTextChanged")
+        pw.println("    lastInvalidate=$lastInvalidate")
         pw.println("    lastMeasureCall=$lastMeasureCall")
+        pw.println("    lastDraw=$lastDraw")
+        pw.println("    lastTimeZoneChange=$lastTimeZoneChange")
         pw.println("    currText=$text")
         pw.println("    currTimeContextDesc=$contentDescription")
         pw.println("    time=$time")
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index 3d72f15..6c49186 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -26,8 +26,8 @@
 import com.android.systemui.plugins.ClockId
 import com.android.systemui.plugins.ClockMetadata
 import com.android.systemui.plugins.ClockProvider
-import com.android.systemui.plugins.RegionDarkness
 import com.android.systemui.shared.R
+import com.android.systemui.shared.regionsampling.RegionDarkness
 import java.io.PrintWriter
 import java.util.Locale
 import java.util.TimeZone
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSamplingInstance.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSamplingInstance.kt
index deabc27..0146795 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSamplingInstance.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSamplingInstance.kt
@@ -18,7 +18,6 @@
 import android.graphics.Rect
 import android.view.View
 import androidx.annotation.VisibleForTesting
-import com.android.systemui.plugins.RegionDarkness
 import com.android.systemui.shared.navigationbar.RegionSamplingHelper
 import com.android.systemui.shared.navigationbar.RegionSamplingHelper.SamplingCallback
 import java.io.PrintWriter
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
index 49e378e..d96476f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollProgressBarDrawable.java
@@ -99,12 +99,11 @@
         mProgressColor = context.getColor(R.color.udfps_enroll_progress);
         final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
         mIsAccessibilityEnabled = am.isTouchExplorationEnabled();
+        mOnFirstBucketFailedColor = context.getColor(R.color.udfps_moving_target_fill_error);
         if (!mIsAccessibilityEnabled) {
             mHelpColor = context.getColor(R.color.udfps_enroll_progress_help);
-            mOnFirstBucketFailedColor = context.getColor(R.color.udfps_moving_target_fill_error);
         } else {
             mHelpColor = context.getColor(R.color.udfps_enroll_progress_help_with_talkback);
-            mOnFirstBucketFailedColor = mHelpColor;
         }
         mCheckmarkDrawable = context.getDrawable(R.drawable.udfps_enroll_checkmark);
         mCheckmarkDrawable.mutate();
@@ -167,7 +166,8 @@
     }
 
     private void updateProgress(int remainingSteps, int totalSteps, boolean showingHelp) {
-        if (mRemainingSteps == remainingSteps && mTotalSteps == totalSteps) {
+        if (mRemainingSteps == remainingSteps && mTotalSteps == totalSteps
+                && mShowingHelp == showingHelp) {
             return;
         }
 
@@ -197,6 +197,7 @@
             }
         }
 
+        mShowingHelp = showingHelp;
         mRemainingSteps = remainingSteps;
         mTotalSteps = totalSteps;
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 5dff4a5..4157728 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -26,6 +26,7 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.internal.logging.UiEventLogger;
 import com.android.keyguard.KeyguardViewController;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -61,6 +62,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryControllerImpl;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -191,7 +193,9 @@
             GroupMembershipManager groupManager,
             VisualStabilityProvider visualStabilityProvider,
             ConfigurationController configurationController,
-            @Main Handler handler) {
+            @Main Handler handler,
+            AccessibilityManagerWrapper accessibilityManagerWrapper,
+            UiEventLogger uiEventLogger) {
         return new HeadsUpManagerPhone(
                 context,
                 headsUpManagerLogger,
@@ -200,7 +204,9 @@
                 groupManager,
                 visualStabilityProvider,
                 configurationController,
-                handler
+                handler,
+                accessibilityManagerWrapper,
+                uiEventLogger
         );
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index f32ea35..2dadf57 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -18,6 +18,7 @@
 
 import android.app.INotificationManager;
 import android.content.Context;
+import android.service.dreams.IDreamManager;
 
 import androidx.annotation.Nullable;
 
@@ -213,6 +214,7 @@
             ShadeController shadeController,
             @Nullable IStatusBarService statusBarService,
             INotificationManager notificationManager,
+            IDreamManager dreamManager,
             NotificationVisibilityProvider visibilityProvider,
             NotificationInterruptStateProvider interruptionStateProvider,
             ZenModeController zenModeController,
@@ -230,6 +232,7 @@
                 shadeController,
                 statusBarService,
                 notificationManager,
+                dreamManager,
                 visibilityProvider,
                 interruptionStateProvider,
                 zenModeController,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 42e6b02..1f356cb 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -182,6 +182,7 @@
     public static final UnreleasedFlag MEDIA_SESSION_ACTIONS = new UnreleasedFlag(901);
     public static final ReleasedFlag MEDIA_NEARBY_DEVICES = new ReleasedFlag(903);
     public static final ReleasedFlag MEDIA_MUTE_AWAIT = new ReleasedFlag(904);
+    public static final UnreleasedFlag MEDIA_DREAM_COMPLICATION = new UnreleasedFlag(905);
 
     // 1000 - dock
     public static final ReleasedFlag SIMULATE_DOCK_THROUGH_CHARGING =
@@ -236,6 +237,7 @@
     // 1300 - screenshots
 
     public static final UnreleasedFlag SCREENSHOT_REQUEST_PROCESSOR = new UnreleasedFlag(1300);
+    public static final UnreleasedFlag SCREENSHOT_WORK_PROFILE_POLICY = new UnreleasedFlag(1301);
 
     // 1400 - columbus, b/242800729
     public static final UnreleasedFlag QUICK_TAP_IN_PCC = new UnreleasedFlag(1400);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index ca65d12..da5819a 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -90,6 +90,8 @@
 import android.widget.LinearLayout;
 import android.widget.ListPopupWindow;
 import android.widget.TextView;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
 
 import androidx.annotation.NonNull;
 import androidx.lifecycle.Lifecycle;
@@ -155,6 +157,8 @@
     public static final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
     public static final String SYSTEM_DIALOG_REASON_DREAM = "dream";
 
+    private static final boolean DEBUG = false;
+
     private static final String TAG = "GlobalActionsDialogLite";
 
     private static final String INTERACTION_JANK_TAG = "global_actions";
@@ -2177,6 +2181,11 @@
 
         protected ViewGroup mContainer;
 
+        private final OnBackInvokedCallback mOnBackInvokedCallback = () -> {
+            logOnBackInvocation();
+            dismiss();
+        };
+
         @VisibleForTesting
         protected GestureDetector.SimpleOnGestureListener mGestureListener =
                 new GestureDetector.SimpleOnGestureListener() {
@@ -2221,6 +2230,16 @@
                     }
                 };
 
+
+        // this exists so that we can point it to a mock during Unit Testing
+        private OnBackInvokedDispatcher mOverriddenBackDispatcher;
+
+        // the following method exists so that a Unit Test can supply a `OnBackInvokedDispatcher`
+        @VisibleForTesting
+        void setBackDispatcherOverride(OnBackInvokedDispatcher mockDispatcher) {
+            mOverriddenBackDispatcher = mockDispatcher;
+        }
+
         ActionsDialogLite(Context context, int themeRes, MyAdapter adapter,
                 MyOverflowAdapter overflowAdapter,
                 SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
@@ -2254,6 +2273,22 @@
             super.onCreate(savedInstanceState);
             initializeLayout();
             mWindowDimAmount = getWindow().getAttributes().dimAmount;
+            getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+                    OnBackInvokedDispatcher.PRIORITY_DEFAULT, mOnBackInvokedCallback);
+            if (DEBUG) Log.d(TAG, "OnBackInvokedCallback handler registered");
+        }
+
+        @VisibleForTesting
+        @Override
+        public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
+            if (mOverriddenBackDispatcher != null) return mOverriddenBackDispatcher;
+            else return super.getOnBackInvokedDispatcher();
+        }
+
+        @Override
+        public void onDetachedFromWindow() {
+            getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
+            if (DEBUG) Log.d(TAG, "OnBackInvokedCallback handler unregistered");
         }
 
         @Override
@@ -2453,7 +2488,12 @@
         @Override
         public void onBackPressed() {
             super.onBackPressed();
+            logOnBackInvocation();
+        }
+
+        private void logOnBackInvocation() {
             mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_BACK);
+            if (DEBUG) Log.d(TAG, "onBack invoked");
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 3eb3c80..6dfbd42 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -323,6 +323,8 @@
         if (sEnableRemoteKeyguardOccludeAnimation) {
             Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_(UN)OCCLUDE");
             // Register for occluding
+            final RemoteTransition occludeTransition = new RemoteTransition(
+                    mOccludeAnimation, getIApplicationThread());
             TransitionFilter f = new TransitionFilter();
             f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
             f.mRequirements = new TransitionFilter.Requirement[]{
@@ -337,10 +339,11 @@
             f.mRequirements[1].mMustBeIndependent = false;
             f.mRequirements[1].mFlags = FLAG_OCCLUDES_KEYGUARD;
             f.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
-            mShellTransitions.registerRemote(f,
-                    new RemoteTransition(mOccludeAnimation, getIApplicationThread()));
+            mShellTransitions.registerRemote(f, occludeTransition);
 
             // Now register for un-occlude.
+            final RemoteTransition unoccludeTransition = new RemoteTransition(
+                    mUnoccludeAnimation, getIApplicationThread());
             f = new TransitionFilter();
             f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
             f.mRequirements = new TransitionFilter.Requirement[]{
@@ -358,8 +361,23 @@
             f.mRequirements[0].mMustBeIndependent = false;
             f.mRequirements[0].mFlags = FLAG_OCCLUDES_KEYGUARD;
             f.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
-            mShellTransitions.registerRemote(f,
-                    new RemoteTransition(mUnoccludeAnimation, getIApplicationThread()));
+            mShellTransitions.registerRemote(f, unoccludeTransition);
+
+            // Register for specific transition type.
+            // Above filter cannot fulfill all conditions.
+            // E.g. close top activity while screen off but next activity is occluded, this should
+            // an occluded transition, but since the activity is invisible, the condition would
+            // match unoccluded transition.
+            // But on the contrary, if we add above condition in occluded transition, then when user
+            // trying to dismiss occluded activity when unlock keyguard, the condition would match
+            // occluded transition.
+            f = new TransitionFilter();
+            f.mTypeSet = new int[]{TRANSIT_KEYGUARD_OCCLUDE};
+            mShellTransitions.registerRemote(f, occludeTransition);
+
+            f = new TransitionFilter();
+            f.mTypeSet = new int[]{TRANSIT_KEYGUARD_UNOCCLUDE};
+            mShellTransitions.registerRemote(f, unoccludeTransition);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index b4f40e2..4a7346e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2377,10 +2377,10 @@
     private void handleHide() {
         Trace.beginSection("KeyguardViewMediator#handleHide");
 
-        // It's possible that the device was unlocked in a dream state. It's time to wake up.
-        if (mAodShowing || mDreamOverlayShowing) {
-            PowerManager pm = mContext.getSystemService(PowerManager.class);
-            pm.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
+        // It's possible that the device was unlocked (via BOUNCER) while dozing. It's time to
+        // wake up.
+        if (mAodShowing) {
+            mPM.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
                     "com.android.systemui:BOUNCER_DOZING");
         }
 
@@ -2409,6 +2409,13 @@
                             null /* nonApps */, null /* finishedCallback */);
                 });
             }
+
+            // It's possible that the device was unlocked (via BOUNCER or Fingerprint) while
+            // dreaming. It's time to wake up.
+            if (mDreamOverlayShowing) {
+                mPM.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
+                        "com.android.systemui:UNLOCK_DREAMING");
+            }
         }
         Trace.endSection();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 237b505..32600fb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -18,19 +18,25 @@
 
 import android.content.Context
 import android.content.res.Configuration
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
 import android.view.View
 import android.view.ViewGroup
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.media.dagger.MediaModule.KEYGUARD
 import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.stack.MediaContainerView
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.LargeScreenUtils
+import com.android.systemui.util.settings.SecureSettings
 import javax.inject.Inject
 import javax.inject.Named
 
@@ -43,9 +49,10 @@
     @param:Named(KEYGUARD) private val mediaHost: MediaHost,
     private val bypassController: KeyguardBypassController,
     private val statusBarStateController: SysuiStatusBarStateController,
-    private val notifLockscreenUserManager: NotificationLockscreenUserManager,
     private val context: Context,
-    configurationController: ConfigurationController
+    private val secureSettings: SecureSettings,
+    @Main private val handler: Handler,
+    configurationController: ConfigurationController,
 ) {
 
     init {
@@ -60,6 +67,24 @@
             }
         })
 
+        val settingsObserver: ContentObserver = object : ContentObserver(handler) {
+            override fun onChange(selfChange: Boolean, uri: Uri?) {
+                if (uri == lockScreenMediaPlayerUri) {
+                    allowMediaPlayerOnLockScreen =
+                            secureSettings.getBoolForUser(
+                                    Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
+                                    true,
+                                    UserHandle.USER_CURRENT
+                            )
+                    refreshMediaPosition()
+                }
+            }
+        }
+        secureSettings.registerContentObserverForUser(
+                Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
+                settingsObserver,
+                UserHandle.USER_ALL)
+
         // First let's set the desired state that we want for this host
         mediaHost.expansion = MediaHostState.EXPANDED
         mediaHost.showsOnlyActiveMedia = true
@@ -101,6 +126,13 @@
     private var splitShadeContainer: ViewGroup? = null
 
     /**
+     * Track the media player setting status on lock screen.
+     */
+    private var allowMediaPlayerOnLockScreen: Boolean = true
+    private val lockScreenMediaPlayerUri =
+            secureSettings.getUriFor(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN)
+
+    /**
      * Attaches media container in single pane mode, situated at the top of the notifications list
      */
     fun attachSinglePaneContainer(mediaView: MediaContainerView?) {
@@ -164,7 +196,7 @@
         visible = mediaHost.visible &&
                 !bypassController.bypassEnabled &&
                 keyguardOrUserSwitcher &&
-                notifLockscreenUserManager.shouldShowLockscreenNotifications()
+                allowMediaPlayerOnLockScreen
         if (visible) {
             showMediaPlayer()
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 88a1b17..7b497ad 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.media
 
 import android.app.Notification
+import android.app.Notification.EXTRA_SUBSTITUTE_APP_NAME
 import android.app.PendingIntent
 import android.app.smartspace.SmartspaceConfig
 import android.app.smartspace.SmartspaceManager
@@ -27,6 +28,7 @@
 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
@@ -57,8 +59,8 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.BcSmartspaceDataPlugin
-import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
 import com.android.systemui.statusbar.NotificationMediaManager.isConnectingState
+import com.android.systemui.statusbar.NotificationMediaManager.isPlayingState
 import com.android.systemui.statusbar.notification.row.HybridGroupManager
 import com.android.systemui.tuner.TunerService
 import com.android.systemui.util.Assert
@@ -633,9 +635,14 @@
         }
         val mediaController = mediaControllerFactory.create(token)
         val metadata = mediaController.metadata
+        val notif: Notification = sbn.notification
+
+        val appInfo = notif.extras.getParcelable(
+            Notification.EXTRA_BUILDER_APPLICATION_INFO,
+            ApplicationInfo::class.java
+        ) ?: getAppInfoFromPackage(sbn.packageName)
 
         // Album art
-        val notif: Notification = sbn.notification
         var artworkBitmap = metadata?.let { loadBitmapFromUri(it) }
         if (artworkBitmap == null) {
             artworkBitmap = metadata?.getBitmap(MediaMetadata.METADATA_KEY_ART)
@@ -650,8 +657,7 @@
         }
 
         // App name
-        val builder = Notification.Builder.recoverBuilder(context, notif)
-        val app = builder.loadHeaderAppName()
+        val appName = getAppName(sbn, appInfo)
 
         // App Icon
         val smallIcon = sbn.notification.smallIcon
@@ -712,12 +718,7 @@
 
         val currentEntry = mediaEntries.get(key)
         val instanceId = currentEntry?.instanceId ?: logger.getNewInstanceId()
-        val appUid = try {
-            context.packageManager.getApplicationInfo(sbn.packageName, 0)?.uid!!
-        } catch (e: PackageManager.NameNotFoundException) {
-            Log.w(TAG, "Could not get app UID for ${sbn.packageName}", e)
-            Process.INVALID_UID
-        }
+        val appUid = appInfo?.uid ?: Process.INVALID_UID
 
         if (logEvent) {
             logger.logActiveMediaAdded(appUid, sbn.packageName, instanceId, playbackLocation)
@@ -730,7 +731,7 @@
             val resumeAction: Runnable? = mediaEntries[key]?.resumeAction
             val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
             val active = mediaEntries[key]?.active ?: true
-            onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, app,
+            onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, appName,
                     smallIcon, artist, song, artWorkIcon, actionIcons, actionsToShowCollapsed,
                     semanticActions, sbn.packageName, token, notif.contentIntent, device,
                     active, resumeAction = resumeAction, playbackLocation = playbackLocation,
@@ -740,6 +741,28 @@
         }
     }
 
+    private fun getAppInfoFromPackage(packageName: String): ApplicationInfo? {
+        try {
+            return context.packageManager.getApplicationInfo(packageName, 0)
+        } catch (e: PackageManager.NameNotFoundException) {
+            Log.w(TAG, "Could not get app info for $packageName", e)
+        }
+        return null
+    }
+
+    private fun getAppName(sbn: StatusBarNotification, appInfo: ApplicationInfo?): String {
+        val name = sbn.notification.extras.getString(EXTRA_SUBSTITUTE_APP_NAME)
+        if (name != null) {
+            return name
+        }
+
+        return if (appInfo != null) {
+            context.packageManager.getApplicationLabel(appInfo).toString()
+        } else {
+            sbn.packageName
+        }
+    }
+
     /**
      * Generate action buttons based on notification actions
      */
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 9260020..ae4c7c7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -22,7 +22,12 @@
 import android.annotation.IntDef
 import android.content.Context
 import android.content.res.Configuration
+import android.database.ContentObserver
 import android.graphics.Rect
+import android.net.Uri
+import android.os.Handler
+import android.os.UserHandle
+import android.provider.Settings
 import android.util.Log
 import android.util.MathUtils
 import android.view.View
@@ -33,12 +38,12 @@
 import com.android.systemui.R
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dreams.DreamOverlayStateController
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.shade.NotifPanelEvents
 import com.android.systemui.statusbar.CrossFadeHelper
-import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
@@ -47,6 +52,7 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.LargeScreenUtils
 import com.android.systemui.util.animation.UniqueObjectHostView
+import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.traceSection
 import javax.inject.Inject
 
@@ -85,15 +91,23 @@
     private val keyguardStateController: KeyguardStateController,
     private val bypassController: KeyguardBypassController,
     private val mediaCarouselController: MediaCarouselController,
-    private val notifLockscreenUserManager: NotificationLockscreenUserManager,
     private val keyguardViewController: KeyguardViewController,
     private val dreamOverlayStateController: DreamOverlayStateController,
     configurationController: ConfigurationController,
     wakefulnessLifecycle: WakefulnessLifecycle,
     panelEventsEvents: NotifPanelEvents,
+    private val secureSettings: SecureSettings,
+    @Main private val handler: Handler,
 ) {
 
     /**
+     * Track the media player setting status on lock screen.
+     */
+    private var allowMediaPlayerOnLockScreen: Boolean = true
+    private val lockScreenMediaPlayerUri =
+            secureSettings.getUriFor(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN)
+
+    /**
      * Whether we "skip" QQS during panel expansion.
      *
      * This means that when expanding the panel we go directly to QS. Also when we are on QS and
@@ -521,6 +535,23 @@
                 updateDesiredLocation()
             }
         })
+
+        val settingsObserver: ContentObserver = object : ContentObserver(handler) {
+            override fun onChange(selfChange: Boolean, uri: Uri?) {
+                if (uri == lockScreenMediaPlayerUri) {
+                    allowMediaPlayerOnLockScreen =
+                            secureSettings.getBoolForUser(
+                                    Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
+                                    true,
+                                    UserHandle.USER_CURRENT
+                            )
+                }
+            }
+        }
+        secureSettings.registerContentObserverForUser(
+                Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
+                settingsObserver,
+                UserHandle.USER_ALL)
     }
 
     private fun updateConfiguration() {
@@ -1036,7 +1067,6 @@
         }
         val onLockscreen = (!bypassController.bypassEnabled &&
             (statusbarState == StatusBarState.KEYGUARD))
-        val allowedOnLockscreen = notifLockscreenUserManager.shouldShowLockscreenNotifications()
         val location = when {
             dreamOverlayActive -> LOCATION_DREAM_OVERLAY
             (qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
@@ -1044,7 +1074,7 @@
             !hasActiveMedia -> LOCATION_QS
             onLockscreen && isSplitShadeExpanding() -> LOCATION_QS
             onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
-            onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN
+            onLockscreen && allowMediaPlayerOnLockScreen -> LOCATION_LOCKSCREEN
             else -> LOCATION_QQS
         }
         // When we're on lock screen and the player is not active, we should keep it in QS.
@@ -1116,7 +1146,7 @@
         return !statusBarStateController.isDozing &&
                 !keyguardViewController.isBouncerShowing &&
                 statusBarStateController.state == StatusBarState.KEYGUARD &&
-                notifLockscreenUserManager.shouldShowLockscreenNotifications() &&
+                allowMediaPlayerOnLockScreen &&
                 statusBarStateController.isExpanded &&
                 !qsExpanded
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
index e077fed..c544871 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.media.dream;
 
+import static com.android.systemui.flags.Flags.MEDIA_DREAM_COMPLICATION;
+
 import android.content.Context;
 
 import androidx.annotation.NonNull;
@@ -23,6 +25,7 @@
 
 import com.android.systemui.CoreStartable;
 import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.MediaData;
 import com.android.systemui.media.MediaDataManager;
 import com.android.systemui.media.SmartspaceMediaData;
@@ -34,7 +37,7 @@
  * the media complication as appropriate
  */
 public class MediaDreamSentinel extends CoreStartable {
-    private MediaDataManager.Listener mListener = new MediaDataManager.Listener() {
+    private final MediaDataManager.Listener mListener = new MediaDataManager.Listener() {
         private boolean mAdded;
         @Override
         public void onSmartspaceMediaDataRemoved(@NonNull String key, boolean immediately) {
@@ -63,6 +66,10 @@
         public void onMediaDataLoaded(@NonNull String key, @Nullable String oldKey,
                 @NonNull MediaData data, boolean immediately, int receivedSmartspaceCardLatency,
                 boolean isSsReactivated) {
+            if (!mFeatureFlags.isEnabled(MEDIA_DREAM_COMPLICATION)) {
+                return;
+            }
+
             if (mAdded) {
                 return;
             }
@@ -79,15 +86,18 @@
     private final MediaDataManager mMediaDataManager;
     private final DreamOverlayStateController mDreamOverlayStateController;
     private final MediaDreamComplication mComplication;
+    private final FeatureFlags mFeatureFlags;
 
     @Inject
     public MediaDreamSentinel(Context context, MediaDataManager mediaDataManager,
             DreamOverlayStateController dreamOverlayStateController,
-            MediaDreamComplication complication) {
+            MediaDreamComplication complication,
+            FeatureFlags featureFlags) {
         super(context);
         mMediaDataManager = mediaDataManager;
         mDreamOverlayStateController = dreamOverlayStateController;
         mComplication = complication;
+        mFeatureFlags = featureFlags;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/IScreenshotProxy.aidl b/packages/SystemUI/src/com/android/systemui/screenshot/IScreenshotProxy.aidl
new file mode 100644
index 0000000..f7c4dad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/IScreenshotProxy.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2009, 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.screenshot;
+
+/** Interface implemented by ScreenshotProxyService */
+interface IScreenshotProxy {
+
+    /** Is the notification shade currently exanded? */
+    boolean isNotificationShadeExpanded();
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt
index 39f35a5..7779760 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt
@@ -22,5 +22,5 @@
 
     fun captureDisplay(displayId: Int, crop: Rect? = null): Bitmap?
 
-    fun captureTask(taskId: Int): Bitmap?
+    suspend fun captureTask(taskId: Int): Bitmap?
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
index 258c436..246265b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
@@ -27,13 +27,19 @@
 import android.view.SurfaceControl.DisplayCaptureArgs
 import android.view.SurfaceControl.ScreenshotHardwareBuffer
 import androidx.annotation.VisibleForTesting
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
 
 private const val TAG = "ImageCaptureImpl"
 
+@SysUISingleton
 open class ImageCaptureImpl @Inject constructor(
     private val displayManager: DisplayManager,
-    private val atmService: IActivityTaskManager
+    private val atmService: IActivityTaskManager,
+    @Background private val bgContext: CoroutineDispatcher
 ) : ImageCapture {
 
     override fun captureDisplay(displayId: Int, crop: Rect?): Bitmap? {
@@ -46,8 +52,8 @@
         return buffer?.asBitmap()
     }
 
-    override fun captureTask(taskId: Int): Bitmap? {
-        val snapshot = atmService.takeTaskSnapshot(taskId)
+    override suspend fun captureTask(taskId: Int): Bitmap? {
+        val snapshot = withContext(bgContext) { atmService.takeTaskSnapshot(taskId) } ?: return null
         return Bitmap.wrapHardwareBuffer(snapshot.hardwareBuffer, snapshot.colorSpace)
     }
 
@@ -67,12 +73,17 @@
     }
 
     @VisibleForTesting
-    open fun captureDisplay(displayToken: IBinder, width: Int, height: Int, crop: Rect): ScreenshotHardwareBuffer? {
-        val captureArgs = DisplayCaptureArgs.Builder(displayToken)
-            .setSize(width, height)
-            .setSourceCrop(crop)
-            .build()
+    open fun captureDisplay(
+        displayToken: IBinder,
+        width: Int,
+        height: Int,
+        crop: Rect
+    ): ScreenshotHardwareBuffer? {
+        val captureArgs =
+            DisplayCaptureArgs.Builder(displayToken)
+                .setSize(width, height)
+                .setSourceCrop(crop)
+                .build()
         return SurfaceControl.captureDisplay(captureArgs)
     }
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
index 4397d3d..a918e5d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -16,51 +16,84 @@
 
 package com.android.systemui.screenshot
 
-import android.net.Uri
-import android.util.Log
-import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
+import android.graphics.Insets
 import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
-import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
 import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
 import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags.SCREENSHOT_WORK_PROFILE_POLICY
 import java.util.function.Consumer
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
 
 /**
  * Processes a screenshot request sent from {@link ScreenshotHelper}.
  */
 @SysUISingleton
-internal class RequestProcessor @Inject constructor(
-    private val controller: ScreenshotController,
+class RequestProcessor @Inject constructor(
+    private val capture: ImageCapture,
+    private val policy: ScreenshotPolicy,
+    private val flags: FeatureFlags,
+    /** For the Java Async version, to invoke the callback. */
+    @Application private val mainScope: CoroutineScope
 ) {
-    fun processRequest(
-        request: ScreenshotRequest,
-        onSavedListener: Consumer<Uri>,
-        callback: RequestCallback
-    ) {
+    /**
+     * Inspects the incoming request, returning a potentially modified request depending on policy.
+     *
+     * @param request the request to process
+     */
+    suspend fun process(request: ScreenshotRequest): ScreenshotRequest {
+        var result = request
 
-        if (request.type == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
-            val image = HardwareBitmapBundler.bundleToHardwareBitmap(request.bitmapBundle)
+        // Apply work profile screenshots policy:
+        //
+        // If the focused app belongs to a work profile, transforms a full screen
+        // (or partial) screenshot request to a task snapshot (provided image) screenshot.
 
-            controller.handleImageAsScreenshot(
-                image, request.boundsInScreen, request.insets,
-                request.taskId, request.userId, request.topComponent, onSavedListener, callback
-            )
-            return
+        // Whenever displayContentInfo is fetched, the topComponent is also populated
+        // regardless of the managed profile status.
+
+        if (request.type != TAKE_SCREENSHOT_PROVIDED_IMAGE &&
+            flags.isEnabled(SCREENSHOT_WORK_PROFILE_POLICY)
+        ) {
+
+            val info = policy.findPrimaryContent(policy.getDefaultDisplayId())
+
+            result = if (policy.isManagedProfile(info.userId)) {
+                val image = capture.captureTask(info.taskId)
+                    ?: error("Task snapshot returned a null Bitmap!")
+
+                // Provide the task snapshot as the screenshot
+                ScreenshotRequest(
+                    TAKE_SCREENSHOT_PROVIDED_IMAGE, request.source,
+                    HardwareBitmapBundler.hardwareBitmapToBundle(image),
+                    info.bounds, Insets.NONE, info.taskId, info.userId, info.component
+                )
+            } else {
+                // Create a new request of the same type which includes the top component
+                ScreenshotRequest(request.source, request.type, info.component)
+            }
         }
 
-        when (request.type) {
-            TAKE_SCREENSHOT_FULLSCREEN ->
-                controller.takeScreenshotFullscreen(null, onSavedListener, callback)
-            TAKE_SCREENSHOT_SELECTED_REGION ->
-                controller.takeScreenshotPartial(null, onSavedListener, callback)
-            else -> Log.w(TAG, "Invalid screenshot option: ${request.type}")
-        }
+        return result
     }
 
-    companion object {
-        const val TAG: String = "RequestProcessor"
+    /**
+     * Note: This is for compatibility with existing Java. Prefer the suspending function when
+     * calling from a Coroutine context.
+     *
+     * @param request the request to process
+     * @param callback the callback to provide the processed request, invoked from the main thread
+     */
+    fun processAsync(request: ScreenshotRequest, callback: Consumer<ScreenshotRequest>) {
+        mainScope.launch {
+            val result = process(request)
+            callback.accept(result)
+        }
     }
 }
+
+private const val TAG = "RequestProcessor"
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicy.kt
new file mode 100644
index 0000000..3580010
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicy.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.annotation.UserIdInt
+import android.content.ComponentName
+import android.graphics.Rect
+import android.view.Display
+
+/**
+ * Provides policy decision-making information to screenshot request handling.
+ */
+interface ScreenshotPolicy {
+
+    /** @return true if the user is a managed profile (a.k.a. work profile) */
+    suspend fun isManagedProfile(@UserIdInt userId: Int): Boolean
+
+    /**
+     * Requests information about the owner of display content which occupies a majority of the
+     * screenshot and/or has most recently been interacted with at the time the screenshot was
+     * requested.
+     *
+     * @param displayId the id of the display to inspect
+     * @return content info for the primary content on the display
+     */
+    suspend fun findPrimaryContent(displayId: Int): DisplayContentInfo
+
+    data class DisplayContentInfo(
+        val component: ComponentName,
+        val bounds: Rect,
+        @UserIdInt val userId: Int,
+        val taskId: Int,
+    )
+
+    fun getDefaultDisplayId(): Int = Display.DEFAULT_DISPLAY
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt
new file mode 100644
index 0000000..ba809f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.annotation.UserIdInt
+import android.app.ActivityTaskManager
+import android.app.ActivityTaskManager.RootTaskInfo
+import android.app.IActivityTaskManager
+import android.app.WindowConfiguration
+import android.app.WindowConfiguration.activityTypeToString
+import android.app.WindowConfiguration.windowingModeToString
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.graphics.Rect
+import android.os.Process
+import android.os.RemoteException
+import android.os.UserManager
+import android.util.Log
+import android.view.Display.DEFAULT_DISPLAY
+import com.android.internal.infra.ServiceConnector
+import com.android.systemui.SystemUIService
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo
+import java.util.Arrays
+import javax.inject.Inject
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+
+@SysUISingleton
+internal class ScreenshotPolicyImpl @Inject constructor(
+    context: Context,
+    private val userMgr: UserManager,
+    private val atmService: IActivityTaskManager,
+    @Background val bgDispatcher: CoroutineDispatcher,
+) : ScreenshotPolicy {
+
+    private val systemUiContent =
+        DisplayContentInfo(
+            ComponentName(context, SystemUIService::class.java),
+            Rect(),
+            ActivityTaskManager.INVALID_TASK_ID,
+            Process.myUserHandle().identifier,
+        )
+
+    private val proxyConnector: ServiceConnector<IScreenshotProxy> =
+        ServiceConnector.Impl(
+            context,
+            Intent(context, ScreenshotProxyService::class.java),
+            Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE,
+            context.userId,
+            IScreenshotProxy.Stub::asInterface
+        )
+
+    override fun getDefaultDisplayId(): Int {
+        return DEFAULT_DISPLAY
+    }
+
+    override suspend fun isManagedProfile(@UserIdInt userId: Int): Boolean {
+        return withContext(bgDispatcher) { userMgr.isManagedProfile(userId) }
+    }
+
+    private fun nonPipVisibleTask(info: RootTaskInfo): Boolean {
+        return info.windowingMode != WindowConfiguration.WINDOWING_MODE_PINNED &&
+            info.isVisible &&
+            info.isRunning &&
+            info.numActivities > 0 &&
+            info.topActivity != null &&
+            info.childTaskIds.isNotEmpty()
+    }
+
+    /**
+     * Uses RootTaskInfo from ActivityTaskManager to guess at the primary focused task within a
+     * display. If no task is visible or the top task is covered by a system window, the info
+     * reported will reference a SystemUI component instead.
+     */
+    override suspend fun findPrimaryContent(displayId: Int): DisplayContentInfo {
+        // Determine if the notification shade is expanded. If so, task windows are not
+        // visible behind it, so the screenshot should instead be associated with SystemUI.
+        if (isNotificationShadeExpanded()) {
+            return systemUiContent
+        }
+
+        val taskInfoList = getAllRootTaskInfosOnDisplay(displayId)
+        if (DEBUG) {
+            debugLogRootTaskInfos(taskInfoList)
+        }
+
+        // If no visible task is located, then report SystemUI as the foreground content
+        val target = taskInfoList.firstOrNull(::nonPipVisibleTask) ?: return systemUiContent
+
+        val topActivity: ComponentName = target.topActivity ?: error("should not be null")
+        val topChildTask = target.childTaskIds.size - 1
+        val childTaskId = target.childTaskIds[topChildTask]
+        val childTaskUserId = target.childTaskUserIds[topChildTask]
+        val childTaskBounds = target.childTaskBounds[topChildTask]
+
+        return DisplayContentInfo(topActivity, childTaskBounds, childTaskId, childTaskUserId)
+    }
+
+    private fun debugLogRootTaskInfos(taskInfoList: List<RootTaskInfo>) {
+        for (info in taskInfoList) {
+            Log.d(
+                TAG,
+                "[root task info] " +
+                    "taskId=${info.taskId} " +
+                    "parentTaskId=${info.parentTaskId} " +
+                    "position=${info.position} " +
+                    "positionInParent=${info.positionInParent} " +
+                    "isVisible=${info.isVisible()} " +
+                    "visible=${info.visible} " +
+                    "isFocused=${info.isFocused} " +
+                    "isSleeping=${info.isSleeping} " +
+                    "isRunning=${info.isRunning} " +
+                    "windowMode=${windowingModeToString(info.windowingMode)} " +
+                    "activityType=${activityTypeToString(info.activityType)} " +
+                    "topActivity=${info.topActivity} " +
+                    "topActivityInfo=${info.topActivityInfo} " +
+                    "numActivities=${info.numActivities} " +
+                    "childTaskIds=${Arrays.toString(info.childTaskIds)} " +
+                    "childUserIds=${Arrays.toString(info.childTaskUserIds)} " +
+                    "childTaskBounds=${Arrays.toString(info.childTaskBounds)} " +
+                    "childTaskNames=${Arrays.toString(info.childTaskNames)}"
+            )
+
+            for (j in 0 until info.childTaskIds.size) {
+                Log.d(TAG, "    *** [$j] ******")
+                Log.d(TAG, "        ***  childTaskIds[$j]: ${info.childTaskIds[j]}")
+                Log.d(TAG, "        ***  childTaskUserIds[$j]: ${info.childTaskUserIds[j]}")
+                Log.d(TAG, "        ***  childTaskBounds[$j]: ${info.childTaskBounds[j]}")
+                Log.d(TAG, "        ***  childTaskNames[$j]: ${info.childTaskNames[j]}")
+            }
+        }
+    }
+
+    private suspend fun getAllRootTaskInfosOnDisplay(displayId: Int): List<RootTaskInfo> =
+        withContext(bgDispatcher) {
+            try {
+                atmService.getAllRootTaskInfosOnDisplay(displayId)
+            } catch (e: RemoteException) {
+                Log.e(TAG, "getAllRootTaskInfosOnDisplay", e)
+                listOf()
+            }
+        }
+
+    private suspend fun isNotificationShadeExpanded(): Boolean = suspendCoroutine { k ->
+        proxyConnector
+            .postForResult { it.isNotificationShadeExpanded }
+            .whenComplete { expanded, error ->
+                if (error != null) {
+                    Log.e(TAG, "isNotificationShadeExpanded", error)
+                }
+                k.resume(expanded ?: false)
+            }
+    }
+
+    companion object {
+        const val TAG: String = "ScreenshotPolicyImpl"
+        const val DEBUG: Boolean = false
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
new file mode 100644
index 0000000..9654e03
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenshot
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+import android.util.Log
+import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager
+import javax.inject.Inject
+
+/**
+ * Provides state from the main SystemUI process on behalf of the Screenshot process.
+ */
+internal class ScreenshotProxyService @Inject constructor(
+    private val mExpansionMgr: PanelExpansionStateManager
+) : Service() {
+
+    private val mBinder: IBinder = object : IScreenshotProxy.Stub() {
+        /**
+         * @return true when the notification shade is partially or fully expanded.
+         */
+        override fun isNotificationShadeExpanded(): Boolean {
+            val expanded = !mExpansionMgr.isClosed()
+            Log.d(TAG, "isNotificationShadeExpanded(): $expanded")
+            return expanded
+        }
+    }
+
+    override fun onBind(intent: Intent): IBinder? {
+        Log.d(TAG, "onBind: $intent")
+        return mBinder
+    }
+
+    companion object {
+        const val TAG = "ScreenshotProxyService"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 7bf3217..a8993bc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -22,6 +22,7 @@
 import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_COMPLETE;
 import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI;
 import static com.android.systemui.flags.Flags.SCREENSHOT_REQUEST_PROCESSOR;
+import static com.android.systemui.flags.Flags.SCREENSHOT_WORK_PROFILE_POLICY;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
 import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE;
@@ -97,7 +98,7 @@
     };
 
     /** Informs about coarse grained state of the Controller. */
-    interface RequestCallback {
+    public interface RequestCallback {
         /** Respond to the current request indicating the screenshot request failed. */
         void reportError();
 
@@ -124,6 +125,7 @@
         mBgExecutor = bgExecutor;
         mFeatureFlags = featureFlags;
         mFeatureFlags.addListener(SCREENSHOT_REQUEST_PROCESSOR, FlagEvent::requestNoRestart);
+        mFeatureFlags.addListener(SCREENSHOT_WORK_PROFILE_POLICY, FlagEvent::requestNoRestart);
         mProcessor = processor;
     }
 
@@ -229,49 +231,57 @@
 
         if (mFeatureFlags.isEnabled(SCREENSHOT_REQUEST_PROCESSOR)) {
             Log.d(TAG, "handleMessage: Using request processor");
-            mProcessor.processRequest(screenshotRequest, uriConsumer, requestCallback);
+            mProcessor.processAsync(screenshotRequest,
+                    (request) -> dispatchToController(request, uriConsumer, requestCallback));
             return true;
         }
 
-        switch (screenshotRequest.getType()) {
+        dispatchToController(screenshotRequest, uriConsumer, requestCallback);
+        return true;
+    }
+
+    private void dispatchToController(ScreenshotHelper.ScreenshotRequest request,
+            Consumer<Uri> uriConsumer, RequestCallback callback) {
+
+        ComponentName topComponent = request.getTopComponent();
+
+        switch (request.getType()) {
             case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
                 if (DEBUG_SERVICE) {
                     Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_FULLSCREEN");
                 }
-                mScreenshot.takeScreenshotFullscreen(topComponent, uriConsumer, requestCallback);
+                mScreenshot.takeScreenshotFullscreen(topComponent, uriConsumer, callback);
                 break;
             case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
                 if (DEBUG_SERVICE) {
                     Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_SELECTED_REGION");
                 }
-                mScreenshot.takeScreenshotPartial(topComponent, uriConsumer, requestCallback);
+                mScreenshot.takeScreenshotPartial(topComponent, uriConsumer, callback);
                 break;
             case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
                 if (DEBUG_SERVICE) {
                     Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_PROVIDED_IMAGE");
                 }
                 Bitmap screenshot = ScreenshotHelper.HardwareBitmapBundler.bundleToHardwareBitmap(
-                        screenshotRequest.getBitmapBundle());
-                Rect screenBounds = screenshotRequest.getBoundsInScreen();
-                Insets insets = screenshotRequest.getInsets();
-                int taskId = screenshotRequest.getTaskId();
-                int userId = screenshotRequest.getUserId();
+                        request.getBitmapBundle());
+                Rect screenBounds = request.getBoundsInScreen();
+                Insets insets = request.getInsets();
+                int taskId = request.getTaskId();
+                int userId = request.getUserId();
 
                 if (screenshot == null) {
                     Log.e(TAG, "Got null bitmap from screenshot message");
                     mNotificationsController.notifyScreenshotError(
                             R.string.screenshot_failed_to_capture_text);
-                    requestCallback.reportError();
+                    callback.reportError();
                 } else {
                     mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
-                            taskId, userId, topComponent, uriConsumer, requestCallback);
+                            taskId, userId, topComponent, uriConsumer, callback);
                 }
                 break;
             default:
-                Log.w(TAG, "Invalid screenshot option: " + msg.what);
-                return false;
+                Log.w(TAG, "Invalid screenshot option: " + request.getType());
         }
-        return true;
     }
 
     private static void sendComplete(Messenger target) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 3e442587..fdb0100 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +20,9 @@
 
 import com.android.systemui.screenshot.ImageCapture;
 import com.android.systemui.screenshot.ImageCaptureImpl;
+import com.android.systemui.screenshot.ScreenshotPolicy;
+import com.android.systemui.screenshot.ScreenshotPolicyImpl;
+import com.android.systemui.screenshot.ScreenshotProxyService;
 import com.android.systemui.screenshot.TakeScreenshotService;
 
 import dagger.Binds;
@@ -33,12 +36,20 @@
 @Module
 public abstract class ScreenshotModule {
 
-    /** */
     @Binds
     @IntoMap
     @ClassKey(TakeScreenshotService.class)
-    public abstract Service bindTakeScreenshotService(TakeScreenshotService service);
+    abstract Service bindTakeScreenshotService(TakeScreenshotService service);
 
     @Binds
-    public abstract ImageCapture bindImageCapture(ImageCaptureImpl capture);
+    @IntoMap
+    @ClassKey(ScreenshotProxyService.class)
+    abstract Service bindScreenshotProxyService(ScreenshotProxyService service);
+
+    @Binds
+    abstract ScreenshotPolicy bindScreenshotPolicyImpl(ScreenshotPolicyImpl impl);
+
+    @Binds
+    abstract ImageCapture bindImageCaptureImpl(ImageCaptureImpl capture);
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 65ba5ad..e754d5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -512,7 +512,8 @@
                     Trace.beginSection("MODE_WAKE_AND_UNLOCK");
                 } else {
                     Trace.beginSection("MODE_WAKE_AND_UNLOCK_FROM_DREAM");
-                    mUpdateMonitor.awakenFromDream();
+                    // Don't call awaken from Dream here. In order to avoid flickering, wait until
+                    // later to awaken.
                 }
                 mNotificationShadeWindowController.setNotificationShadeFocusable(false);
                 mKeyguardViewMediator.onWakeAndUnlocking();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 4c9c75b..103e4f6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -27,6 +27,7 @@
 import androidx.collection.ArraySet;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
@@ -39,6 +40,7 @@
 import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
@@ -107,8 +109,10 @@
             GroupMembershipManager groupMembershipManager,
             VisualStabilityProvider visualStabilityProvider,
             ConfigurationController configurationController,
-            @Main Handler handler) {
-        super(context, logger, handler);
+            @Main Handler handler,
+            AccessibilityManagerWrapper accessibilityManagerWrapper,
+            UiEventLogger uiEventLogger) {
+        super(context, logger, handler, accessibilityManagerWrapper, uiEventLogger);
         Resources resources = mContext.getResources();
         mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time);
         statusBarStateController.addCallback(mStatusBarStateListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 699414c..e4e59a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -32,7 +32,6 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
-import com.android.systemui.Dependency;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Main;
@@ -81,12 +80,15 @@
         }
     }
 
-    public HeadsUpManager(@NonNull final Context context, HeadsUpManagerLogger logger,
-            @Main Handler handler) {
+    public HeadsUpManager(@NonNull final Context context,
+            HeadsUpManagerLogger logger,
+            @Main Handler handler,
+            AccessibilityManagerWrapper accessibilityManagerWrapper,
+            UiEventLogger uiEventLogger) {
         super(logger, handler);
         mContext = context;
-        mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class);
-        mUiEventLogger = Dependency.get(UiEventLogger.class);
+        mAccessibilityMgr = accessibilityManagerWrapper;
+        mUiEventLogger = uiEventLogger;
         Resources resources = context.getResources();
         mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
         mAutoDismissNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 094490b..adef182 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -244,7 +244,8 @@
         final int currentUser = mUserTracker.getUserId();
         final boolean hadWallpaperColors = mCurrentColors.get(userId) != null;
         int latestWallpaperType = getLatestWallpaperType(userId);
-        if ((flags & latestWallpaperType) != 0) {
+        boolean eventForLatestWallpaper = (flags & latestWallpaperType) != 0;
+        if (eventForLatestWallpaper) {
             mCurrentColors.put(userId, wallpaperColors);
             if (DEBUG) Log.d(TAG, "got new colors: " + wallpaperColors + " where: " + flags);
         }
@@ -280,14 +281,19 @@
                 currentUser);
         boolean isDestinationBoth = (flags == (WallpaperManager.FLAG_SYSTEM
                 | WallpaperManager.FLAG_LOCK));
+        boolean isDestinationHomeOnly = (flags == WallpaperManager.FLAG_SYSTEM);
         try {
             JSONObject jsonObject = (overlayPackageJson == null) ? new JSONObject()
                     : new JSONObject(overlayPackageJson);
             // The latest applied wallpaper should be the source of system colors when:
             // There is not preset color applied and the incoming wallpaper color is not applied
-            if (!COLOR_SOURCE_PRESET.equals(jsonObject.optString(OVERLAY_COLOR_SOURCE))
-                    && ((flags & latestWallpaperType) != 0 && !isSeedColorSet(jsonObject,
-                    wallpaperColors))) {
+            String wallpaperPickerColorSource = jsonObject.optString(OVERLAY_COLOR_SOURCE);
+            boolean userChosePresetColor = COLOR_SOURCE_PRESET.equals(wallpaperPickerColorSource);
+            boolean userChoseLockScreenColor = COLOR_SOURCE_LOCK.equals(wallpaperPickerColorSource);
+            boolean preserveLockScreenColor = isDestinationHomeOnly && userChoseLockScreenColor;
+
+            if (!userChosePresetColor && !preserveLockScreenColor && eventForLatestWallpaper
+                    && !isSeedColorSet(jsonObject, wallpaperColors)) {
                 mSkipSettingChange = true;
                 if (jsonObject.has(OVERLAY_CATEGORY_ACCENT_COLOR) || jsonObject.has(
                         OVERLAY_CATEGORY_SYSTEM_PALETTE)) {
@@ -642,7 +648,7 @@
         }
         if (mNeedsOverlayCreation) {
             mNeedsOverlayCreation = false;
-            mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[] {
+            mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[]{
                     mSecondaryOverlay, mNeutralOverlay
             }, currentUser, managedProfiles);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index 0fe10cb..abffe555 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -26,6 +26,7 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.internal.logging.UiEventLogger;
 import com.android.keyguard.KeyguardViewController;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
@@ -63,6 +64,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryControllerImpl;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -173,7 +175,9 @@
             GroupMembershipManager groupManager,
             VisualStabilityProvider visualStabilityProvider,
             ConfigurationController configurationController,
-            @Main Handler handler) {
+            @Main Handler handler,
+            AccessibilityManagerWrapper accessibilityManagerWrapper,
+            UiEventLogger uiEventLogger) {
         return new HeadsUpManagerPhone(
                 context,
                 headsUpManagerLogger,
@@ -182,7 +186,9 @@
                 groupManager,
                 visualStabilityProvider,
                 configurationController,
-                handler
+                handler,
+                accessibilityManagerWrapper,
+                uiEventLogger
         );
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 4c76270..aeab2df 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -38,6 +38,7 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.service.dreams.IDreamManager;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.ZenModeConfig;
 import android.util.Log;
@@ -101,6 +102,7 @@
     private final ShadeController mShadeController;
     private final IStatusBarService mBarService;
     private final INotificationManager mNotificationManager;
+    private final IDreamManager mDreamManager;
     private final NotificationVisibilityProvider mVisibilityProvider;
     private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
     private final NotificationLockscreenUserManager mNotifUserManager;
@@ -126,6 +128,7 @@
             ShadeController shadeController,
             @Nullable IStatusBarService statusBarService,
             INotificationManager notificationManager,
+            IDreamManager dreamManager,
             NotificationVisibilityProvider visibilityProvider,
             NotificationInterruptStateProvider interruptionStateProvider,
             ZenModeController zenModeController,
@@ -144,6 +147,7 @@
                     shadeController,
                     statusBarService,
                     notificationManager,
+                    dreamManager,
                     visibilityProvider,
                     interruptionStateProvider,
                     zenModeController,
@@ -167,6 +171,7 @@
             ShadeController shadeController,
             @Nullable IStatusBarService statusBarService,
             INotificationManager notificationManager,
+            IDreamManager dreamManager,
             NotificationVisibilityProvider visibilityProvider,
             NotificationInterruptStateProvider interruptionStateProvider,
             ZenModeController zenModeController,
@@ -182,6 +187,7 @@
         mNotificationShadeWindowController = notificationShadeWindowController;
         mShadeController = shadeController;
         mNotificationManager = notificationManager;
+        mDreamManager = dreamManager;
         mVisibilityProvider = visibilityProvider;
         mNotificationInterruptStateProvider = interruptionStateProvider;
         mNotifUserManager = notifUserManager;
@@ -203,7 +209,7 @@
             @Override
             public void onKeyguardShowingChanged() {
                 boolean isUnlockedShade = !keyguardStateController.isShowing()
-                        && !keyguardStateController.isOccluded();
+                        && !isDreamingOrInPreview();
                 bubbles.onStatusBarStateChanged(isUnlockedShade);
             }
         });
@@ -397,6 +403,15 @@
         mBubbles.setSysuiProxy(mSysuiProxy);
     }
 
+    private boolean isDreamingOrInPreview() {
+        try {
+            return mDreamManager.isDreamingOrInPreview();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to query dream manager.", e);
+            return false;
+        }
+    }
+
     private void setupNotifPipeline() {
         mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
             @Override
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 763a5cb..ba28045 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -53,7 +53,8 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.REGISTER_WINDOW_MANAGER_LISTENERS" />
 
-    <application android:debuggable="true" android:largeHeap="true">
+    <application android:debuggable="true" android:largeHeap="true"
+            android:enableOnBackInvokedCallback="true" >
         <uses-library android:name="android.test.runner" />
 
         <receiver android:name="com.android.systemui.SliceBroadcastRelayHandlerTest$Receiver"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 141a213..b42b769 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -42,8 +42,11 @@
 import android.testing.TestableLooper;
 import android.view.GestureDetector;
 import android.view.IWindowManager;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.WindowManagerPolicyConstants;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
 
 import androidx.test.filters.SmallTest;
 
@@ -73,6 +76,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -117,6 +122,8 @@
     @Mock private CentralSurfaces mCentralSurfaces;
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock private DialogLaunchAnimator mDialogLaunchAnimator;
+    @Mock private OnBackInvokedDispatcher mOnBackInvokedDispatcher;
+    @Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
 
     private TestableLooper mTestableLooper;
 
@@ -203,6 +210,58 @@
     }
 
     @Test
+    public void testPredictiveBackCallbackRegisteredAndUnregistered() {
+        mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+        doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+        doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+        doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+        String[] actions = {
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
+        };
+        doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+
+        GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
+        dialog.setBackDispatcherOverride(mOnBackInvokedDispatcher);
+        dialog.create();
+        mTestableLooper.processAllMessages();
+        verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+                eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), any());
+        dialog.onDetachedFromWindow();
+        mTestableLooper.processAllMessages();
+        verify(mOnBackInvokedDispatcher).unregisterOnBackInvokedCallback(any());
+    }
+
+    @Test
+    public void testPredictiveBackInvocationDismissesDialog() {
+        mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
+        doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
+        doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
+        doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
+        String[] actions = {
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_POWER,
+                GlobalActionsDialogLite.GLOBAL_ACTION_KEY_RESTART,
+        };
+        doReturn(actions).when(mGlobalActionsDialogLite).getDefaultActions();
+
+        GlobalActionsDialogLite.ActionsDialogLite dialog = mGlobalActionsDialogLite.createDialog();
+        dialog.create();
+        dialog.show();
+        mTestableLooper.processAllMessages();
+        dialog.getWindow().injectInputEvent(
+                new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
+        dialog.getWindow().injectInputEvent(
+                new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK));
+        mTestableLooper.processAllMessages();
+        verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_BACK);
+        assertThat(dialog.isShowing()).isFalse();
+    }
+
+    @Test
     public void testSingleTap_logAndDismiss() {
         mGlobalActionsDialogLite = spy(mGlobalActionsDialogLite);
         doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
index fcfef4a4..c41fac7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
@@ -16,19 +16,22 @@
 
 package com.android.systemui.media
 
+import android.provider.Settings
 import android.test.suitebuilder.annotation.SmallTest
 import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
 import android.view.View.GONE
 import android.view.View.VISIBLE
 import android.widget.FrameLayout
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.stack.MediaContainerView
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.animation.UniqueObjectHostView
+import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.utils.os.FakeHandler
 import com.google.common.truth.Truth.assertThat
 import junit.framework.Assert.assertTrue
 import org.junit.Before
@@ -37,11 +40,12 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
 class KeyguardMediaControllerTest : SysuiTestCase() {
 
     @Mock
@@ -53,31 +57,33 @@
     @Mock
     private lateinit var configurationController: ConfigurationController
 
-    @Mock
-    private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
     @JvmField @Rule
     val mockito = MockitoJUnit.rule()
 
     private val mediaContainerView: MediaContainerView = MediaContainerView(context, null)
     private val hostView = UniqueObjectHostView(context)
+    private val settings = FakeSettings()
     private lateinit var keyguardMediaController: KeyguardMediaController
+    private lateinit var testableLooper: TestableLooper
+    private lateinit var fakeHandler: FakeHandler
 
     @Before
     fun setup() {
         // default state is positive, media should show up
         whenever(mediaHost.visible).thenReturn(true)
         whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
-        whenever(notificationLockscreenUserManager.shouldShowLockscreenNotifications())
-                .thenReturn(true)
         whenever(mediaHost.hostView).thenReturn(hostView)
         hostView.layoutParams = FrameLayout.LayoutParams(100, 100)
+        testableLooper = TestableLooper.get(this)
+        fakeHandler = FakeHandler(testableLooper.looper)
         keyguardMediaController = KeyguardMediaController(
             mediaHost,
             bypassController,
             statusBarStateController,
-            notificationLockscreenUserManager,
             context,
-            configurationController
+            settings,
+            fakeHandler,
+            configurationController,
         )
         keyguardMediaController.attachSinglePaneContainer(mediaContainerView)
         keyguardMediaController.useSplitShade = false
@@ -106,9 +112,8 @@
     }
 
     @Test
-    fun testHiddenOnKeyguard_whenNotificationsAreHidden() {
-        whenever(notificationLockscreenUserManager.shouldShowLockscreenNotifications())
-                .thenReturn(false)
+    fun testHiddenOnKeyguard_whenMediaOnLockScreenDisabled() {
+        settings.putInt(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, 0)
 
         keyguardMediaController.refreshMediaPosition()
 
@@ -116,6 +121,15 @@
     }
 
     @Test
+    fun testAvailableOnKeyguard_whenMediaOnLockScreenEnabled() {
+        settings.putInt(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, 1)
+
+        keyguardMediaController.refreshMediaPosition()
+
+        assertThat(mediaContainerView.visibility).isEqualTo(VISIBLE)
+    }
+
+    @Test
     fun testActivatesSplitShadeContainerInSplitShadeMode() {
         val splitShadeContainer = FrameLayout(context)
         keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
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 1cce7cf..d1ed8e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -50,11 +50,10 @@
 import org.mockito.Mockito
 import org.mockito.Mockito.never
 import org.mockito.Mockito.reset
-import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.junit.MockitoJUnit
 import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
 
 private const val KEY = "KEY"
 private const val KEY_2 = "KEY_2"
@@ -287,6 +286,30 @@
     }
 
     @Test
+    fun testOnNotificationAdded_hasSubstituteName_isUsed() {
+        val subName = "Substitute Name"
+        val notif = SbnBuilder().run {
+            modifyNotification(context).also {
+                it.extras = Bundle().apply {
+                    putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, subName)
+                }
+                it.setStyle(MediaStyle().apply {
+                    setMediaSession(session.sessionToken)
+                })
+            }
+            build()
+        }
+
+        mediaDataManager.onNotificationAdded(KEY, notif)
+        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor), eq(true),
+            eq(0), eq(false))
+
+        assertThat(mediaDataCaptor.value!!.app).isEqualTo(subName)
+    }
+
+    @Test
     fun testLoadMediaDataInBg_invalidTokenNoCrash() {
         val bundle = Bundle()
         // wrong data type
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index 369913d..18bfd04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.media
 
 import android.graphics.Rect
+import android.provider.Settings
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.ViewGroup
@@ -30,7 +31,6 @@
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.shade.testing.FakeNotifPanelEvents
-import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.phone.KeyguardBypassController
@@ -38,6 +38,8 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.animation.UniqueObjectHostView
 import com.android.systemui.util.mockito.any
+import com.android.systemui.util.settings.FakeSettings
+import com.android.systemui.utils.os.FakeHandler
 import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertNotNull
 import org.junit.Before
@@ -53,7 +55,6 @@
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
 
@@ -68,7 +69,6 @@
     @Mock private lateinit var bypassController: KeyguardBypassController
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
-    @Mock private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
     @Mock private lateinit var mediaCarouselController: MediaCarouselController
     @Mock private lateinit var mediaCarouselScrollHandler: MediaCarouselScrollHandler
     @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@@ -82,37 +82,42 @@
     @JvmField
     @Rule
     val mockito = MockitoJUnit.rule()
-    private lateinit var mediaHiearchyManager: MediaHierarchyManager
+    private lateinit var mediaHierarchyManager: MediaHierarchyManager
     private lateinit var mediaFrame: ViewGroup
     private val configurationController = FakeConfigurationController()
     private val notifPanelEvents = FakeNotifPanelEvents()
+    private val settings = FakeSettings()
+    private lateinit var testableLooper: TestableLooper
+    private lateinit var fakeHandler: FakeHandler
 
     @Before
     fun setup() {
         context.getOrCreateTestableResources().addOverride(
                 R.bool.config_use_split_notification_shade, false)
         mediaFrame = FrameLayout(context)
-        `when`(mediaCarouselController.mediaFrame).thenReturn(mediaFrame)
-        mediaHiearchyManager = MediaHierarchyManager(
+        testableLooper = TestableLooper.get(this)
+        fakeHandler = FakeHandler(testableLooper.looper)
+        whenever(mediaCarouselController.mediaFrame).thenReturn(mediaFrame)
+        mediaHierarchyManager = MediaHierarchyManager(
                 context,
                 statusBarStateController,
                 keyguardStateController,
                 bypassController,
                 mediaCarouselController,
-                notificationLockscreenUserManager,
                 keyguardViewController,
                 dreamOverlayStateController,
                 configurationController,
                 wakefulnessLifecycle,
                 notifPanelEvents,
-        )
+                settings,
+                fakeHandler,)
         verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
         verify(statusBarStateController).addCallback(statusBarCallback.capture())
         setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN, LOCKSCREEN_TOP)
         setupHost(qsHost, MediaHierarchyManager.LOCATION_QS, QS_TOP)
         setupHost(qqsHost, MediaHierarchyManager.LOCATION_QQS, QQS_TOP)
-        `when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
-        `when`(mediaCarouselController.mediaCarouselScrollHandler)
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
+        whenever(mediaCarouselController.mediaCarouselScrollHandler)
                 .thenReturn(mediaCarouselScrollHandler)
         val observer = wakefullnessObserver.value
         assertNotNull("lifecycle observer wasn't registered", observer)
@@ -122,30 +127,30 @@
     }
 
     private fun setupHost(host: MediaHost, location: Int, top: Int) {
-        `when`(host.location).thenReturn(location)
-        `when`(host.currentBounds).thenReturn(Rect(0, top, 0, top))
-        `when`(host.hostView).thenReturn(uniqueObjectHostView)
-        `when`(host.visible).thenReturn(true)
-        mediaHiearchyManager.register(host)
+        whenever(host.location).thenReturn(location)
+        whenever(host.currentBounds).thenReturn(Rect(0, top, 0, top))
+        whenever(host.hostView).thenReturn(uniqueObjectHostView)
+        whenever(host.visible).thenReturn(true)
+        mediaHierarchyManager.register(host)
     }
 
     @Test
     fun testHostViewSetOnRegister() {
-        val host = mediaHiearchyManager.register(lockHost)
+        val host = mediaHierarchyManager.register(lockHost)
         verify(lockHost).hostView = eq(host)
     }
 
     @Test
     fun testBlockedWhenScreenTurningOff() {
         // Let's set it onto QS:
-        mediaHiearchyManager.qsExpansion = 1.0f
+        mediaHierarchyManager.qsExpansion = 1.0f
         verify(mediaCarouselController).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
                 any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
         val observer = wakefullnessObserver.value
         assertNotNull("lifecycle observer wasn't registered", observer)
         observer.onStartedGoingToSleep()
         clearInvocations(mediaCarouselController)
-        mediaHiearchyManager.qsExpansion = 0.0f
+        mediaHierarchyManager.qsExpansion = 0.0f
         verify(mediaCarouselController, times(0))
                 .onDesiredLocationChanged(ArgumentMatchers.anyInt(),
                 any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
@@ -154,13 +159,13 @@
     @Test
     fun testAllowedWhenNotTurningOff() {
         // Let's set it onto QS:
-        mediaHiearchyManager.qsExpansion = 1.0f
+        mediaHierarchyManager.qsExpansion = 1.0f
         verify(mediaCarouselController).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
                 any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
         val observer = wakefullnessObserver.value
         assertNotNull("lifecycle observer wasn't registered", observer)
         clearInvocations(mediaCarouselController)
-        mediaHiearchyManager.qsExpansion = 0.0f
+        mediaHierarchyManager.qsExpansion = 0.0f
         verify(mediaCarouselController).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
                 any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
     }
@@ -170,7 +175,7 @@
         goToLockscreen()
 
         // Let's transition all the way to full shade
-        mediaHiearchyManager.setTransitionToFullShadeAmount(100000f)
+        mediaHierarchyManager.setTransitionToFullShadeAmount(100000f)
         verify(mediaCarouselController).onDesiredLocationChanged(
             eq(MediaHierarchyManager.LOCATION_QQS),
             any(MediaHostState::class.java),
@@ -180,7 +185,7 @@
         clearInvocations(mediaCarouselController)
 
         // Let's go back to the lock screen
-        mediaHiearchyManager.setTransitionToFullShadeAmount(0.0f)
+        mediaHierarchyManager.setTransitionToFullShadeAmount(0.0f)
         verify(mediaCarouselController).onDesiredLocationChanged(
             eq(MediaHierarchyManager.LOCATION_LOCKSCREEN),
             any(MediaHostState::class.java),
@@ -189,7 +194,7 @@
             anyLong())
 
         // Let's make sure alpha is set
-        mediaHiearchyManager.setTransitionToFullShadeAmount(2.0f)
+        mediaHierarchyManager.setTransitionToFullShadeAmount(2.0f)
         assertThat(mediaFrame.alpha).isNotEqualTo(1.0f)
     }
 
@@ -198,7 +203,7 @@
         goToLockscreen()
         expandQS()
 
-        val transformType = mediaHiearchyManager.calculateTransformationType()
+        val transformType = mediaHierarchyManager.calculateTransformationType()
         assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
     }
 
@@ -206,7 +211,7 @@
     fun calculateTransformationType_notOnLockscreen_returnsTransition() {
         expandQS()
 
-        val transformType = mediaHiearchyManager.calculateTransformationType()
+        val transformType = mediaHierarchyManager.calculateTransformationType()
 
         assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_TRANSITION)
     }
@@ -216,7 +221,7 @@
         goToLockscreen()
         expandQS()
 
-        val transformType = mediaHiearchyManager.calculateTransformationType()
+        val transformType = mediaHierarchyManager.calculateTransformationType()
 
         assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
     }
@@ -226,9 +231,9 @@
         enableSplitShade()
         goToLockscreen()
         expandQS()
-        mediaHiearchyManager.setTransitionToFullShadeAmount(10000f)
+        mediaHierarchyManager.setTransitionToFullShadeAmount(10000f)
 
-        val transformType = mediaHiearchyManager.calculateTransformationType()
+        val transformType = mediaHierarchyManager.calculateTransformationType()
         assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_TRANSITION)
     }
 
@@ -238,9 +243,9 @@
         goToLockscreen()
         expandQS()
         whenever(lockHost.visible).thenReturn(false)
-        mediaHiearchyManager.setTransitionToFullShadeAmount(10000f)
+        mediaHierarchyManager.setTransitionToFullShadeAmount(10000f)
 
-        val transformType = mediaHiearchyManager.calculateTransformationType()
+        val transformType = mediaHierarchyManager.calculateTransformationType()
         assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
     }
 
@@ -250,9 +255,9 @@
         goToLockscreen()
         goToLockedShade()
         expandQS()
-        mediaHiearchyManager.setTransitionToFullShadeAmount(0f)
+        mediaHierarchyManager.setTransitionToFullShadeAmount(0f)
 
-        val transformType = mediaHiearchyManager.calculateTransformationType()
+        val transformType = mediaHierarchyManager.calculateTransformationType()
         assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
     }
 
@@ -261,13 +266,13 @@
         goToLockscreen()
         goToLockedShade()
 
-        val transformType = mediaHiearchyManager.calculateTransformationType()
+        val transformType = mediaHierarchyManager.calculateTransformationType()
         assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
     }
 
     @Test
     fun testCloseGutsRelayToCarousel() {
-        mediaHiearchyManager.closeGuts()
+        mediaHierarchyManager.closeGuts()
 
         verify(mediaCarouselController).closeGuts()
     }
@@ -281,7 +286,7 @@
 
     @Test
     fun getGuidedTransformationTranslationY_notInGuidedTransformation_returnsNegativeNumber() {
-        assertThat(mediaHiearchyManager.getGuidedTransformationTranslationY()).isLessThan(0)
+        assertThat(mediaHierarchyManager.getGuidedTransformationTranslationY()).isLessThan(0)
     }
 
     @Test
@@ -289,7 +294,7 @@
         enterGuidedTransformation()
 
         val expectedTranslation = LOCKSCREEN_TOP - QS_TOP
-        assertThat(mediaHiearchyManager.getGuidedTransformationTranslationY())
+        assertThat(mediaHierarchyManager.getGuidedTransformationTranslationY())
                 .isEqualTo(expectedTranslation)
     }
 
@@ -301,7 +306,7 @@
         whenever(qsHost.visible).thenReturn(true)
         whenever(qqsHost.visible).thenReturn(true)
 
-        assertThat(mediaHiearchyManager.isCurrentlyInGuidedTransformation()).isTrue()
+        assertThat(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).isTrue()
     }
 
     @Test
@@ -313,7 +318,7 @@
         whenever(qsHost.visible).thenReturn(true)
         whenever(qqsHost.visible).thenReturn(true)
 
-        assertThat(mediaHiearchyManager.isCurrentlyInGuidedTransformation()).isFalse()
+        assertThat(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).isFalse()
     }
 
     @Test
@@ -324,7 +329,7 @@
         whenever(qsHost.visible).thenReturn(true)
         whenever(qqsHost.visible).thenReturn(true)
 
-        assertThat(mediaHiearchyManager.isCurrentlyInGuidedTransformation()).isFalse()
+        assertThat(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).isFalse()
     }
 
     private fun enableSplitShade() {
@@ -336,9 +341,7 @@
 
     private fun goToLockscreen() {
         whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
-        whenever(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
-            true
-        )
+        settings.putInt(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, 1)
         statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
         clearInvocations(mediaCarouselController)
     }
@@ -352,13 +355,13 @@
     }
 
     private fun expandQS() {
-        mediaHiearchyManager.qsExpansion = 1.0f
+        mediaHierarchyManager.qsExpansion = 1.0f
     }
 
     private fun enterGuidedTransformation() {
-        mediaHiearchyManager.qsExpansion = 1.0f
+        mediaHierarchyManager.qsExpansion = 1.0f
         goToLockscreen()
-        mediaHiearchyManager.setTransitionToFullShadeAmount(123f)
+        mediaHierarchyManager.setTransitionToFullShadeAmount(123f)
     }
 
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
index 247316a..c101b9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.media.dream;
 
+import static com.android.systemui.flags.Flags.MEDIA_DREAM_COMPLICATION;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
@@ -28,6 +30,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.MediaData;
 import com.android.systemui.media.MediaDataManager;
 
@@ -50,6 +53,9 @@
     @Mock
     MediaDreamComplication mComplication;
 
+    @Mock
+    FeatureFlags mFeatureFlags;
+
     final String mKey = "key";
     final String mOldKey = "old_key";
 
@@ -59,21 +65,18 @@
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
+
+        when(mFeatureFlags.isEnabled(MEDIA_DREAM_COMPLICATION)).thenReturn(true);
     }
 
     @Test
     public void testComplicationAddition() {
         final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager,
-                mDreamOverlayStateController, mComplication);
+                mDreamOverlayStateController, mComplication, mFeatureFlags);
 
         sentinel.start();
 
-        ArgumentCaptor<MediaDataManager.Listener> listenerCaptor =
-                ArgumentCaptor.forClass(MediaDataManager.Listener.class);
-        verify(mMediaDataManager).addListener(listenerCaptor.capture());
-
-        final MediaDataManager.Listener listener = listenerCaptor.getValue();
-
+        final MediaDataManager.Listener listener = captureMediaDataListener();
         when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
         listener.onMediaDataLoaded(mKey, mOldKey, mData, /* immediately= */ true,
                 /* receivedSmartspaceCardLatency= */ 0, /* isSsReactived= */ false);
@@ -92,4 +95,27 @@
         verify(mDreamOverlayStateController).removeComplication(eq(mComplication));
     }
 
+    @Test
+    public void testMediaDreamSentinel_mediaComplicationDisabled_doNotAddComplication() {
+        when(mFeatureFlags.isEnabled(MEDIA_DREAM_COMPLICATION)).thenReturn(false);
+
+        final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager,
+                mDreamOverlayStateController, mComplication, mFeatureFlags);
+
+        sentinel.start();
+
+        final MediaDataManager.Listener listener = captureMediaDataListener();
+        when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
+        listener.onMediaDataLoaded(mKey, mOldKey, mData, /* immediately= */true,
+                /* receivedSmartspaceCardLatency= */0, /* isSsReactived= */ false);
+        verify(mDreamOverlayStateController, never()).addComplication(any());
+    }
+
+    private MediaDataManager.Listener captureMediaDataListener() {
+        final ArgumentCaptor<MediaDataManager.Listener> listenerCaptor =
+                ArgumentCaptor.forClass(MediaDataManager.Listener.class);
+        verify(mMediaDataManager).addListener(listenerCaptor.capture());
+
+        return listenerCaptor.getValue();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeImageCapture.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeImageCapture.kt
new file mode 100644
index 0000000..447e28c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeImageCapture.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenshot
+
+import android.graphics.Bitmap
+import android.graphics.Rect
+
+internal class FakeImageCapture : ImageCapture {
+
+    var requestedDisplayId: Int? = null
+    var requestedDisplayCrop: Rect? = null
+    var requestedTaskId: Int? = null
+
+    var image: Bitmap? = null
+
+    override fun captureDisplay(displayId: Int, crop: Rect?): Bitmap? {
+        requestedDisplayId = displayId
+        requestedDisplayCrop = crop
+        return image
+    }
+
+    override suspend fun captureTask(taskId: Int): Bitmap? {
+        requestedTaskId = taskId
+        return image
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScreenshotPolicy.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScreenshotPolicy.kt
new file mode 100644
index 0000000..28d53c7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScreenshotPolicy.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo
+
+internal class FakeScreenshotPolicy : ScreenshotPolicy {
+
+    private val userTypes = mutableMapOf<Int, Boolean>()
+    private val contentInfo = mutableMapOf<Int, DisplayContentInfo?>()
+
+    fun setManagedProfile(userId: Int, managedUser: Boolean) {
+        userTypes[userId] = managedUser
+    }
+    override suspend fun isManagedProfile(userId: Int): Boolean {
+        return userTypes[userId] ?: error("No managedProfile value set for userId $userId")
+    }
+
+    fun setDisplayContentInfo(userId: Int, contentInfo: DisplayContentInfo) {
+        this.contentInfo[userId] = contentInfo
+    }
+
+    override suspend fun findPrimaryContent(displayId: Int): DisplayContentInfo {
+        return contentInfo[displayId] ?: error("No DisplayContentInfo set for displayId $displayId")
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
index ce3f20d..00f3808 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
@@ -27,6 +27,8 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
 import org.junit.Test
 import org.junit.runner.RunWith
 
@@ -37,7 +39,10 @@
 class ImageCaptureImplTest : SysuiTestCase() {
     private val displayManager = mock<DisplayManager>()
     private val atmService = mock<IActivityTaskManager>()
-    private val capture = TestableImageCaptureImpl(displayManager, atmService)
+    private val capture = TestableImageCaptureImpl(
+        displayManager,
+        atmService,
+        Dispatchers.Unconfined)
 
     @Test
     fun captureDisplayWithCrop() {
@@ -59,9 +64,10 @@
 
     class TestableImageCaptureImpl(
         displayManager: DisplayManager,
-        atmService: IActivityTaskManager
+        atmService: IActivityTaskManager,
+        bgDispatcher: CoroutineDispatcher
     ) :
-        ImageCaptureImpl(displayManager, atmService) {
+        ImageCaptureImpl(displayManager, atmService, bgDispatcher) {
 
         var token: IBinder? = null
         var width: Int? = null
@@ -81,4 +87,4 @@
             return ScreenshotHardwareBuffer(null, null, false, false)
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
index 024d3bd..48fbd35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
@@ -22,86 +22,253 @@
 import android.graphics.Insets
 import android.graphics.Rect
 import android.hardware.HardwareBuffer
-import android.net.Uri
+import android.os.Bundle
 import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_CHORD
 import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER
 import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
-import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
 import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
-
+import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
 import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
+import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler.bundleToHardwareBitmap
 import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
-import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.screenshot.ScreenshotPolicy.DisplayContentInfo
 import com.google.common.truth.Truth.assertThat
-import java.util.function.Consumer
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
 import org.junit.Test
-import org.mockito.Mockito.eq
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.isNull
+
+private const val USER_ID = 1
+private const val TASK_ID = 1
 
 class RequestProcessorTest {
-    private val controller = mock<ScreenshotController>()
-    private val bitmapCaptor = argumentCaptor<Bitmap>()
+    private val imageCapture = FakeImageCapture()
+    private val component = ComponentName("android.test", "android.test.Component")
+    private val bounds = Rect(25, 25, 75, 75)
 
+    private val scope = CoroutineScope(Dispatchers.Unconfined)
+    private val dispatcher = Dispatchers.Unconfined
+    private val policy = FakeScreenshotPolicy()
+    private val flags = FakeFeatureFlags()
+
+    /** Tests the Java-compatible function wrapper, ensures callback is invoked. */
     @Test
-    fun testFullScreenshot() {
+    fun testProcessAsync() {
+        flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false)
+
         val request = ScreenshotRequest(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
-        val onSavedListener = mock<Consumer<Uri>>()
-        val callback = mock<RequestCallback>()
-        val processor = RequestProcessor(controller)
+        val processor = RequestProcessor(imageCapture, policy, flags, scope)
 
-        processor.processRequest(request, onSavedListener, callback)
+        var result: ScreenshotRequest? = null
+        var callbackCount = 0
+        val callback: (ScreenshotRequest) -> Unit = { processedRequest: ScreenshotRequest ->
+            result = processedRequest
+            callbackCount++
+        }
 
-        verify(controller).takeScreenshotFullscreen(/* topComponent */ isNull(),
-            eq(onSavedListener), eq(callback))
+        // runs synchronously, using Unconfined Dispatcher
+        processor.processAsync(request, callback)
+
+        // Callback invoked once returning the same request (no changes)
+        assertThat(callbackCount).isEqualTo(1)
+        assertThat(result).isEqualTo(request)
     }
 
     @Test
-    fun testSelectedRegionScreenshot() {
+    fun testFullScreenshot_workProfilePolicyDisabled() = runBlocking {
+        flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false)
+
+        val request = ScreenshotRequest(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
+        val processor = RequestProcessor(imageCapture, policy, flags, scope)
+
+        val processedRequest = processor.process(request)
+
+        // No changes
+        assertThat(processedRequest).isEqualTo(request)
+    }
+
+    @Test
+    fun testFullScreenshot() = runBlocking {
+        flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, true)
+
+        // Indicate that the primary content belongs to a normal user
+        policy.setManagedProfile(USER_ID, false)
+        policy.setDisplayContentInfo(
+            policy.getDefaultDisplayId(),
+            DisplayContentInfo(component, bounds, USER_ID, TASK_ID))
+
+        val request = ScreenshotRequest(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
+        val processor = RequestProcessor(imageCapture, policy, flags, scope)
+
+        val processedRequest = processor.process(request)
+
+        // Request has topComponent added, but otherwise unchanged.
+        assertThat(processedRequest.type).isEqualTo(TAKE_SCREENSHOT_FULLSCREEN)
+        assertThat(processedRequest.topComponent).isEqualTo(component)
+    }
+
+    @Test
+    fun testFullScreenshot_managedProfile() = runBlocking {
+        flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, true)
+
+        // Provide a fake task bitmap when asked
+        val bitmap = makeHardwareBitmap(100, 100)
+        imageCapture.image = bitmap
+
+        // Indicate that the primary content belongs to a manged profile
+        policy.setManagedProfile(USER_ID, true)
+        policy.setDisplayContentInfo(policy.getDefaultDisplayId(),
+            DisplayContentInfo(component, bounds, USER_ID, TASK_ID))
+
+        val request = ScreenshotRequest(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_CHORD)
+        val processor = RequestProcessor(imageCapture, policy, flags, scope)
+
+        val processedRequest = processor.process(request)
+
+        // Expect a task snapshot is taken, overriding the full screen mode
+        assertThat(processedRequest.type).isEqualTo(TAKE_SCREENSHOT_PROVIDED_IMAGE)
+        assertThat(bitmap.equalsHardwareBitmapBundle(processedRequest.bitmapBundle)).isTrue()
+        assertThat(processedRequest.boundsInScreen).isEqualTo(bounds)
+        assertThat(processedRequest.insets).isEqualTo(Insets.NONE)
+        assertThat(processedRequest.taskId).isEqualTo(TASK_ID)
+        assertThat(imageCapture.requestedTaskId).isEqualTo(TASK_ID)
+        assertThat(processedRequest.userId).isEqualTo(USER_ID)
+        assertThat(processedRequest.topComponent).isEqualTo(component)
+    }
+
+    @Test
+    fun testSelectedRegionScreenshot_workProfilePolicyDisabled() = runBlocking {
+        flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false)
+
         val request = ScreenshotRequest(TAKE_SCREENSHOT_SELECTED_REGION, SCREENSHOT_KEY_CHORD)
-        val onSavedListener = mock<Consumer<Uri>>()
-        val callback = mock<RequestCallback>()
-        val processor = RequestProcessor(controller)
+        val processor = RequestProcessor(imageCapture, policy, flags, scope)
 
-        processor.processRequest(request, onSavedListener, callback)
+        val processedRequest = processor.process(request)
 
-        verify(controller).takeScreenshotPartial(/* topComponent */ isNull(),
-            eq(onSavedListener), eq(callback))
+        // No changes
+        assertThat(processedRequest).isEqualTo(request)
+     }
+
+    @Test
+    fun testSelectedRegionScreenshot() = runBlocking {
+        flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, true)
+
+        val request = ScreenshotRequest(TAKE_SCREENSHOT_SELECTED_REGION, SCREENSHOT_KEY_CHORD)
+        val processor = RequestProcessor(imageCapture, policy, flags, scope)
+
+        policy.setManagedProfile(USER_ID, false)
+        policy.setDisplayContentInfo(policy.getDefaultDisplayId(),
+            DisplayContentInfo(component, bounds, USER_ID, TASK_ID))
+
+        val processedRequest = processor.process(request)
+
+        // Request has topComponent added, but otherwise unchanged.
+        assertThat(processedRequest.type).isEqualTo(TAKE_SCREENSHOT_FULLSCREEN)
+        assertThat(processedRequest.topComponent).isEqualTo(component)
     }
 
     @Test
-    fun testProvidedImageScreenshot() {
-        val taskId = 1111
-        val userId = 2222
-        val bounds = Rect(50, 50, 150, 150)
-        val topComponent = ComponentName("test", "test")
-        val processor = RequestProcessor(controller)
+    fun testSelectedRegionScreenshot_managedProfile() = runBlocking {
+        flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, true)
 
-        val buffer = HardwareBuffer.create(100, 100, HardwareBuffer.RGBA_8888, 1,
-            HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)
-        val bitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!!
+        // Provide a fake task bitmap when asked
+        val bitmap = makeHardwareBitmap(100, 100)
+        imageCapture.image = bitmap
+
+        val request = ScreenshotRequest(TAKE_SCREENSHOT_SELECTED_REGION, SCREENSHOT_KEY_CHORD)
+        val processor = RequestProcessor(imageCapture, policy, flags, scope)
+
+        // Indicate that the primary content belongs to a manged profile
+        policy.setManagedProfile(USER_ID, true)
+        policy.setDisplayContentInfo(policy.getDefaultDisplayId(),
+            DisplayContentInfo(component, bounds, USER_ID, TASK_ID))
+
+        val processedRequest = processor.process(request)
+
+        // Expect a task snapshot is taken, overriding the selected region mode
+        assertThat(processedRequest.type).isEqualTo(TAKE_SCREENSHOT_PROVIDED_IMAGE)
+        assertThat(bitmap.equalsHardwareBitmapBundle(processedRequest.bitmapBundle)).isTrue()
+        assertThat(processedRequest.boundsInScreen).isEqualTo(bounds)
+        assertThat(processedRequest.insets).isEqualTo(Insets.NONE)
+        assertThat(processedRequest.taskId).isEqualTo(TASK_ID)
+        assertThat(imageCapture.requestedTaskId).isEqualTo(TASK_ID)
+        assertThat(processedRequest.userId).isEqualTo(USER_ID)
+        assertThat(processedRequest.topComponent).isEqualTo(component)
+    }
+
+    @Test
+    fun testProvidedImageScreenshot_workProfilePolicyDisabled() = runBlocking {
+        flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, false)
+
+        val bounds = Rect(50, 50, 150, 150)
+        val processor = RequestProcessor(imageCapture, policy, flags, scope)
+
+        val bitmap = makeHardwareBitmap(100, 100)
         val bitmapBundle = HardwareBitmapBundler.hardwareBitmapToBundle(bitmap)
 
         val request = ScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER,
-            bitmapBundle, bounds, Insets.NONE, taskId, userId, topComponent)
+            bitmapBundle, bounds, Insets.NONE, TASK_ID, USER_ID, component)
 
-        val onSavedListener = mock<Consumer<Uri>>()
-        val callback = mock<RequestCallback>()
+        val processedRequest = processor.process(request)
 
-        processor.processRequest(request, onSavedListener, callback)
-
-        verify(controller).handleImageAsScreenshot(
-            bitmapCaptor.capture(), eq(bounds), eq(Insets.NONE), eq(taskId), eq(userId),
-            eq(topComponent), eq(onSavedListener), eq(callback)
-        )
-
-        assertThat(bitmapCaptor.value.equalsHardwareBitmap(bitmap)).isTrue()
+        // No changes
+        assertThat(processedRequest).isEqualTo(request)
     }
 
-    private fun Bitmap.equalsHardwareBitmap(bitmap: Bitmap): Boolean {
-        return bitmap.hardwareBuffer == this.hardwareBuffer &&
-                bitmap.colorSpace == this.colorSpace
+    @Test
+    fun testProvidedImageScreenshot() = runBlocking {
+        flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, true)
+
+        val bounds = Rect(50, 50, 150, 150)
+        val processor = RequestProcessor(imageCapture, policy, flags, scope)
+
+        policy.setManagedProfile(USER_ID, false)
+
+        val bitmap = makeHardwareBitmap(100, 100)
+        val bitmapBundle = HardwareBitmapBundler.hardwareBitmapToBundle(bitmap)
+
+        val request = ScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER,
+            bitmapBundle, bounds, Insets.NONE, TASK_ID, USER_ID, component)
+
+        val processedRequest = processor.process(request)
+
+        // No changes
+        assertThat(processedRequest).isEqualTo(request)
+    }
+
+    @Test
+    fun testProvidedImageScreenshot_managedProfile() = runBlocking {
+        flags.set(Flags.SCREENSHOT_WORK_PROFILE_POLICY, true)
+
+        val bounds = Rect(50, 50, 150, 150)
+        val processor = RequestProcessor(imageCapture, policy, flags, scope)
+
+        // Indicate that the screenshot belongs to a manged profile
+        policy.setManagedProfile(USER_ID, true)
+
+        val bitmap = makeHardwareBitmap(100, 100)
+        val bitmapBundle = HardwareBitmapBundler.hardwareBitmapToBundle(bitmap)
+
+        val request = ScreenshotRequest(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OTHER,
+            bitmapBundle, bounds, Insets.NONE, TASK_ID, USER_ID, component)
+
+        val processedRequest = processor.process(request)
+
+        // Work profile, but already a task snapshot, so no changes
+        assertThat(processedRequest).isEqualTo(request)
+    }
+
+    private fun makeHardwareBitmap(width: Int, height: Int): Bitmap {
+        val buffer = HardwareBuffer.create(width, height, HardwareBuffer.RGBA_8888, 1,
+            HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)
+        return Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!!
+    }
+
+    private fun Bitmap.equalsHardwareBitmapBundle(bundle: Bundle): Boolean {
+        val provided = bundleToHardwareBitmap(bundle)
+        return provided.hardwareBuffer == this.hardwareBuffer &&
+                provided.colorSpace == this.colorSpace
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
index 64d0256..214ba16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
@@ -98,7 +98,7 @@
 
     @Test
     fun testPrepareDialogForApp_onlyDefaultChannel() {
-        group.addChannel(channelDefault)
+        group.channels = listOf(channelDefault)
 
         controller.prepareDialogForApp(TEST_APP_NAME, TEST_PACKAGE_NAME, TEST_UID,
                 setOf(channelDefault), appIcon, clickListener)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index c25737d..cc4cbbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -48,6 +48,7 @@
 import android.widget.RemoteViews;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.TestableDependency;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.classifier.FalsingManagerFake;
@@ -75,6 +76,7 @@
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
 import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
 import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
@@ -145,7 +147,9 @@
                 mock(GroupMembershipManager.class),
                 mock(VisualStabilityProvider.class),
                 mock(ConfigurationControllerImpl.class),
-                new Handler(mTestLooper.getLooper())
+                new Handler(mTestLooper.getLooper()),
+                mock(AccessibilityManagerWrapper.class),
+                mock(UiEventLogger.class)
         );
         mIconManager = new IconManager(
                 mock(CommonNotifCollection.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index ac3d0c2..e252401 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -29,6 +29,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.AlertingNotificationManagerTest;
@@ -65,6 +66,8 @@
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private KeyguardBypassController mBypassController;
     @Mock private ConfigurationControllerImpl mConfigurationController;
+    @Mock private AccessibilityManagerWrapper mAccessibilityManagerWrapper;
+    @Mock private UiEventLogger mUiEventLogger;
     private boolean mLivesPastNormalTime;
 
     private static final class TestableHeadsUpManagerPhone extends HeadsUpManagerPhone {
@@ -76,7 +79,9 @@
                 StatusBarStateController statusBarStateController,
                 KeyguardBypassController keyguardBypassController,
                 ConfigurationController configurationController,
-                Handler handler
+                Handler handler,
+                AccessibilityManagerWrapper accessibilityManagerWrapper,
+                UiEventLogger uiEventLogger
         ) {
             super(
                     context,
@@ -86,7 +91,9 @@
                     groupManager,
                     visualStabilityProvider,
                     configurationController,
-                    handler
+                    handler,
+                    accessibilityManagerWrapper,
+                    uiEventLogger
             );
             mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
             mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
@@ -116,7 +123,9 @@
                 mStatusBarStateController,
                 mBypassController,
                 mConfigurationController,
-                mTestHandler
+                mTestHandler,
+                mAccessibilityManagerWrapper,
+                mUiEventLogger
         );
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index e2b9a9e..9764b8c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -63,7 +63,6 @@
     private static final int TEST_A11Y_AUTO_DISMISS_TIME = 600;
     private static final int TEST_A11Y_TIMEOUT_TIME = 5_000;
 
-    private AccessibilityManagerWrapper mAccessibilityMgr;
     private HeadsUpManager mHeadsUpManager;
     private boolean mLivesPastNormalTime;
     private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
@@ -72,10 +71,15 @@
     @Mock private StatusBarNotification mSbn;
     @Mock private Notification mNotification;
     @Mock private HeadsUpManagerLogger mLogger;
+    @Mock private AccessibilityManagerWrapper mAccessibilityMgr;
 
     private final class TestableHeadsUpManager extends HeadsUpManager {
-        TestableHeadsUpManager(Context context, HeadsUpManagerLogger logger, Handler handler) {
-            super(context, logger, handler);
+        TestableHeadsUpManager(Context context,
+                HeadsUpManagerLogger logger,
+                Handler handler,
+                AccessibilityManagerWrapper accessibilityManagerWrapper,
+                UiEventLogger uiEventLogger) {
+            super(context, logger, handler, accessibilityManagerWrapper, uiEventLogger);
             mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
             mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
         }
@@ -88,13 +92,11 @@
     @Before
     public void setUp() {
         initMocks(this);
-        mAccessibilityMgr = mDependency.injectMockDependency(AccessibilityManagerWrapper.class);
-        mDependency.injectTestDependency(UiEventLogger.class, mUiEventLoggerFake);
         when(mEntry.getSbn()).thenReturn(mSbn);
         when(mSbn.getNotification()).thenReturn(mNotification);
-
         super.setUp();
-        mHeadsUpManager = new TestableHeadsUpManager(mContext, mLogger, mTestHandler);
+        mHeadsUpManager = new TestableHeadsUpManager(mContext, mLogger, mTestHandler,
+                mAccessibilityMgr, mUiEventLoggerFake);
     }
 
     @After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index b7f38f1..50259b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -310,7 +310,7 @@
     }
 
     @Test
-    public void onWallpaperColorsChanged_ResetThemeWithNewHomeWallpapers() {
+    public void onWallpaperColorsChanged_resetThemeWithNewHomeWallpapers() {
         // Should ask for a new theme when wallpaper colors change
         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
                 Color.valueOf(Color.BLUE), null);
@@ -345,6 +345,61 @@
     }
 
     @Test
+    public void onWallpaperColorsChanged_keepsThemeWhenSetFromLockScreen() {
+        // Should ask for a new theme when wallpaper colors change
+        WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
+                Color.valueOf(Color.BLUE), null);
+        String jsonString =
+                "{\"android.theme.customization.color_source\":\"lock_wallpaper\","
+                        + "\"android.theme.customization.system_palette\":\"A16B00\","
+                        + "\"android.theme.customization.accent_color\":\"A16B00\","
+                        + "\"android.theme.customization.color_index\":\"2\"}";
+        when(mSecureSettings.getStringForUser(
+                eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+                .thenReturn(jsonString);
+        when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
+                .thenReturn(20);
+        when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, USER_SYSTEM))
+                .thenReturn(21);
+        mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM,
+                USER_SYSTEM);
+        verify(mSecureSettings, never()).putStringForUser(
+                eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), any(), anyInt());
+    }
+
+    @Test
+    public void onWallpaperColorsChanged_resetLockScreenThemeWhenBothSet() {
+        // Should ask for a new theme when wallpaper colors change
+        WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
+                Color.valueOf(Color.BLUE), null);
+        String jsonString =
+                "{\"android.theme.customization.color_source\":\"lock_wallpaper\","
+                        + "\"android.theme.customization.system_palette\":\"A16B00\","
+                        + "\"android.theme.customization.accent_color\":\"A16B00\","
+                        + "\"android.theme.customization.color_index\":\"2\"}";
+        when(mSecureSettings.getStringForUser(
+                eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), anyInt()))
+                .thenReturn(jsonString);
+        when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_LOCK, USER_SYSTEM))
+                .thenReturn(20);
+        when(mWallpaperManager.getWallpaperIdForUser(WallpaperManager.FLAG_SYSTEM, USER_SYSTEM))
+                .thenReturn(21);
+
+        mColorsListener.getValue().onColorsChanged(mainColors,
+                WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK,
+                USER_SYSTEM);
+
+        ArgumentCaptor<String> updatedSetting = ArgumentCaptor.forClass(String.class);
+        verify(mSecureSettings).putStringForUser(
+                eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture(),
+                anyInt());
+        assertThat(updatedSetting.getValue().contains(
+                "android.theme.customization.color_both\":\"1")).isTrue();
+        verify(mThemeOverlayApplier)
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+    }
+
+    @Test
     public void onSettingChanged_honorThemeStyle() {
         when(mDeviceProvisionedController.isUserSetup(anyInt())).thenReturn(true);
         List<Style> validStyles = Arrays.asList(Style.EXPRESSIVE, Style.SPRITZ, Style.TONAL_SPOT,
@@ -381,7 +436,7 @@
     }
 
     @Test
-    public void onWallpaperColorsChanged_ResetThemeWithNewHomeAndLockWallpaper() {
+    public void onWallpaperColorsChanged_resetThemeWithNewHomeAndLockWallpaper() {
         // Should ask for a new theme when wallpaper colors change
         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
                 Color.valueOf(Color.BLUE), null);
@@ -450,7 +505,7 @@
         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
                 Color.valueOf(Color.BLUE), null);
         String jsonString =
-                "{\"android.theme.customization.color_source\":\"lock_wallpaper\","
+                "{\"android.theme.customization.color_source\":\"home_wallpaper\","
                         + "\"android.theme.customization.system_palette\":\"A16B00\","
                         + "\"android.theme.customization.accent_color\":\"A16B00\","
                         + "\"android.theme.customization.color_index\":\"2\"}";
@@ -476,7 +531,7 @@
     }
 
     @Test
-    public void onWallpaperColorsChanged_ResetThemeWhenFromLatestWallpaper() {
+    public void onWallpaperColorsChanged_resetThemeWhenFromLatestWallpaper() {
         // Should ask for a new theme when the colors of the last applied wallpaper change
         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
                 Color.valueOf(Color.BLUE), null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 18acf3f..8f2b715 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -63,6 +63,7 @@
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.os.Handler;
 import android.os.PowerManager;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.service.dreams.IDreamManager;
@@ -204,6 +205,8 @@
     private ArgumentCaptor<IntentFilter> mFilterArgumentCaptor;
     @Captor
     private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor;
+    @Captor
+    private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallbackCaptor;
 
     private BubblesManager mBubblesManager;
     private TestableBubbleController mBubbleController;
@@ -240,6 +243,8 @@
     @Mock
     private IStatusBarService mStatusBarService;
     @Mock
+    private IDreamManager mIDreamManager;
+    @Mock
     private NotificationVisibilityProvider mVisibilityProvider;
     @Mock
     private LauncherApps mLauncherApps;
@@ -371,10 +376,11 @@
                 mContext,
                 mBubbleController.asBubbles(),
                 mNotificationShadeWindowController,
-                mock(KeyguardStateController.class),
+                mKeyguardStateController,
                 mShadeController,
                 mStatusBarService,
                 mock(INotificationManager.class),
+                mIDreamManager,
                 mVisibilityProvider,
                 interruptionStateProvider,
                 mZenModeController,
@@ -391,6 +397,25 @@
         verify(mNotifPipeline, atLeastOnce())
                 .addCollectionListener(mNotifListenerCaptor.capture());
         mEntryListener = mNotifListenerCaptor.getValue();
+
+        // Get a reference to KeyguardStateController.Callback
+        verify(mKeyguardStateController, atLeastOnce())
+                .addCallback(mKeyguardStateControllerCallbackCaptor.capture());
+    }
+
+    @Test
+    public void dreamingHidesBubbles() throws RemoteException {
+        mBubbleController.updateBubble(mBubbleEntry);
+        assertTrue(mBubbleController.hasBubbles());
+        assertThat(mBubbleController.getStackView().getVisibility()).isEqualTo(View.VISIBLE);
+
+        when(mIDreamManager.isDreamingOrInPreview()).thenReturn(true); // dreaming is happening
+        when(mKeyguardStateController.isShowing()).thenReturn(false); // device is unlocked
+        KeyguardStateController.Callback callback =
+                mKeyguardStateControllerCallbackCaptor.getValue();
+        callback.onKeyguardShowingChanged();
+
+        assertThat(mBubbleController.getStackView().getVisibility()).isEqualTo(View.INVISIBLE);
     }
 
     @Test
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c8519c1..297e6a2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18414,11 +18414,11 @@
                         attributionSource.getUid()), Process.SHELL_UID);
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    return superImpl.apply(code, new AttributionSource(shellUid, /*pid*/ -1,
+                    return superImpl.apply(code, new AttributionSource(shellUid,
                             "com.android.shell", attributionSource.getAttributionTag(),
-                            attributionSource.getToken(), /*renouncedPermissions*/ null,
-                            attributionSource.getNext()), shouldCollectAsyncNotedOp, message,
-                            shouldCollectMessage, skiProxyOperation);
+                            attributionSource.getToken(), attributionSource.getNext()),
+                            shouldCollectAsyncNotedOp, message, shouldCollectMessage,
+                            skiProxyOperation);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
@@ -18465,13 +18465,12 @@
                         attributionSource.getUid()), Process.SHELL_UID);
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    return superImpl.apply(code, new AttributionSource(shellUid, /*pid*/ -1,
+                    return superImpl.apply(code, new AttributionSource(shellUid,
                             "com.android.shell", attributionSource.getAttributionTag(),
-                            attributionSource.getToken(), /*renouncedPermissions*/ null,
-                            attributionSource.getNext()), startIfModeDefault,
-                            shouldCollectAsyncNotedOp, message, shouldCollectMessage,
-                            skipProxyOperation, proxyAttributionFlags, proxiedAttributionFlags,
-                            attributionChainId);
+                            attributionSource.getToken(), attributionSource.getNext()),
+                            startIfModeDefault, shouldCollectAsyncNotedOp, message,
+                            shouldCollectMessage, skipProxyOperation, proxyAttributionFlags,
+                            proxiedAttributionFlags, attributionChainId);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
@@ -18490,10 +18489,10 @@
                         attributionSource.getUid()), Process.SHELL_UID);
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    superImpl.apply(code, new AttributionSource(shellUid, /*pid*/ -1,
+                    superImpl.apply(code, new AttributionSource(shellUid,
                             "com.android.shell", attributionSource.getAttributionTag(),
-                            attributionSource.getToken(), /*renouncedPermissions*/ null,
-                            attributionSource.getNext()), skipProxyOperation);
+                            attributionSource.getToken(), attributionSource.getNext()),
+                            skipProxyOperation);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b97aa5f..aa2a25b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6684,6 +6684,20 @@
             }
         }
 
+        // Ensure only allowed packages have a substitute app name
+        if (notification.extras.containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME)) {
+            int hasSubstituteAppNamePermission = mPackageManager.checkPermission(
+                    permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg, userId);
+            if (hasSubstituteAppNamePermission != PERMISSION_GRANTED) {
+                notification.extras.remove(Notification.EXTRA_SUBSTITUTE_APP_NAME);
+                if (DBG) {
+                    Slog.w(TAG, "warning: pkg " + pkg + " attempting to substitute app name"
+                            + " without holding perm "
+                            + Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME);
+                }
+            }
+        }
+
         // Remote views? Are they too big?
         checkRemoteViews(pkg, tag, id, notification);
     }
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 477b8da..729c521 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -86,6 +86,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -1327,16 +1328,17 @@
                 return null;
             }
             NotificationChannelGroup group = r.groups.get(groupId).clone();
-            group.setChannels(new ArrayList<>());
+            ArrayList channels = new ArrayList();
             int N = r.channels.size();
             for (int i = 0; i < N; i++) {
                 final NotificationChannel nc = r.channels.valueAt(i);
                 if (includeDeleted || !nc.isDeleted()) {
                     if (groupId.equals(nc.getGroup())) {
-                        group.addChannel(nc);
+                        channels.add(nc);
                     }
                 }
             }
+            group.setChannels(channels);
             return group;
         }
     }
@@ -1349,7 +1351,10 @@
             if (r == null) {
                 return null;
             }
-            return r.groups.get(groupId);
+            if (r.groups.get(groupId) != null) {
+                 return r.groups.get(groupId).clone();
+            }
+            return null;
         }
     }
 
@@ -1357,44 +1362,48 @@
     public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
             int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
         Objects.requireNonNull(pkg);
-        Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
+        List<NotificationChannelGroup> groups = new ArrayList<>();
         synchronized (mPackagePreferences) {
             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
             if (r == null) {
                 return ParceledListSlice.emptyList();
             }
-            NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
+            Map<String, ArrayList<NotificationChannel>> groupedChannels = new HashMap();
             int N = r.channels.size();
             for (int i = 0; i < N; i++) {
                 final NotificationChannel nc = r.channels.valueAt(i);
                 if (includeDeleted || !nc.isDeleted()) {
                     if (nc.getGroup() != null) {
                         if (r.groups.get(nc.getGroup()) != null) {
-                            NotificationChannelGroup ncg = groups.get(nc.getGroup());
-                            if (ncg == null) {
-                                ncg = r.groups.get(nc.getGroup()).clone();
-                                ncg.setChannels(new ArrayList<>());
-                                groups.put(nc.getGroup(), ncg);
-
-                            }
-                            ncg.addChannel(nc);
+                            ArrayList<NotificationChannel> channels = groupedChannels.getOrDefault(
+                                    nc.getGroup(), new ArrayList<>());
+                            channels.add(nc);
+                            groupedChannels.put(nc.getGroup(), channels);
                         }
                     } else {
-                        nonGrouped.addChannel(nc);
+                        ArrayList<NotificationChannel> channels = groupedChannels.getOrDefault(
+                            null, new ArrayList<>());
+                        channels.add(nc);
+                        groupedChannels.put(null, channels);
                     }
                 }
             }
-            if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
-                groups.put(null, nonGrouped);
-            }
-            if (includeEmpty) {
-                for (NotificationChannelGroup group : r.groups.values()) {
-                    if (!groups.containsKey(group.getId())) {
-                        groups.put(group.getId(), group);
-                    }
+            for (NotificationChannelGroup group : r.groups.values()) {
+                ArrayList<NotificationChannel> channels =
+                        groupedChannels.getOrDefault(group.getId(), new ArrayList<>());
+                if (includeEmpty || !channels.isEmpty()) {
+                    NotificationChannelGroup clone = group.clone();
+                    clone.setChannels(channels);
+                    groups.add(clone);
                 }
             }
-            return new ParceledListSlice<>(new ArrayList<>(groups.values()));
+
+            if (includeNonGrouped && groupedChannels.containsKey(null)) {
+                NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
+                nonGrouped.setChannels(groupedChannels.get(null));
+                groups.add(nonGrouped);
+            }
+            return new ParceledListSlice<>(groups);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java
index a878bfd..b3c7cc0 100644
--- a/services/core/java/com/android/server/pm/Computer.java
+++ b/services/core/java/com/android/server/pm/Computer.java
@@ -500,6 +500,13 @@
     boolean isComponentEffectivelyEnabled(@NonNull ComponentInfo componentInfo,
             @UserIdInt int userId);
 
+    /**
+     * @return true if the runtime app user enabled state and the install-time app manifest enabled
+     * state are both effectively enabled for the given app. Or if the app cannot be found,
+     * returns false.
+     */
+    boolean isApplicationEffectivelyEnabled(@NonNull String packageName, @UserIdInt int userId);
+
     @Nullable
     KeySet getKeySetByAlias(@NonNull String packageName, @NonNull String alias);
 
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 8ec3d2b..7dc648c 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -5412,6 +5412,26 @@
         }
     }
 
+    @Override
+    public boolean isApplicationEffectivelyEnabled(@NonNull String packageName,
+            @UserIdInt int userId) {
+        try {
+            int appEnabledSetting = mSettings.getApplicationEnabledSetting(packageName, userId);
+            if (appEnabledSetting == COMPONENT_ENABLED_STATE_DEFAULT) {
+                final AndroidPackage pkg = getPackage(packageName);
+                if (pkg == null) {
+                    // Should not happen because getApplicationEnabledSetting would have thrown
+                    return false;
+                }
+                return pkg.isEnabled();
+            } else {
+                return appEnabledSetting == COMPONENT_ENABLED_STATE_ENABLED;
+            }
+        } catch (PackageManager.NameNotFoundException ignored) {
+            return false;
+        }
+    }
+
     @Nullable
     @Override
     public KeySet getKeySetByAlias(@NonNull String packageName, @NonNull String alias) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 025e973..87ab87d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -18,6 +18,7 @@
 
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.os.UserManager.DISALLOW_USER_SWITCH;
 
 import android.Manifest;
 import android.accounts.Account;
@@ -1885,6 +1886,19 @@
     }
 
     @Override
+    public boolean isUserSwitcherEnabled(@UserIdInt int mUserId) {
+        boolean multiUserSettingOn = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.USER_SWITCHER_ENABLED,
+                Resources.getSystem().getBoolean(com.android.internal
+                        .R.bool.config_showUserSwitcherByDefault) ? 1 : 0) != 0;
+
+        return UserManager.supportsMultipleUsers()
+                && !hasUserRestriction(DISALLOW_USER_SWITCH, mUserId)
+                && !UserManager.isDeviceInDemoMode(mContext)
+                && multiUserSettingOn;
+    }
+
+    @Override
     public boolean isRestricted(@UserIdInt int userId) {
         if (userId != UserHandle.getCallingUserId()) {
             checkCreateUsersPermission("query isRestricted for user " + userId);
@@ -2277,7 +2291,6 @@
                     originatingUserId, local);
             localChanged = updateLocalRestrictionsForTargetUsersLR(originatingUserId, local,
                     updatedLocalTargetUserIds);
-
             if (isDeviceOwner) {
                 // Remember the global restriction owner userId to be able to make a distinction
                 // in getUserRestrictionSource on who set local policies.
@@ -4804,41 +4817,59 @@
                 null, // use default PullAtomMetadata values
                 BackgroundThread.getExecutor(),
                 this::onPullAtom);
+        statsManager.setPullAtomCallback(
+                FrameworkStatsLog.MULTI_USER_INFO,
+                null, // use default PullAtomMetadata values
+                BackgroundThread.getExecutor(),
+                this::onPullAtom);
     }
 
     /** Writes a UserInfo pulled atom for each user on the device. */
     private int onPullAtom(int atomTag, List<StatsEvent> data) {
-        if (atomTag != FrameworkStatsLog.USER_INFO) {
-            Slogf.e(LOG_TAG, "Unexpected atom tag: %d", atomTag);
-            return android.app.StatsManager.PULL_SKIP;
-        }
-        final List<UserInfo> users = getUsersInternal(true, true, true);
-        final int size = users.size();
-        if (size > 1) {
-            for (int idx = 0; idx < size; idx++) {
-                final UserInfo user = users.get(idx);
-                final int userTypeStandard = UserManager.getUserTypeForStatsd(user.userType);
-                final String userTypeCustom = (userTypeStandard == FrameworkStatsLog
-                        .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN)
-                        ?
-                        user.userType : null;
+        if (atomTag == FrameworkStatsLog.USER_INFO) {
+            final List<UserInfo> users = getUsersInternal(true, true, true);
+            final int size = users.size();
+            if (size > 1) {
+                for (int idx = 0; idx < size; idx++) {
+                    final UserInfo user = users.get(idx);
+                    final int userTypeStandard = UserManager.getUserTypeForStatsd(user.userType);
+                    final String userTypeCustom = (userTypeStandard == FrameworkStatsLog
+                            .USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN)
+                            ?
+                            user.userType : null;
 
-                boolean isUserRunningUnlocked;
-                synchronized (mUserStates) {
-                    isUserRunningUnlocked =
-                            mUserStates.get(user.id, -1) == UserState.STATE_RUNNING_UNLOCKED;
+                    boolean isUserRunningUnlocked;
+                    synchronized (mUserStates) {
+                        isUserRunningUnlocked =
+                                mUserStates.get(user.id, -1) == UserState.STATE_RUNNING_UNLOCKED;
+                    }
+
+                    data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.USER_INFO,
+                            user.id,
+                            userTypeStandard,
+                            userTypeCustom,
+                            user.flags,
+                            user.creationTime,
+                            user.lastLoggedInTime,
+                            isUserRunningUnlocked
+                    ));
+                }
+            }
+        } else if (atomTag == FrameworkStatsLog.MULTI_USER_INFO) {
+            if (UserManager.getMaxSupportedUsers() > 1) {
+                int deviceOwnerUserId = UserHandle.USER_NULL;
+
+                synchronized (mRestrictionsLock) {
+                    deviceOwnerUserId = mDeviceOwnerUserId;
                 }
 
-                data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.USER_INFO,
-                        user.id,
-                        userTypeStandard,
-                        userTypeCustom,
-                        user.flags,
-                        user.creationTime,
-                        user.lastLoggedInTime,
-                        isUserRunningUnlocked
-                ));
+                data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.MULTI_USER_INFO,
+                        UserManager.getMaxSupportedUsers(),
+                        isUserSwitcherEnabled(deviceOwnerUserId)));
             }
+        } else {
+            Slogf.e(LOG_TAG, "Unexpected atom tag: %d", atomTag);
+            return android.app.StatsManager.PULL_SKIP;
         }
         return android.app.StatsManager.PULL_SUCCESS;
     }
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index 0a39e64..47a3705 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -25,6 +25,7 @@
 import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
 import static android.os.PowerWhitelistManager.REASON_PACKAGE_VERIFIER;
 import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 
 import static com.android.server.pm.PackageManagerService.CHECK_PENDING_INTEGRITY_VERIFICATION;
@@ -77,6 +78,7 @@
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 final class VerifyingSession {
@@ -353,7 +355,7 @@
         }
         final int verifierUserId = verifierUser.getIdentifier();
 
-        String[] requiredVerifierPackages = mPm.mRequiredVerifierPackages;
+        List<String> requiredVerifierPackages = Arrays.asList(mPm.mRequiredVerifierPackages);
         boolean requiredVerifierPackagesOverridden = false;
 
         // Allow verifier override for ADB installations which could already be unverified using
@@ -377,8 +379,7 @@
                     // are not adding a new way to disable verifications.
                     if (!isAdbVerificationEnabled(pkgLite, verifierUserId,
                             requestedDisableVerification)) {
-                        requiredVerifierPackages = adbVerifierOverridePackages.toArray(
-                                new String[adbVerifierOverridePackages.size()]);
+                        requiredVerifierPackages = adbVerifierOverridePackages;
                         requiredVerifierPackagesOverridden = true;
                     }
                 }
@@ -397,6 +398,16 @@
          */
         final Computer snapshot = mPm.snapshotComputer();
 
+        final int numRequiredVerifierPackages = requiredVerifierPackages.size();
+        for (int i = numRequiredVerifierPackages - 1; i >= 0; i--) {
+            if (!snapshot.isApplicationEffectivelyEnabled(requiredVerifierPackages.get(i),
+                    SYSTEM_UID)) {
+                Slog.w(TAG,
+                        "Required verifier: " + requiredVerifierPackages.get(i) + " is disabled");
+                requiredVerifierPackages.remove(i);
+            }
+        }
+
         for (String requiredVerifierPackage : requiredVerifierPackages) {
             final int requiredUid = snapshot.getPackageUid(requiredVerifierPackage,
                     MATCH_DEBUG_TRIAGED_MISSING, verifierUserId);
@@ -514,7 +525,7 @@
             }
         }
 
-        if (requiredVerifierPackages.length == 0) {
+        if (requiredVerifierPackages.size() == 0) {
             Slog.e(TAG, "No required verifiers");
             return;
         }
@@ -532,7 +543,7 @@
 
             final Intent requiredIntent;
             final String receiverPermission;
-            if (!requiredVerifierPackagesOverridden || requiredVerifierPackages.length == 1) {
+            if (!requiredVerifierPackagesOverridden || requiredVerifierPackages.size() == 1) {
                 // Prod code OR test code+single verifier.
                 requiredIntent = new Intent(verification);
                 if (!requiredVerifierPackagesOverridden) {
@@ -657,7 +668,7 @@
      * @return true if verification should be performed
      */
     private boolean isVerificationEnabled(PackageInfoLite pkgInfoLite, int userId,
-            String[] requiredVerifierPackages) {
+            List<String> requiredVerifierPackages) {
         if (!DEFAULT_VERIFY_ENABLE) {
             return false;
         }
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 9c95769..7602d33 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -354,7 +354,7 @@
      * Get op that controls the access related to the permission.
      *
      * <p>Usually the permission-op relationship is 1:1 but some permissions (e.g. fine location)
-     * {@link AppOpsManager#sOpToSwitch share an op} to control the access.
+     * {@link AppOpsManager#opToSwitch(int)}  share an op} to control the access.
      *
      * @param permission The permission
      * @return The op that controls the access of the permission
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 51bf557..7dcfd8b 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2082,21 +2082,22 @@
 
         mWindowManagerInternal.registerAppTransitionListener(new AppTransitionListener() {
             @Override
-            public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
+            public int onAppTransitionStartingLocked(boolean keyguardGoingAway,
+                    boolean keyguardOccluding, long duration, long statusBarAnimationStartTime,
                     long statusBarAnimationDuration) {
-                return handleTransitionForKeyguardLw(false /* startKeyguardExitAnimation */,
-                        false /* notifyOccluded */);
+                // When remote animation is enabled for KEYGUARD_GOING_AWAY transition, SysUI
+                // receives IRemoteAnimationRunner#onAnimationStart to start animation, so we don't
+                // need to call IKeyguardService#keyguardGoingAway here.
+                return handleStartTransitionForKeyguardLw(keyguardGoingAway
+                        && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation,
+                        keyguardOccluding, duration);
             }
 
             @Override
-            public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled,
-                    boolean keyguardOccludedCancelled) {
-                // When app KEYGUARD_GOING_AWAY or (UN)OCCLUDE app transition is canceled, we need
-                // to trigger relevant IKeyguardService calls to sync keyguard status in
-                // WindowManagerService and SysUI.
-                handleTransitionForKeyguardLw(
-                        keyguardGoingAwayCancelled /* startKeyguardExitAnimation */,
-                        keyguardOccludedCancelled /* notifyOccluded */);
+            public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
+                handleStartTransitionForKeyguardLw(
+                        keyguardGoingAway, false /* keyguardOccludingStarted */,
+                        0 /* duration */);
             }
         });
 
@@ -3262,39 +3263,31 @@
             mPendingKeyguardOccluded = occluded;
             mKeyguardOccludedChanged = true;
         } else {
-            setKeyguardOccludedLw(occluded, true /* notify */);
+            setKeyguardOccludedLw(occluded, false /* force */,
+                    false /* transitionStarted */);
         }
     }
 
     @Override
-    public int applyKeyguardOcclusionChange(boolean notify) {
+    public int applyKeyguardOcclusionChange(boolean transitionStarted) {
         if (mKeyguardOccludedChanged) {
             if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded changed occluded="
                     + mPendingKeyguardOccluded);
-            if (setKeyguardOccludedLw(mPendingKeyguardOccluded, notify)) {
+            if (setKeyguardOccludedLw(mPendingKeyguardOccluded, false /* force */,
+                    transitionStarted)) {
                 return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER;
             }
         }
         return 0;
     }
 
-    /**
-     * Called when keyguard related app transition starts, or cancelled.
-     *
-     * @param startKeyguardExitAnimation Trigger IKeyguardService#startKeyguardExitAnimation to
-     *                                  start keyguard exit animation.
-     * @param notifyOccluded Trigger IKeyguardService#setOccluded binder call to notify whether
-     *                      the top activity can occlude the keyguard or not.
-     *
-     * @return Whether the flags have changed and we have to redo the layout.
-     */
-    private int handleTransitionForKeyguardLw(boolean startKeyguardExitAnimation,
-            boolean notifyOccluded) {
-        final int redoLayout = applyKeyguardOcclusionChange(notifyOccluded);
+    private int handleStartTransitionForKeyguardLw(boolean keyguardGoingAway,
+            boolean keyguardOccluding, long duration) {
+        final int redoLayout = applyKeyguardOcclusionChange(keyguardOccluding);
         if (redoLayout != 0) return redoLayout;
-        if (startKeyguardExitAnimation) {
+        if (keyguardGoingAway) {
             if (DEBUG_KEYGUARD) Slog.d(TAG, "Starting keyguard exit animation");
-            startKeyguardExitAnimation(SystemClock.uptimeMillis());
+            startKeyguardExitAnimation(SystemClock.uptimeMillis(), duration);
         }
         return 0;
     }
@@ -3526,18 +3519,28 @@
      * Updates the occluded state of the Keyguard.
      *
      * @param isOccluded Whether the Keyguard is occluded by another window.
-     * @param notify Notify keyguard occlude status change immediately via
-     *       {@link com.android.internal.policy.IKeyguardService}.
+     * @param force notify the occluded status to KeyguardService and update flags even though
+     *             occlude status doesn't change.
+     * @param transitionStarted {@code true} if keyguard (un)occluded transition started.
      * @return Whether the flags have changed and we have to redo the layout.
      */
-    private boolean setKeyguardOccludedLw(boolean isOccluded, boolean notify) {
+    private boolean setKeyguardOccludedLw(boolean isOccluded, boolean force,
+            boolean transitionStarted) {
         if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded);
         mKeyguardOccludedChanged = false;
-        if (isKeyguardOccluded() == isOccluded) {
+        if (isKeyguardOccluded() == isOccluded && !force) {
             return false;
         }
-        mKeyguardDelegate.setOccluded(isOccluded, notify);
-        return mKeyguardDelegate.isShowing();
+
+        final boolean showing = mKeyguardDelegate.isShowing();
+        final boolean animate = showing && !isOccluded;
+        // When remote animation is enabled for keyguard (un)occlude transition, KeyguardService
+        // uses remote animation start as a signal to update its occlusion status ,so we don't need
+        // to notify here.
+        final boolean notify = !WindowManagerService.sEnableRemoteKeyguardOccludeAnimation
+                || !transitionStarted;
+        mKeyguardDelegate.setOccluded(isOccluded, animate, notify);
+        return showing;
     }
 
     /** {@inheritDoc} */
@@ -4933,10 +4936,10 @@
     }
 
     @Override
-    public void startKeyguardExitAnimation(long startTime) {
+    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
         if (mKeyguardDelegate != null) {
             if (DEBUG_KEYGUARD) Slog.d(TAG, "PWM.startKeyguardExitAnimation");
-            mKeyguardDelegate.startKeyguardExitAnimation(startTime);
+            mKeyguardDelegate.startKeyguardExitAnimation(startTime, fadeoutDuration);
         }
     }
 
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 2b04050..4f00992 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -171,10 +171,10 @@
     void onKeyguardOccludedChangedLw(boolean occluded);
 
     /**
-     * @param notify {@code true} if the status change should be immediately notified via
-     *        {@link com.android.internal.policy.IKeyguardService}
+     * Applies a keyguard occlusion change if one happened.
+     * @param transitionStarted Whether keyguard (un)occlude transition is starting or not.
      */
-    int applyKeyguardOcclusionChange(boolean notify);
+    int applyKeyguardOcclusionChange(boolean transitionStarted);
 
     /**
      * Interface to the Window Manager state associated with a particular
@@ -1129,10 +1129,11 @@
 
     /**
      * Notifies the keyguard to start fading out.
-     *  @param startTime the start time of the animation in uptime milliseconds
      *
+     * @param startTime the start time of the animation in uptime milliseconds
+     * @param fadeoutDuration the duration of the exit animation, in milliseconds
      */
-    void startKeyguardExitAnimation(long startTime);
+    void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
 
     /**
      * Called when System UI has been started.
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 7737421..b79ac6f 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -249,10 +249,10 @@
         }
     }
 
-    public void setOccluded(boolean isOccluded, boolean notify) {
+    public void setOccluded(boolean isOccluded, boolean animate, boolean notify) {
         if (mKeyguardService != null && notify) {
-            if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ")");
-            mKeyguardService.setOccluded(isOccluded, false /* animate */);
+            if (DEBUG) Log.v(TAG, "setOccluded(" + isOccluded + ") animate=" + animate);
+            mKeyguardService.setOccluded(isOccluded, animate);
         }
         mKeyguardState.occluded = isOccluded;
     }
@@ -394,9 +394,9 @@
         }
     }
 
-    public void startKeyguardExitAnimation(long startTime) {
+    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
         if (mKeyguardService != null) {
-            mKeyguardService.startKeyguardExitAnimation(startTime, 0);
+            mKeyguardService.startKeyguardExitAnimation(startTime, fadeoutDuration);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 55175c5..7e94743 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -659,6 +659,9 @@
 
     boolean mUseTransferredAnimation;
 
+    /** Whether we need to setup the animation to animate only within the letterbox. */
+    private boolean mNeedsLetterboxedAnimation;
+
     /**
      * @see #currentLaunchCanTurnScreenOn()
      */
@@ -1762,8 +1765,12 @@
         mLetterboxUiController.layoutLetterbox(winHint);
     }
 
-    boolean hasWallpaperBackgroudForLetterbox() {
-        return mLetterboxUiController.hasWallpaperBackgroudForLetterbox();
+    boolean hasWallpaperBackgroundForLetterbox() {
+        return mLetterboxUiController.hasWallpaperBackgroundForLetterbox();
+    }
+
+    void updateLetterboxSurface(WindowState winHint, Transaction t) {
+        mLetterboxUiController.updateLetterboxSurface(winHint, t);
     }
 
     void updateLetterboxSurface(WindowState winHint) {
@@ -5325,6 +5332,18 @@
         commitVisibility(visible, performLayout, false /* fromTransition */);
     }
 
+    void setNeedsLetterboxedAnimation(boolean needsLetterboxedAnimation) {
+        mNeedsLetterboxedAnimation = needsLetterboxedAnimation;
+    }
+
+    boolean isNeedsLetterboxedAnimation() {
+        return mNeedsLetterboxedAnimation;
+    }
+
+    boolean isInLetterboxAnimation() {
+        return mNeedsLetterboxedAnimation && isAnimating();
+    }
+
     /**
      * Post process after applying an app transition animation.
      *
@@ -7252,6 +7271,10 @@
                 .setParent(getAnimationLeashParent())
                 .setName(getSurfaceControl() + " - animation-bounds")
                 .setCallsite("ActivityRecord.createAnimationBoundsLayer");
+        if (mNeedsLetterboxedAnimation) {
+            // Needs to be an effect layer to support rounded corners
+            builder.setEffectLayer();
+        }
         final SurfaceControl boundsLayer = builder.build();
         t.show(boundsLayer);
         return boundsLayer;
@@ -7289,6 +7312,11 @@
             mAnimatingActivityRegistry.notifyStarting(this);
         }
 
+        if (mNeedsLetterboxedAnimation) {
+            updateLetterboxSurface(findMainWindow(), t);
+            mNeedsAnimationBoundsLayer = true;
+        }
+
         // If the animation needs to be cropped then an animation bounds layer is created as a
         // child of the root pinned task or animation layer. The leash is then reparented to this
         // new layer.
@@ -7311,6 +7339,17 @@
             t.setLayer(leash, 0);
             t.setLayer(mAnimationBoundsLayer, getLastLayer());
 
+            if (mNeedsLetterboxedAnimation) {
+                final int cornerRadius = mLetterboxUiController
+                        .getRoundedCornersRadius(findMainWindow());
+
+                final Rect letterboxInnerBounds = new Rect();
+                getLetterboxInnerBounds(letterboxInnerBounds);
+
+                t.setCornerRadius(mAnimationBoundsLayer, cornerRadius)
+                        .setCrop(mAnimationBoundsLayer, letterboxInnerBounds);
+            }
+
             // Reparent leash to animation bounds layer.
             t.reparent(leash, mAnimationBoundsLayer);
         }
@@ -7424,6 +7463,12 @@
             mAnimationBoundsLayer = null;
         }
 
+        mNeedsAnimationBoundsLayer = false;
+        if (mNeedsLetterboxedAnimation) {
+            mNeedsLetterboxedAnimation = false;
+            updateLetterboxSurface(findMainWindow(), t);
+        }
+
         if (mAnimatingActivityRegistry != null) {
             mAnimatingActivityRegistry.notifyFinished(this);
         }
@@ -7436,7 +7481,6 @@
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AR#onAnimationFinished");
         mTransit = TRANSIT_OLD_UNSET;
         mTransitFlags = 0;
-        mNeedsAnimationBoundsLayer = false;
 
         setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER,
                 "ActivityRecord");
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0a4bc60d..de3b2a6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1486,6 +1486,7 @@
         a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
         a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
         a.resizeMode = RESIZE_MODE_UNRESIZEABLE;
+        a.configChanges = ActivityInfo.CONFIG_ORIENTATION;
 
         final ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchActivityType(ACTIVITY_TYPE_DREAM);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 5a1afc4..20032d6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -789,7 +789,7 @@
 
             // schedule launch ticks to collect information about slow apps.
             r.startLaunchTickingLocked();
-
+            r.lastLaunchTime = SystemClock.uptimeMillis();
             r.setProcess(proc);
 
             // Ensure activity is allowed to be resumed after process has set.
@@ -835,8 +835,6 @@
             final IActivityClientController activityClientController =
                     proc.hasEverLaunchedActivity() ? null : mService.mActivityClientController;
             r.launchCount++;
-            r.lastLaunchTime = SystemClock.uptimeMillis();
-            proc.setLastActivityLaunchTime(r.lastLaunchTime);
 
             if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);
 
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 95169db..55d6b2f 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -375,6 +375,9 @@
         final AnimationAdapter topOpeningAnim = wc != null ? wc.getAnimation() : null;
 
         int redoLayout = notifyAppTransitionStartingLocked(
+                AppTransition.isKeyguardGoingAwayTransitOld(transit),
+                AppTransition.isKeyguardOccludeTransitOld(transit),
+                topOpeningAnim != null ? topOpeningAnim.getDurationHint() : 0,
                 topOpeningAnim != null
                         ? topOpeningAnim.getStatusBarTransitionsStartTime()
                         : SystemClock.uptimeMillis(),
@@ -413,11 +416,8 @@
     }
 
     void freeze() {
-        final boolean keyguardGoingAwayCancelled = mNextAppTransitionRequests.contains(
+        final boolean keyguardGoingAway = mNextAppTransitionRequests.contains(
                 TRANSIT_KEYGUARD_GOING_AWAY);
-        final boolean keyguardOccludedCancelled =
-                mNextAppTransitionRequests.contains(TRANSIT_KEYGUARD_OCCLUDE)
-                || mNextAppTransitionRequests.contains(TRANSIT_KEYGUARD_UNOCCLUDE);
 
         // The RemoteAnimationControl didn't register AppTransitionListener and
         // only initialized the finish and timeout callback when goodToGo().
@@ -429,7 +429,7 @@
         mNextAppTransitionRequests.clear();
         clear();
         setReady();
-        notifyAppTransitionCancelledLocked(keyguardGoingAwayCancelled, keyguardOccludedCancelled);
+        notifyAppTransitionCancelledLocked(keyguardGoingAway);
     }
 
     private void setAppTransitionState(int state) {
@@ -479,11 +479,9 @@
         }
     }
 
-    private void notifyAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled,
-            boolean keyguardOccludedCancelled) {
+    private void notifyAppTransitionCancelledLocked(boolean keyguardGoingAway) {
         for (int i = 0; i < mListeners.size(); i++) {
-            mListeners.get(i).onAppTransitionCancelledLocked(keyguardGoingAwayCancelled,
-                    keyguardOccludedCancelled);
+            mListeners.get(i).onAppTransitionCancelledLocked(keyguardGoingAway);
         }
     }
 
@@ -493,12 +491,14 @@
         }
     }
 
-    private int notifyAppTransitionStartingLocked(long statusBarAnimationStartTime,
+    private int notifyAppTransitionStartingLocked(boolean keyguardGoingAway,
+            boolean keyguardOcclude, long duration, long statusBarAnimationStartTime,
             long statusBarAnimationDuration) {
         int redoLayout = 0;
         for (int i = 0; i < mListeners.size(); i++) {
-            redoLayout |= mListeners.get(i).onAppTransitionStartingLocked(
-                    statusBarAnimationStartTime, statusBarAnimationDuration);
+            redoLayout |= mListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway,
+                    keyguardOcclude, duration, statusBarAnimationStartTime,
+                    statusBarAnimationDuration);
         }
         return redoLayout;
     }
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index a645e89..44f388b 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -20,6 +20,10 @@
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
@@ -77,9 +81,11 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.Rect;
 import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Pair;
 import android.util.Slog;
 import android.view.Display;
 import android.view.RemoteAnimationAdapter;
@@ -89,6 +95,7 @@
 import android.view.WindowManager.TransitionFlags;
 import android.view.WindowManager.TransitionOldType;
 import android.view.WindowManager.TransitionType;
+import android.view.animation.Animation;
 import android.window.ITaskFragmentOrganizer;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -290,6 +297,7 @@
 
             final int flags = appTransition.getTransitFlags();
             layoutRedo = appTransition.goodToGo(transit, topOpeningApp);
+            handleNonAppWindowsInTransition(transit, flags);
             appTransition.postAnimationCallback();
             appTransition.clear();
         } finally {
@@ -1031,6 +1039,32 @@
             return;
         }
 
+        if (AppTransition.isActivityTransitOld(transit)) {
+            final ArrayList<Pair<ActivityRecord, Rect>> closingLetterboxes = new ArrayList();
+            for (int i = 0; i < closingApps.size(); ++i) {
+                ActivityRecord closingApp = closingApps.valueAt(i);
+                if (closingApp.areBoundsLetterboxed()) {
+                    final Rect insets = closingApp.getLetterboxInsets();
+                    closingLetterboxes.add(new Pair(closingApp, insets));
+                }
+            }
+
+            for (int i = 0; i < openingApps.size(); ++i) {
+                ActivityRecord openingApp = openingApps.valueAt(i);
+                if (openingApp.areBoundsLetterboxed()) {
+                    final Rect openingInsets = openingApp.getLetterboxInsets();
+                    for (Pair<ActivityRecord, Rect> closingLetterbox : closingLetterboxes) {
+                        final Rect closingInsets = closingLetterbox.second;
+                        if (openingInsets.equals(closingInsets)) {
+                            ActivityRecord closingApp = closingLetterbox.first;
+                            openingApp.setNeedsLetterboxedAnimation(true);
+                            closingApp.setNeedsLetterboxedAnimation(true);
+                        }
+                    }
+                }
+            }
+        }
+
         final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
                 openingApps, closingApps, true /* visible */);
         final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
@@ -1137,6 +1171,30 @@
         }
     }
 
+    private void handleNonAppWindowsInTransition(@TransitionOldType int transit, int flags) {
+        if (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
+                && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
+            if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
+                    && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0
+                    && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) == 0) {
+                Animation anim = mService.mPolicy.createKeyguardWallpaperExit(
+                        (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
+                if (anim != null) {
+                    anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
+                    mDisplayContent.mWallpaperController.startWallpaperAnimation(anim);
+                }
+            }
+        }
+        if ((transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY
+                || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER)
+                && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
+            mDisplayContent.startKeyguardExitOnNonAppWindows(
+                    transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+                    (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
+                    (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0);
+        }
+    }
+
     private boolean transitionGoodToGo(ArraySet<? extends WindowContainer> apps,
             ArrayMap<WindowContainer, Integer> outReasons) {
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 0422906..b033dca 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -504,6 +504,7 @@
 
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
+        mTransitionController.collectForDisplayAreaChange(this);
         mTmpConfiguration.setTo(getConfiguration());
         super.onConfigurationChanged(newParentConfig);
 
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 721497e..8a34af3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5946,6 +5946,9 @@
         if (changes != 0) {
             Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " "
                     + mTempConfig + " for displayId=" + mDisplayId);
+            if (isReady() && mTransitionController.isShellTransitionsEnabled()) {
+                requestChangeTransitionIfNeeded(changes, null /* displayChange */);
+            }
             onRequestedOverrideConfigurationChanged(mTempConfig);
 
             final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
@@ -5962,9 +5965,6 @@
             }
             mWmService.mDisplayNotificationController.dispatchDisplayChanged(
                     this, getConfiguration());
-            if (isReady() && mTransitionController.isShellTransitionsEnabled()) {
-                requestChangeTransitionIfNeeded(changes, null /* displayChange */);
-            }
         }
         return changes;
     }
@@ -6598,8 +6598,7 @@
         }
 
         @Override
-        public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled,
-                boolean keyguardOccludedCancelled) {
+        public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
             // It is only needed when freezing display in legacy transition.
             if (mTransitionController.isShellTransitionsEnabled()) return;
             continueUpdateOrientationForDiffOrienLaunchingApp();
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 24cef31..1a34c93 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -602,8 +602,9 @@
             }
 
             @Override
-            public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
-                    long statusBarAnimationDuration) {
+            public int onAppTransitionStartingLocked(boolean keyguardGoingAway,
+                    boolean keyguardOccluding, long duration,
+                    long statusBarAnimationStartTime, long statusBarAnimationDuration) {
                 mHandler.post(() -> {
                     StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
                     if (statusBar != null) {
@@ -615,8 +616,7 @@
             }
 
             @Override
-            public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled,
-                    boolean keyguardOccludedCancelled) {
+            public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
                 mHandler.post(mAppTransitionCancelled);
             }
 
@@ -794,11 +794,11 @@
     }
 
     public void setAwake(boolean awake) {
-        if (awake == mAwake) {
-            return;
-        }
-        mAwake = awake;
-        synchronized (mService.mGlobalLock) {
+        synchronized (mLock) {
+            if (awake == mAwake) {
+                return;
+            }
+            mAwake = awake;
             if (!mDisplayContent.isDefaultDisplay) {
                 return;
             }
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 8dd5850..97609a7 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -513,19 +513,6 @@
         return true;
     }
 
-    /**
-     * Utility to get a rotating displaycontent from a Transition.
-     * @return null if the transition doesn't contain a rotating display.
-     */
-    static DisplayContent getDisplayFromTransition(Transition transition) {
-        for (int i = transition.mParticipants.size() - 1; i >= 0; --i) {
-            final WindowContainer wc = transition.mParticipants.valueAt(i);
-            if (!(wc instanceof DisplayContent)) continue;
-            return (DisplayContent) wc;
-        }
-        return null;
-    }
-
     private void startRemoteRotation(int fromRotation, int toRotation) {
         mDisplayContent.mRemoteDisplayChangeController.performRemoteDisplayChange(
                 fromRotation, toRotation, null /* newDisplayAreaInfo */,
@@ -545,11 +532,6 @@
                 throw new IllegalStateException("Trying to rotate outside a transition");
             }
             mDisplayContent.mTransitionController.collect(mDisplayContent);
-            // Go through all tasks and collect them before the rotation
-            // TODO(shell-transitions): move collect() to onConfigurationChange once wallpaper
-            //       handling is synchronized.
-            mDisplayContent.mTransitionController.collectForDisplayAreaChange(mDisplayContent,
-                    null /* use collecting transition */);
         }
         mService.mAtmService.deferWindowLayout();
         try {
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index df3109a..27550d9 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -56,6 +56,7 @@
     private final Supplier<Boolean> mHasWallpaperBackgroundSupplier;
     private final Supplier<Integer> mBlurRadiusSupplier;
     private final Supplier<Float> mDarkScrimAlphaSupplier;
+    private final Supplier<SurfaceControl> mParentSurfaceSupplier;
 
     private final Rect mOuter = new Rect();
     private final Rect mInner = new Rect();
@@ -87,7 +88,8 @@
             Supplier<Integer> blurRadiusSupplier,
             Supplier<Float> darkScrimAlphaSupplier,
             IntConsumer doubleTapCallbackX,
-            IntConsumer doubleTapCallbackY) {
+            IntConsumer doubleTapCallbackY,
+            Supplier<SurfaceControl> parentSurface) {
         mSurfaceControlFactory = surfaceControlFactory;
         mTransactionFactory = transactionFactory;
         mAreCornersRounded = areCornersRounded;
@@ -97,6 +99,7 @@
         mDarkScrimAlphaSupplier = darkScrimAlphaSupplier;
         mDoubleTapCallbackX = doubleTapCallbackX;
         mDoubleTapCallbackY = doubleTapCallbackY;
+        mParentSurfaceSupplier = parentSurface;
     }
 
     /**
@@ -121,7 +124,6 @@
         mFullWindowSurface.layout(outer.left, outer.top, outer.right, outer.bottom, surfaceOrigin);
     }
 
-
     /**
      * Gets the insets between the outer and inner rects.
      */
@@ -333,6 +335,7 @@
         private SurfaceControl mSurface;
         private Color mColor;
         private boolean mHasWallpaperBackground;
+        private SurfaceControl mParentSurface;
 
         private final Rect mSurfaceFrameRelative = new Rect();
         private final Rect mLayoutFrameGlobal = new Rect();
@@ -403,10 +406,12 @@
                 }
 
                 mColor = mColorSupplier.get();
+                mParentSurface = mParentSurfaceSupplier.get();
                 t.setColor(mSurface, getRgbColorArray());
                 t.setPosition(mSurface, mSurfaceFrameRelative.left, mSurfaceFrameRelative.top);
                 t.setWindowCrop(mSurface, mSurfaceFrameRelative.width(),
                         mSurfaceFrameRelative.height());
+                t.reparent(mSurface, mParentSurface);
 
                 mHasWallpaperBackground = mHasWallpaperBackgroundSupplier.get();
                 updateAlphaAndBlur(t);
@@ -452,12 +457,13 @@
 
         public boolean needsApplySurfaceChanges() {
             return !mSurfaceFrameRelative.equals(mLayoutFrameRelative)
-                    // If mSurfaceFrameRelative is empty then mHasWallpaperBackground and mColor
-                    // may never be updated in applySurfaceChanges but this doesn't mean that
-                    // update is needed.
+                    // If mSurfaceFrameRelative is empty then mHasWallpaperBackground, mColor,
+                    // and mParentSurface may never be updated in applySurfaceChanges but this
+                    // doesn't mean that update is needed.
                     || !mSurfaceFrameRelative.isEmpty()
                     && (mHasWallpaperBackgroundSupplier.get() != mHasWallpaperBackground
-                    || !mColorSupplier.get().equals(mColor));
+                    || !mColorSupplier.get().equals(mColor)
+                    || mParentSurfaceSupplier.get() != mParentSurface);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 57c60f4..a469c6b 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -265,7 +265,7 @@
     }
 
     /**
-     * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0,
+     * Overrides corners radius for activities presented in the letterbox mode. If given value < 0,
      * both it and a value of {@link
      * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and
      * corners of the activity won't be rounded.
@@ -275,7 +275,7 @@
     }
 
     /**
-     * Resets corners raidus for activities presented in the letterbox mode to {@link
+     * Resets corners radius for activities presented in the letterbox mode to {@link
      * com.android.internal.R.integer.config_letterboxActivityCornersRadius}.
      */
     void resetLetterboxActivityCornersRadius() {
@@ -291,7 +291,7 @@
     }
 
     /**
-     * Gets corners raidus for activities presented in the letterbox mode.
+     * Gets corners radius for activities presented in the letterbox mode.
      */
     int getLetterboxActivityCornersRadius() {
         return mLetterboxActivityCornersRadius;
@@ -318,7 +318,7 @@
     /**
      * Sets color of letterbox background which is used when {@link
      * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
-     * fallback for other backfround types.
+     * fallback for other background types.
      */
     void setLetterboxBackgroundColor(Color color) {
         mLetterboxBackgroundColorOverride = color;
@@ -327,7 +327,7 @@
     /**
      * Sets color ID of letterbox background which is used when {@link
      * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
-     * fallback for other backfround types.
+     * fallback for other background types.
      */
     void setLetterboxBackgroundColorResourceId(int colorId) {
         mLetterboxBackgroundColorResourceIdOverride = colorId;
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index c8ed602..317c93e 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -89,7 +89,7 @@
 
     // Taskbar expanded height. Used to determine whether to crop an app window to display rounded
     // corners above the taskbar.
-    private float mExpandedTaskBarHeight;
+    private final float mExpandedTaskBarHeight;
 
     private boolean mShowWallpaperForLetterboxBackground;
 
@@ -120,7 +120,7 @@
         }
     }
 
-    boolean hasWallpaperBackgroudForLetterbox() {
+    boolean hasWallpaperBackgroundForLetterbox() {
         return mShowWallpaperForLetterboxBackground;
     }
 
@@ -137,6 +137,11 @@
     void getLetterboxInnerBounds(Rect outBounds) {
         if (mLetterbox != null) {
             outBounds.set(mLetterbox.getInnerFrame());
+            final WindowState w = mActivityRecord.findMainWindow();
+            if (w == null) {
+                return;
+            }
+            adjustBoundsForTaskbar(w, outBounds);
         } else {
             outBounds.setEmpty();
         }
@@ -160,13 +165,17 @@
     }
 
     void updateLetterboxSurface(WindowState winHint) {
+        updateLetterboxSurface(winHint, mActivityRecord.getSyncTransaction());
+    }
+
+    void updateLetterboxSurface(WindowState winHint, Transaction t) {
         final WindowState w = mActivityRecord.findMainWindow();
         if (w != winHint && winHint != null && w != null) {
             return;
         }
         layoutLetterbox(winHint);
         if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
-            mLetterbox.applySurfaceChanges(mActivityRecord.getSyncTransaction());
+            mLetterbox.applySurfaceChanges(t);
         }
     }
 
@@ -191,14 +200,22 @@
                         mActivityRecord.mWmService.mTransactionFactory,
                         this::shouldLetterboxHaveRoundedCorners,
                         this::getLetterboxBackgroundColor,
-                        this::hasWallpaperBackgroudForLetterbox,
+                        this::hasWallpaperBackgroundForLetterbox,
                         this::getLetterboxWallpaperBlurRadius,
                         this::getLetterboxWallpaperDarkScrimAlpha,
                         this::handleHorizontalDoubleTap,
-                        this::handleVerticalDoubleTap);
+                        this::handleVerticalDoubleTap,
+                        this::getLetterboxParentSurface);
                 mLetterbox.attachInput(w);
             }
-            mActivityRecord.getPosition(mTmpPoint);
+
+            if (mActivityRecord.isInLetterboxAnimation()) {
+                // In this case we attach the letterbox to the task instead of the activity.
+                mActivityRecord.getTask().getPosition(mTmpPoint);
+            } else {
+                mActivityRecord.getPosition(mTmpPoint);
+            }
+
             // Get the bounds of the "space-to-fill". The transformed bounds have the highest
             // priority because the activity is launched in a rotated environment. In multi-window
             // mode, the task-level represents this. In fullscreen-mode, the task container does
@@ -215,6 +232,13 @@
         }
     }
 
+    SurfaceControl getLetterboxParentSurface() {
+        if (mActivityRecord.isInLetterboxAnimation()) {
+            return mActivityRecord.getTask().getSurfaceControl();
+        }
+        return mActivityRecord.getSurfaceControl();
+    }
+
     private boolean shouldLetterboxHaveRoundedCorners() {
         // TODO(b/214030873): remove once background is drawn for transparent activities
         // Letterbox shouldn't have rounded corners if the activity is transparent
@@ -436,7 +460,7 @@
                 }
                 break;
             case LETTERBOX_BACKGROUND_WALLPAPER:
-                if (hasWallpaperBackgroudForLetterbox()) {
+                if (hasWallpaperBackgroundForLetterbox()) {
                     // Color is used for translucent scrim that dims wallpaper.
                     return Color.valueOf(Color.BLACK);
                 }
@@ -459,15 +483,14 @@
     private void updateRoundedCorners(WindowState mainWindow) {
         final SurfaceControl windowSurface = mainWindow.getClientViewRootSurface();
         if (windowSurface != null && windowSurface.isValid()) {
-            Transaction transaction = mActivityRecord.getSyncTransaction();
+            final Transaction transaction = mActivityRecord.getSyncTransaction();
 
-            final InsetsState insetsState = mainWindow.getInsetsState();
-            final InsetsSource taskbarInsetsSource =
-                    insetsState.peekSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
-
-            if (!isLetterboxedNotForDisplayCutout(mainWindow)
-                    || !mLetterboxConfiguration.isLetterboxActivityCornersRounded()
-                    || taskbarInsetsSource == null) {
+            if (!requiresRoundedCorners(mainWindow) || mActivityRecord.isInLetterboxAnimation()) {
+                // We don't want corner radius on the window.
+                // In the case the ActivityRecord requires a letterboxed animation we never want
+                // rounded corners on the window because rounded corners are applied at the
+                // animation-bounds surface level and rounded corners on the window would interfere
+                // with that leading to unexpected rounded corner positioning during the animation.
                 transaction
                         .setWindowCrop(windowSurface, null)
                         .setCornerRadius(windowSurface, 0);
@@ -476,48 +499,89 @@
 
             Rect cropBounds = null;
 
-            // Rounded corners should be displayed above the taskbar. When taskbar is hidden,
-            // an insets frame is equal to a navigation bar which shouldn't affect position of
-            // rounded corners since apps are expected to handle navigation bar inset.
-            // This condition checks whether the taskbar is visible.
-            // Do not crop the taskbar inset if the window is in immersive mode - the user can
-            // swipe to show/hide the taskbar as an overlay.
-            if (taskbarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight
-                    && taskbarInsetsSource.isVisible()) {
+            if (hasVisibleTaskbar(mainWindow)) {
                 cropBounds = new Rect(mActivityRecord.getBounds());
                 // Activity bounds are in screen coordinates while (0,0) for activity's surface
                 // control is at the top left corner of an app window so offsetting bounds
                 // accordingly.
                 cropBounds.offsetTo(0, 0);
-                // Rounded cornerners should be displayed above the taskbar.
-                cropBounds.bottom =
-                        Math.min(cropBounds.bottom, taskbarInsetsSource.getFrame().top);
-                if (mActivityRecord.inSizeCompatMode()
-                        && mActivityRecord.getSizeCompatScale() < 1.0f) {
-                    cropBounds.scale(1.0f / mActivityRecord.getSizeCompatScale());
-                }
+                // Rounded corners should be displayed above the taskbar.
+                adjustBoundsForTaskbarUnchecked(mainWindow, cropBounds);
             }
 
             transaction
                     .setWindowCrop(windowSurface, cropBounds)
-                    .setCornerRadius(windowSurface, getRoundedCorners(insetsState));
+                    .setCornerRadius(windowSurface, getRoundedCornersRadius(mainWindow));
         }
     }
 
-    // Returns rounded corners radius based on override in
+    private boolean requiresRoundedCorners(WindowState mainWindow) {
+        final InsetsSource taskbarInsetsSource = getTaskbarInsetsSource(mainWindow);
+
+        return isLetterboxedNotForDisplayCutout(mainWindow)
+                && mLetterboxConfiguration.isLetterboxActivityCornersRounded()
+                && taskbarInsetsSource != null;
+    }
+
+    // Returns rounded corners radius the letterboxed activity should have based on override in
     // R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii.
     // Device corners can be different on the right and left sides but we use the same radius
     // for all corners for consistency and pick a minimal bottom one for consistency with a
     // taskbar rounded corners.
-    private int getRoundedCorners(InsetsState insetsState) {
+    int getRoundedCornersRadius(WindowState mainWindow) {
+        if (!requiresRoundedCorners(mainWindow)) {
+            return 0;
+        }
+
         if (mLetterboxConfiguration.getLetterboxActivityCornersRadius() >= 0) {
             return mLetterboxConfiguration.getLetterboxActivityCornersRadius();
         }
+
+        final InsetsState insetsState = mainWindow.getInsetsState();
         return Math.min(
                 getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_LEFT),
                 getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_RIGHT));
     }
 
+    /**
+     * Returns whether the taskbar is visible. Returns false if the window is in immersive mode,
+     * since the user can swipe to show/hide the taskbar as an overlay.
+     */
+    private boolean hasVisibleTaskbar(WindowState mainWindow) {
+        final InsetsSource taskbarInsetsSource = getTaskbarInsetsSource(mainWindow);
+
+        return taskbarInsetsSource != null
+                && taskbarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight
+                && taskbarInsetsSource.isVisible();
+    }
+
+    private InsetsSource getTaskbarInsetsSource(WindowState mainWindow) {
+        final InsetsState insetsState = mainWindow.getInsetsState();
+        return insetsState.peekSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
+    }
+
+    private void adjustBoundsForTaskbar(WindowState mainWindow, Rect bounds) {
+        // Rounded corners should be displayed above the taskbar. When taskbar is hidden,
+        // an insets frame is equal to a navigation bar which shouldn't affect position of
+        // rounded corners since apps are expected to handle navigation bar inset.
+        // This condition checks whether the taskbar is visible.
+        // Do not crop the taskbar inset if the window is in immersive mode - the user can
+        // swipe to show/hide the taskbar as an overlay.
+        if (hasVisibleTaskbar(mainWindow)) {
+            adjustBoundsForTaskbarUnchecked(mainWindow, bounds);
+        }
+    }
+
+    private void adjustBoundsForTaskbarUnchecked(WindowState mainWindow, Rect bounds) {
+        // Rounded corners should be displayed above the taskbar.
+        bounds.bottom =
+                Math.min(bounds.bottom, getTaskbarInsetsSource(mainWindow).getFrame().top);
+        if (mActivityRecord.inSizeCompatMode()
+                && mActivityRecord.getSizeCompatScale() < 1.0f) {
+            bounds.scale(1.0f / mActivityRecord.getSizeCompatScale());
+        }
+    }
+
     private int getInsetsStateCornerRadius(
                 InsetsState insetsState, @RoundedCorner.Position int position) {
         RoundedCorner corner = insetsState.getRoundedCorners().getRoundedCorner(position);
@@ -592,7 +656,7 @@
                 + letterboxBackgroundTypeToString(
                         mLetterboxConfiguration.getLetterboxBackgroundType()));
         pw.println(prefix + "  letterboxCornerRadius="
-                + getRoundedCorners(mainWin.getInsetsState()));
+                + getRoundedCornersRadius(mainWin));
         if (mLetterboxConfiguration.getLetterboxBackgroundType()
                 == LETTERBOX_BACKGROUND_WALLPAPER) {
             pw.println(prefix + "  isLetterboxWallpaperBlurSupported="
diff --git a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
index 64749cf..a89894d 100644
--- a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
+++ b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java
@@ -101,7 +101,6 @@
 
         if (t != null) {
             mDisplayContent.mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
-            mTransitionController.collectForDisplayAreaChange(mDisplayContent, t);
             mTransition = t;
         }
     }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index db79eae..5b702ea 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -161,15 +161,15 @@
      */
     final AppTransitionListener mAppTransitionListener = new AppTransitionListener() {
         @Override
-        public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
+        public int onAppTransitionStartingLocked(boolean keyguardGoingAway,
+                boolean keyguardOccluding, long duration, long statusBarAnimationStartTime,
                 long statusBarAnimationDuration) {
             continueDeferredCancel();
             return 0;
         }
 
         @Override
-        public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled,
-                boolean keyguardOccludedCancelled) {
+        public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
             continueDeferredCancel();
         }
 
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index c7f8a1e..f3670e4 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -27,7 +27,10 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.Nullable;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
 import android.graphics.Insets;
+import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.hardware.power.Boost;
@@ -374,43 +377,45 @@
         final int targetSurfaceWidth = bounds.width();
 
         if (maxExtensionInsets.left < 0) {
-            final Rect edgeBounds = new Rect(0, 0, 1, targetSurfaceHeight);
+            final Rect edgeBounds = new Rect(bounds.left, bounds.top, bounds.left + 1,
+                    bounds.bottom);
             final Rect extensionRect = new Rect(0, 0,
                     -maxExtensionInsets.left, targetSurfaceHeight);
-            final int xPos = maxExtensionInsets.left;
-            final int yPos = 0;
+            final int xPos = bounds.left + maxExtensionInsets.left;
+            final int yPos = bounds.top;
             createExtensionSurface(leash, edgeBounds,
                     extensionRect, xPos, yPos, "Left Edge Extension", transaction);
         }
 
         if (maxExtensionInsets.top < 0) {
-            final Rect edgeBounds = new Rect(0, 0, targetSurfaceWidth, 1);
+            final Rect edgeBounds = new Rect(bounds.left, bounds.top, targetSurfaceWidth,
+                    bounds.top + 1);
             final Rect extensionRect = new Rect(0, 0,
                     targetSurfaceWidth, -maxExtensionInsets.top);
-            final int xPos = 0;
-            final int yPos = maxExtensionInsets.top;
+            final int xPos = bounds.left;
+            final int yPos = bounds.top + maxExtensionInsets.top;
             createExtensionSurface(leash, edgeBounds,
                     extensionRect, xPos, yPos, "Top Edge Extension", transaction);
         }
 
         if (maxExtensionInsets.right < 0) {
-            final Rect edgeBounds = new Rect(targetSurfaceWidth - 1, 0,
-                    targetSurfaceWidth, targetSurfaceHeight);
+            final Rect edgeBounds = new Rect(bounds.right - 1, bounds.top, bounds.right,
+                    bounds.bottom);
             final Rect extensionRect = new Rect(0, 0,
                     -maxExtensionInsets.right, targetSurfaceHeight);
-            final int xPos = targetSurfaceWidth;
-            final int yPos = 0;
+            final int xPos = bounds.right;
+            final int yPos = bounds.top;
             createExtensionSurface(leash, edgeBounds,
                     extensionRect, xPos, yPos, "Right Edge Extension", transaction);
         }
 
         if (maxExtensionInsets.bottom < 0) {
-            final Rect edgeBounds = new Rect(0, targetSurfaceHeight - 1,
-                    targetSurfaceWidth, targetSurfaceHeight);
+            final Rect edgeBounds = new Rect(bounds.left, bounds.bottom - 1,
+                    bounds.right, bounds.bottom);
             final Rect extensionRect = new Rect(0, 0,
                     targetSurfaceWidth, -maxExtensionInsets.bottom);
-            final int xPos = maxExtensionInsets.left;
-            final int yPos = targetSurfaceHeight;
+            final int xPos = bounds.left;
+            final int yPos = bounds.bottom;
             createExtensionSurface(leash, edgeBounds,
                     extensionRect, xPos, yPos, "Bottom Edge Extension", transaction);
         }
@@ -453,16 +458,20 @@
                 .setHidden(true)
                 .setCallsite("DefaultTransitionHandler#startAnimation")
                 .setOpaque(true)
-                .setBufferSize(edgeBounds.width(), edgeBounds.height())
+                .setBufferSize(extensionRect.width(), extensionRect.height())
                 .build();
 
-        final Surface surface = new Surface(edgeExtensionLayer);
-        surface.attachAndQueueBufferWithColorSpace(edgeBuffer.getHardwareBuffer(),
-                edgeBuffer.getColorSpace());
-        surface.release();
+        BitmapShader shader = new BitmapShader(edgeBuffer.asBitmap(),
+                android.graphics.Shader.TileMode.CLAMP,
+                android.graphics.Shader.TileMode.CLAMP);
+        final Paint paint = new Paint();
+        paint.setShader(shader);
 
-        final float scaleX = getScaleXForExtensionSurface(edgeBounds, extensionRect);
-        final float scaleY = getScaleYForExtensionSurface(edgeBounds, extensionRect);
+        final Surface surface = new Surface(edgeExtensionLayer);
+        Canvas c = surface.lockHardwareCanvas();
+        c.drawRect(extensionRect, paint);
+        surface.unlockCanvasAndPost(c);
+        surface.release();
 
         synchronized (mEdgeExtensionLock) {
             if (!mEdgeExtensions.containsKey(leash)) {
@@ -472,7 +481,6 @@
                 return;
             }
 
-            startTransaction.setScale(edgeExtensionLayer, scaleX, scaleY);
             startTransaction.reparent(edgeExtensionLayer, leash);
             startTransaction.setLayer(edgeExtensionLayer, Integer.MIN_VALUE);
             startTransaction.setPosition(edgeExtensionLayer, xPos, yPos);
@@ -508,8 +516,6 @@
         throw new RuntimeException("Unexpected edgeBounds and extensionRect heights");
     }
 
-
-
     private static final class RunningAnimation {
         final AnimationSpec mAnimSpec;
         final SurfaceControl mLeash;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 3a17e48..803890b 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -31,7 +31,13 @@
 import static android.view.WindowManager.TRANSIT_CHANGE;
 import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -66,6 +72,7 @@
 import android.os.IBinder;
 import android.os.IRemoteCallback;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -73,6 +80,7 @@
 import android.util.SparseArray;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
+import android.view.animation.Animation;
 import android.window.RemoteTransition;
 import android.window.TransitionInfo;
 
@@ -1058,9 +1066,36 @@
 
     private void handleNonAppWindowsInTransition(@NonNull DisplayContent dc,
             @TransitionType int transit, @TransitionFlags int flags) {
+        if ((transit == TRANSIT_KEYGUARD_GOING_AWAY
+                || (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0)
+                && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
+            if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
+                    && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0
+                    && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) == 0) {
+                Animation anim = mController.mAtm.mWindowManager.mPolicy
+                        .createKeyguardWallpaperExit(
+                                (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
+                if (anim != null) {
+                    anim.scaleCurrentDuration(
+                            mController.mAtm.mWindowManager.getTransitionAnimationScaleLocked());
+                    dc.mWallpaperController.startWallpaperAnimation(anim);
+                }
+            }
+            dc.startKeyguardExitOnNonAppWindows(
+                    (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0,
+                    (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0,
+                    (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0);
+            if (!WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) {
+                // When remote animation is enabled for KEYGUARD_GOING_AWAY transition, SysUI
+                // receives IRemoteAnimationRunner#onAnimationStart to start animation, so we don't
+                // need to call IKeyguardService#keyguardGoingAway here.
+                mController.mAtm.mWindowManager.mPolicy.startKeyguardExitAnimation(
+                        SystemClock.uptimeMillis(), 0 /* duration */);
+            }
+        }
         if ((flags & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
             mController.mAtm.mWindowManager.mPolicy.applyKeyguardOcclusionChange(
-                    false /* notify */);
+                    true /* keyguardOccludingStarted */);
         }
     }
 
@@ -1606,6 +1641,19 @@
         return mainWin.getAttrs().rotationAnimation;
     }
 
+    /** Applies the new configuration and returns {@code true} if there is a display change. */
+    boolean applyDisplayChangeIfNeeded() {
+        boolean changed = false;
+        for (int i = mParticipants.size() - 1; i >= 0; --i) {
+            final WindowContainer<?> wc = mParticipants.valueAt(i);
+            final DisplayContent dc = wc.asDisplayContent();
+            if (dc == null || !mChanges.get(dc).hasChanged(dc)) continue;
+            dc.sendNewConfiguration();
+            changed = true;
+        }
+        return changed;
+    }
+
     boolean getLegacyIsReady() {
         return isCollecting() && mSyncId >= 0;
     }
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 79dfaa9..846aa3e 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -463,13 +463,12 @@
     }
 
     /**
-     * Collects the window containers which need to be synced with the changing display (e.g.
-     * rotating) to the given transition or the current collecting transition.
+     * Collects the window containers which need to be synced with the changing display area into
+     * the current collecting transition.
      */
-    void collectForDisplayAreaChange(@NonNull DisplayArea<?> wc, @Nullable Transition incoming) {
-        if (incoming == null) incoming = mCollectingTransition;
-        if (incoming == null) return;
-        final Transition transition = incoming;
+    void collectForDisplayAreaChange(@NonNull DisplayArea<?> wc) {
+        final Transition transition = mCollectingTransition;
+        if (transition == null || !transition.mParticipants.contains(wc)) return;
         // Collect all visible tasks.
         wc.forAllLeafTasks(task -> {
             if (task.isVisible()) {
@@ -638,9 +637,11 @@
     }
 
     void dispatchLegacyAppTransitionStarting(TransitionInfo info, long statusBarTransitionDelay) {
+        final boolean keyguardGoingAway = info.isKeyguardGoingAway();
         for (int i = 0; i < mLegacyListeners.size(); ++i) {
             // TODO(shell-transitions): handle (un)occlude transition.
-            mLegacyListeners.get(i).onAppTransitionStartingLocked(
+            mLegacyListeners.get(i).onAppTransitionStartingLocked(keyguardGoingAway,
+                    false /* keyguardOcclude */, 0 /* durationHint */,
                     SystemClock.uptimeMillis() + statusBarTransitionDelay,
                     AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
         }
@@ -655,7 +656,7 @@
     void dispatchLegacyAppTransitionCancelled() {
         for (int i = 0; i < mLegacyListeners.size(); ++i) {
             mLegacyListeners.get(i).onAppTransitionCancelledLocked(
-                    false /* keyguardGoingAwayCancelled */, false /* keyguardOccludedCancelled */);
+                    false /* keyguardGoingAway */);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 70dd9f3..73658b2 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3019,6 +3019,10 @@
                 final float windowCornerRadius = !inMultiWindowMode()
                         ? getDisplayContent().getWindowCornerRadius()
                         : 0;
+                if (asActivityRecord() != null
+                        && asActivityRecord().isNeedsLetterboxedAnimation()) {
+                    asActivityRecord().getLetterboxInnerBounds(mTmpRect);
+                }
                 AnimationAdapter adapter = new LocalAnimationAdapter(
                         new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
                                 getDisplayContent().mAppTransition.canSkipFirstFrame(),
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 4b5b6da..a71c386 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -220,13 +220,9 @@
         /**
          * Called when a pending app transition gets cancelled.
          *
-         * @param keyguardGoingAwayCancelled {@code true} if keyguard going away transition was
-         *        cancelled.
-         * @param keyguardOccludedCancelled {@code true} if keyguard (un)occluded transition was
-         *        cancelled.
+         * @param keyguardGoingAway true if keyguard going away transition got cancelled.
          */
-        public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled,
-                boolean keyguardOccludedCancelled) {}
+        public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {}
 
         /**
          * Called when an app transition is timed out.
@@ -236,6 +232,9 @@
         /**
          * Called when an app transition gets started
          *
+         * @param keyguardGoingAway true if keyguard going away transition is started.
+         * @param keyguardOccluding true if keyguard (un)occlude transition is started.
+         * @param duration the total duration of the transition
          * @param statusBarAnimationStartTime the desired start time for all visual animations in
          *        the status bar caused by this app transition in uptime millis
          * @param statusBarAnimationDuration the duration for all visual animations in the status
@@ -246,7 +245,8 @@
          * {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_WALLPAPER},
          * or {@link WindowManagerPolicy#FINISH_LAYOUT_REDO_ANIM}.
          */
-        public int onAppTransitionStartingLocked(long statusBarAnimationStartTime,
+        public int onAppTransitionStartingLocked(boolean keyguardGoingAway,
+                boolean keyguardOccluding, long duration, long statusBarAnimationStartTime,
                 long statusBarAnimationDuration) {
             return 0;
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 298f93d..22411bb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -425,6 +425,32 @@
             SystemProperties.getBoolean(ENABLE_SHELL_TRANSITIONS, false);
 
     /**
+     * Run Keyguard animation as remote animation in System UI instead of local animation in
+     * the server process.
+     *
+     * 0: Runs all keyguard animation as local animation
+     * 1: Only runs keyguard going away animation as remote animation
+     * 2: Runs all keyguard animation as remote animation
+     */
+    private static final String ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY =
+            "persist.wm.enable_remote_keyguard_animation";
+
+    private static final int sEnableRemoteKeyguardAnimation =
+            SystemProperties.getInt(ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY, 2);
+
+    /**
+     * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+     */
+    public static final boolean sEnableRemoteKeyguardGoingAwayAnimation =
+            sEnableRemoteKeyguardAnimation >= 1;
+
+    /**
+     * @see #ENABLE_REMOTE_KEYGUARD_ANIMATION_PROPERTY
+     */
+    public static final boolean sEnableRemoteKeyguardOccludeAnimation =
+            sEnableRemoteKeyguardAnimation >= 2;
+
+    /**
      * Allows a fullscreen windowing mode activity to launch in its desired orientation directly
      * when the display has different orientation.
      */
@@ -1092,8 +1118,7 @@
             = new WindowManagerInternal.AppTransitionListener() {
 
         @Override
-        public void onAppTransitionCancelledLocked(boolean keyguardGoingAwayCancelled,
-                boolean keyguardOccludedCancelled) {
+        public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 3b9cd36..4f03264 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -397,17 +397,8 @@
         mService.deferWindowLayout();
         mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
         try {
-            if (transition != null) {
-                // First check if we have a display rotation transition and if so, update it.
-                final DisplayContent dc = DisplayRotation.getDisplayFromTransition(transition);
-                if (dc != null && transition.mChanges.get(dc).hasChanged(dc)) {
-                    // Go through all tasks and collect them before the rotation
-                    // TODO(shell-transitions): move collect() to onConfigurationChange once
-                    //       wallpaper handling is synchronized.
-                    dc.mTransitionController.collectForDisplayAreaChange(dc, transition);
-                    dc.sendNewConfiguration();
-                    effects |= TRANSACT_EFFECTS_LIFECYCLE;
-                }
+            if (transition != null && transition.applyDisplayChangeIfNeeded()) {
+                effects |= TRANSACT_EFFECTS_LIFECYCLE;
             }
             final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
             final int hopSize = hops.size();
@@ -428,15 +419,6 @@
                     addToSyncSet(syncId, wc);
                 }
                 if (transition != null) transition.collect(wc);
-                final DisplayArea da = wc.asDisplayArea();
-                // Only check DisplayArea here as a similar thing is done for DisplayContent above.
-                if (da != null && wc.asDisplayContent() == null
-                        && entry.getValue().getWindowingMode() != da.getWindowingMode()) {
-                    // Go through all tasks and collect them before changing the windowing mode of a
-                    // display-level container.
-                    // TODO(shell-transitions): handle this more elegantly.
-                    da.mTransitionController.collectForDisplayAreaChange(da, transition);
-                }
 
                 if ((entry.getValue().getChangeMask()
                         & WindowContainerTransaction.Change.CHANGE_FORCE_NO_PIP) != 0) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a85dcbf..d2b9bda 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -6041,7 +6041,7 @@
     }
 
     boolean hasWallpaperForLetterboxBackground() {
-        return mActivityRecord != null && mActivityRecord.hasWallpaperBackgroudForLetterbox();
+        return mActivityRecord != null && mActivityRecord.hasWallpaperBackgroundForLetterbox();
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/input/OWNERS b/services/tests/servicestests/src/com/android/server/input/OWNERS
index d701f23..6e9aa1d 100644
--- a/services/tests/servicestests/src/com/android/server/input/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/input/OWNERS
@@ -1 +1,2 @@
-include /core/java/android/hardware/input/OWNERS
+include /services/core/java/com/android/server/input/OWNERS
+
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 34b40c7..b1ad8ec 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -16,53 +16,79 @@
 
 package com.android.server.pm;
 
+import static android.os.UserManager.DISALLOW_USER_SWITCH;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.ActivityManager;
+import android.app.PropertyInvalidatedCache;
+import android.content.Context;
 import android.content.pm.UserInfo;
 import android.os.Bundle;
 import android.os.FileUtils;
+import android.os.Looper;
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.platform.test.annotations.Postsubmit;
 import android.support.test.uiautomator.UiDevice;
-import android.test.AndroidTestCase;
-import android.text.TextUtils;
 import android.util.AtomicFile;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.Arrays;
 
+/** Test {@link UserManagerService} functionality. */
 @Postsubmit
-@SmallTest
-public class UserManagerServiceTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class UserManagerServiceTest {
     private static String[] STRING_ARRAY = new String[] {"<tag", "<![CDATA["};
     private File restrictionsFile;
     private int tempUserId = UserHandle.USER_NULL;
+    private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
+    private UserManagerService mUserManagerService;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Before
+    public void setup() throws Exception {
+        // Currently UserManagerService cannot be instantiated twice inside a VM without a cleanup
+        // TODO: Remove once UMS supports proper dependency injection
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        // Disable binder caches in this process.
+        PropertyInvalidatedCache.disableForTestMode();
+
+        LocalServices.removeServiceForTest(UserManagerInternal.class);
+        mUserManagerService = new UserManagerService(InstrumentationRegistry.getContext());
+
         restrictionsFile = new File(mContext.getCacheDir(), "restrictions.xml");
         restrictionsFile.delete();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void teardown() throws Exception {
         restrictionsFile.delete();
         if (tempUserId != UserHandle.USER_NULL) {
             UserManager.get(mContext).removeUser(tempUserId);
         }
-        super.tearDown();
     }
 
+    @Test
     public void testWriteReadApplicationRestrictions() throws IOException {
         AtomicFile atomicFile = new AtomicFile(restrictionsFile);
         Bundle bundle = createBundle();
         UserManagerService.writeApplicationRestrictionsLAr(bundle, atomicFile);
-        assertTrue(atomicFile.getBaseFile().exists());
+        assertThat(atomicFile.getBaseFile().exists()).isTrue();
         String s = FileUtils.readTextFile(restrictionsFile, 10000, "");
         System.out.println("restrictionsFile: " + s);
         bundle = UserManagerService.readApplicationRestrictionsLAr(atomicFile);
@@ -70,22 +96,22 @@
         assertBundle(bundle);
     }
 
+    @Test
     public void testAddUserWithAccount() {
         UserManager um = UserManager.get(mContext);
         UserInfo user = um.createUser("Test User", 0);
-        assertNotNull(user);
+        assertThat(user).isNotNull();
         tempUserId = user.id;
         String accountName = "Test Account";
         um.setUserAccount(tempUserId, accountName);
-        assertEquals(accountName, um.getUserAccount(tempUserId));
+        assertThat(um.getUserAccount(tempUserId)).isEqualTo(accountName);
     }
 
+    @Test
     public void testUserSystemPackageWhitelist() throws Exception {
         String cmd = "cmd user report-system-user-package-whitelist-problems --critical-only";
         final String result = runShellCommand(cmd);
-        if (!TextUtils.isEmpty(result)) {
-            fail("Command '" + cmd + " reported errors:\n" + result);
-        }
+        assertThat(result).isEmpty();
     }
 
     private Bundle createBundle() {
@@ -114,26 +140,141 @@
     }
 
     private void assertBundle(Bundle bundle) {
-        assertFalse(bundle.getBoolean("boolean_0"));
-        assertTrue(bundle.getBoolean("boolean_1"));
-        assertEquals(100, bundle.getInt("integer"));
-        assertEquals("", bundle.getString("empty"));
-        assertEquals("text", bundle.getString("string"));
-        assertEquals(Arrays.asList(STRING_ARRAY), Arrays.asList(bundle.getStringArray("string[]")));
+        assertThat(bundle.getBoolean("boolean_0")).isFalse();
+        assertThat(bundle.getBoolean("boolean_1")).isTrue();
+        assertThat(bundle.getInt("integer")).isEqualTo(100);
+        assertThat(bundle.getString("empty")).isEqualTo("");
+        assertThat(bundle.getString("string")).isEqualTo("text");
+        assertThat(Arrays.asList(bundle.getStringArray("string[]")))
+                .isEqualTo(Arrays.asList(STRING_ARRAY));
         Parcelable[] bundle_array = bundle.getParcelableArray("bundle_array");
-        assertEquals(2, bundle_array.length);
+        assertThat(bundle_array.length).isEqualTo(2);
         Bundle bundle1 = (Bundle) bundle_array[0];
-        assertEquals("bundle_array_string", bundle1.getString("bundle_array_string"));
-        assertNotNull(bundle1.getBundle("bundle_array_bundle"));
+        assertThat(bundle1.getString("bundle_array_string"))
+                .isEqualTo("bundle_array_string");
+        assertThat(bundle1.getBundle("bundle_array_bundle")).isNotNull();
         Bundle bundle2 = (Bundle) bundle_array[1];
-        assertEquals("bundle_array_string2", bundle2.getString("bundle_array_string2"));
+        assertThat(bundle2.getString("bundle_array_string2"))
+                .isEqualTo("bundle_array_string2");
         Bundle childBundle = bundle.getBundle("bundle");
-        assertEquals("bundle_string", childBundle.getString("bundle_string"));
-        assertEquals(1, childBundle.getInt("bundle_int"));
+        assertThat(childBundle.getString("bundle_string"))
+                .isEqualTo("bundle_string");
+        assertThat(childBundle.getInt("bundle_int")).isEqualTo(1);
     }
 
+    @Test
+    public void assertHasUserRestriction() throws Exception {
+        int userId = ActivityManager.getCurrentUser();
+
+        mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, true, userId);
+        assertThat(mUserManagerService.hasUserRestriction(DISALLOW_USER_SWITCH, userId)).isTrue();
+
+        mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, false, userId);
+        assertThat(mUserManagerService.hasUserRestriction(DISALLOW_USER_SWITCH, userId)).isFalse();
+    }
+
+    @Test
+    public void assertIsUserSwitcherEnabledOnMultiUserSettings() throws Exception {
+        int userId = ActivityManager.getCurrentUser();
+        resetUserSwitcherEnabled();
+
+        setUserSwitch(false);
+        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();
+
+        setUserSwitch(true);
+        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
+    }
+
+    @Test
+    public void assertIsUserSwitcherEnabledOnMaxSupportedUsers()  throws Exception {
+        int userId = ActivityManager.getCurrentUser();
+        setMaxSupportedUsers(1);
+
+        assertThat(UserManager.supportsMultipleUsers()).isFalse();
+        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();
+
+        setMaxSupportedUsers(8);
+
+        assertThat(UserManager.supportsMultipleUsers()).isTrue();
+        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
+    }
+
+
+    @Test
+    public void assertIsUserSwitcherEnabledOnShowMultiuserUI()  throws Exception {
+        int userId = ActivityManager.getCurrentUser();
+        setShowMultiuserUI(false);
+
+        assertThat(UserManager.supportsMultipleUsers()).isFalse();
+        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();
+
+        setShowMultiuserUI(true);
+
+        assertThat(UserManager.supportsMultipleUsers()).isTrue();
+        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
+    }
+
+    @Test
+    public void assertIsUserSwitcherEnabledOnUserRestrictions() throws Exception {
+        int userId = ActivityManager.getCurrentUser();
+        resetUserSwitcherEnabled();
+
+        mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, true, userId);
+        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();
+
+        mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, false, userId);
+        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
+    }
+
+    @Test
+    public void assertIsUserSwitcherEnabledOnDemoMode()  throws Exception {
+        int userId = ActivityManager.getCurrentUser();
+        resetUserSwitcherEnabled();
+
+        setDeviceDemoMode(true);
+        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();
+
+        setDeviceDemoMode(false);
+        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
+    }
+
+    private void resetUserSwitcherEnabled() throws Exception {
+        int userId = ActivityManager.getCurrentUser();
+        setUserSwitch(true);
+        setShowMultiuserUI(true);
+        setDeviceDemoMode(false);
+        setMaxSupportedUsers(8);
+        mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, false, userId);
+    }
+
+    private void setUserSwitch(boolean enabled) {
+        android.provider.Settings.Global.putInt(mContext.getContentResolver(),
+                android.provider.Settings.Global.USER_SWITCHER_ENABLED, enabled ? 1 : 0);
+    }
+
+    private void setDeviceDemoMode(boolean enabled) {
+        android.provider.Settings.Global.putInt(mContext.getContentResolver(),
+                android.provider.Settings.Global.DEVICE_DEMO_MODE, enabled ? 1 : 0);
+    }
+
+
     private static String runShellCommand(String cmd) throws Exception {
         return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
                 .executeShellCommand(cmd);
     }
+
+    private static String setSystemProperty(String name, String value) throws Exception {
+        final String oldValue = runShellCommand("getprop " + name);
+        assertThat(runShellCommand("setprop " + name + " " + value))
+                .isEqualTo("");
+        return oldValue;
+    }
+
+    private static void setMaxSupportedUsers(int max) throws Exception {
+        setSystemProperty("fw.max_users", String.valueOf(max));
+    }
+
+    public static void setShowMultiuserUI(boolean show) throws Exception {
+        setSystemProperty("fw.show_multiuserui", String.valueOf(show));
+    }
 }
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 40cda34..ec48e23 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4239,6 +4239,59 @@
     }
 
     @Test
+    public void testSubstituteAppName_hasPermission() throws RemoteException {
+        String subName = "Substitute Name";
+        when(mPackageManager.checkPermission(
+                eq(android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME), any(), anyInt()))
+                .thenReturn(PERMISSION_GRANTED);
+        Bundle extras = new Bundle();
+        extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, subName);
+        Notification.Builder nb = new Notification.Builder(mContext,
+                mTestNotificationChannel.getId())
+                .addExtras(extras);
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "testSubstituteAppNamePermission", 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());
+
+        assertTrue(posted.getNotification().extras
+                .containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME));
+        assertEquals(posted.getNotification().extras
+                .getString(Notification.EXTRA_SUBSTITUTE_APP_NAME), subName);
+    }
+
+    @Test
+    public void testSubstituteAppName_noPermission() throws RemoteException {
+        when(mPackageManager.checkPermission(
+                eq(android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME), any(), anyInt()))
+                .thenReturn(PERMISSION_DENIED);
+        Bundle extras = new Bundle();
+        extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, "Substitute Name");
+        Notification.Builder nb = new Notification.Builder(mContext,
+                mTestNotificationChannel.getId())
+                .addExtras(extras);
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "testSubstituteAppNamePermission", 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_SUBSTITUTE_APP_NAME));
+    }
+
+    @Test
     public void testGetNotificationCountLocked() {
         String sampleTagToExclude = null;
         int sampleIdToExclude = 0;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 598a22b..d62ac99 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -93,6 +93,7 @@
 import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Parcel;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -2447,6 +2448,35 @@
     }
 
     @Test
+    public void testGetNotificationChannelGroup() throws Exception {
+        NotificationChannelGroup notDeleted = new NotificationChannelGroup("not", "deleted");
+        NotificationChannel base =
+                new NotificationChannel("not deleted", "belongs to notDeleted", IMPORTANCE_DEFAULT);
+        base.setGroup("not");
+        NotificationChannel convo =
+                new NotificationChannel("convo", "belongs to notDeleted", IMPORTANCE_DEFAULT);
+        convo.setGroup("not");
+        convo.setConversationId("not deleted", "banana");
+
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, notDeleted, true);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, base, true, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, convo, true, false);
+        mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, notDeleted, true);
+
+        NotificationChannelGroup g
+                = mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG_N_MR1, UID_N_MR1);
+        Parcel parcel = Parcel.obtain();
+        g.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        NotificationChannelGroup g2
+                = mHelper.getNotificationChannelGroup(notDeleted.getId(), PKG_N_MR1, UID_N_MR1);
+        Parcel parcel2 = Parcel.obtain();
+        g2.writeToParcel(parcel2, 0);
+        parcel2.setDataPosition(0);
+    }
+
+    @Test
     public void testOnUserRemoved() throws Exception {
         int[] user0Uids = {98, 235, 16, 3782};
         int[] user1Uids = new int[user0Uids.length];
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 7a73f08..7244d94 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1916,10 +1916,10 @@
         testPlayer.start();
         assertNotEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation());
         assertNotNull(testPlayer.mLastReady);
-        assertEquals(dc, DisplayRotation.getDisplayFromTransition(testPlayer.mLastTransit));
         WindowContainerToken dcToken = dc.mRemoteToken.toWindowContainerToken();
         assertNotEquals(testPlayer.mLastReady.getChange(dcToken).getEndRotation(),
                 testPlayer.mLastReady.getChange(dcToken).getStartRotation());
+        assertTrue(testPlayer.mLastTransit.applyDisplayChangeIfNeeded());
         testPlayer.finish();
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index e502f2f..d400a4c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -56,6 +56,7 @@
     private boolean mHasWallpaperBackground = false;
     private int mBlurRadius = 0;
     private float mDarkScrimAlpha = 0.5f;
+    private SurfaceControl mParentSurface = mock(SurfaceControl.class);
 
     @Before
     public void setUp() throws Exception {
@@ -63,7 +64,8 @@
         mLetterbox = new Letterbox(mSurfaces, StubTransaction::new,
                 () -> mAreCornersRounded, () -> Color.valueOf(mColor),
                 () -> mHasWallpaperBackground, () -> mBlurRadius, () -> mDarkScrimAlpha,
-                /* doubleTapCallbackX= */ x -> {}, /* doubleTapCallbackY= */ y -> {});
+                /* doubleTapCallbackX= */ x -> {}, /* doubleTapCallbackY= */ y -> {},
+                () -> mParentSurface);
         mTransaction = spy(StubTransaction.class);
     }
 
@@ -205,6 +207,22 @@
     }
 
     @Test
+    public void testNeedsApplySurfaceChanges_setParentSurface() {
+        mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
+        mLetterbox.applySurfaceChanges(mTransaction);
+
+        verify(mTransaction).reparent(mSurfaces.top, mParentSurface);
+        assertFalse(mLetterbox.needsApplySurfaceChanges());
+
+        mParentSurface = mock(SurfaceControl.class);
+
+        assertTrue(mLetterbox.needsApplySurfaceChanges());
+
+        mLetterbox.applySurfaceChanges(mTransaction);
+        verify(mTransaction).reparent(mSurfaces.top, mParentSurface);
+    }
+
+    @Test
     public void testApplySurfaceChanges_cornersNotRounded_surfaceFullWindowSurfaceNotCreated() {
         mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
         mLetterbox.applySurfaceChanges(mTransaction);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 88e58ea..8546763 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -281,7 +281,7 @@
         verify(mMockRunner).onAnimationCanceled(null /* taskIds */, null /* taskSnapshots */);
 
         // Simulate the app transition finishing
-        mController.mAppTransitionListener.onAppTransitionStartingLocked(0, 0);
+        mController.mAppTransitionListener.onAppTransitionStartingLocked(false, false, 0, 0, 0);
         verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 13da154..d2cb7ba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -310,11 +310,11 @@
     }
 
     @Override
-    public void startKeyguardExitAnimation(long startTime) {
+    public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
     }
 
     @Override
-    public int applyKeyguardOcclusionChange(boolean notify) {
+    public int applyKeyguardOcclusionChange(boolean keyguardOccludingStarted) {
         return 0;
     }
 
diff --git a/tests/FlickerTests/libs/window-extensions-release.aar b/tests/FlickerTests/libs/window-extensions-release.aar
index 6fc9a67..918e514 100644
--- a/tests/FlickerTests/libs/window-extensions-release.aar
+++ b/tests/FlickerTests/libs/window-extensions-release.aar
Binary files differ
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
index 3b201f9..e4add80 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
@@ -16,6 +16,9 @@
 
 package android.net.vcn.persistablebundleutils;
 
+import static android.net.vcn.persistablebundleutils.IkeSessionParamsUtils.IKE_OPTION_AUTOMATIC_ADDRESS_FAMILY_SELECTION;
+import static android.net.vcn.persistablebundleutils.IkeSessionParamsUtils.IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES;
+import static android.net.vcn.persistablebundleutils.IkeSessionParamsUtils.isIkeOptionValid;
 import static android.system.OsConstants.AF_INET;
 import static android.system.OsConstants.AF_INET6;
 import static android.telephony.TelephonyManager.APPTYPE_USIM;
@@ -134,15 +137,37 @@
         verifyPersistableBundleEncodeDecodeIsLossless(params);
     }
 
+    private static IkeSessionParams.Builder createBuilderMinimumWithEap() throws Exception {
+        final X509Certificate serverCaCert = createCertFromPemFile("self-signed-ca.pem");
+
+        final byte[] eapId = "test@android.net".getBytes(StandardCharsets.US_ASCII);
+        final int subId = 1;
+        final EapSessionConfig eapConfig =
+                new EapSessionConfig.Builder()
+                        .setEapIdentity(eapId)
+                        .setEapSimConfig(subId, APPTYPE_USIM)
+                        .setEapAkaConfig(subId, APPTYPE_USIM)
+                        .build();
+        return createBuilderMinimum().setAuthEap(serverCaCert, eapConfig);
+    }
+
     @Test
     public void testEncodeDecodeParamsWithIkeOptions() throws Exception {
-        final IkeSessionParams params =
-                createBuilderMinimum()
+        final IkeSessionParams.Builder builder =
+                createBuilderMinimumWithEap()
                         .addIkeOption(IkeSessionParams.IKE_OPTION_ACCEPT_ANY_REMOTE_ID)
+                        .addIkeOption(IkeSessionParams.IKE_OPTION_EAP_ONLY_AUTH)
                         .addIkeOption(IkeSessionParams.IKE_OPTION_MOBIKE)
+                        .addIkeOption(IkeSessionParams.IKE_OPTION_FORCE_PORT_4500)
                         .addIkeOption(IkeSessionParams.IKE_OPTION_INITIAL_CONTACT)
-                        .build();
-        verifyPersistableBundleEncodeDecodeIsLossless(params);
+                        .addIkeOption(IkeSessionParams.IKE_OPTION_REKEY_MOBILITY);
+        if (isIkeOptionValid(IKE_OPTION_AUTOMATIC_ADDRESS_FAMILY_SELECTION)) {
+            builder.addIkeOption(IKE_OPTION_AUTOMATIC_ADDRESS_FAMILY_SELECTION);
+        }
+        if (isIkeOptionValid(IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES)) {
+            builder.addIkeOption(IKE_OPTION_AUTOMATIC_NATT_KEEPALIVES);
+        }
+        verifyPersistableBundleEncodeDecodeIsLossless(builder.build());
     }
 
     private static InputStream openAssetsFile(String fileName) throws Exception {
@@ -176,19 +201,7 @@
 
     @Test
     public void testEncodeRecodeParamsWithEapAuth() throws Exception {
-        final X509Certificate serverCaCert = createCertFromPemFile("self-signed-ca.pem");
-
-        final byte[] eapId = "test@android.net".getBytes(StandardCharsets.US_ASCII);
-        final int subId = 1;
-        final EapSessionConfig eapConfig =
-                new EapSessionConfig.Builder()
-                        .setEapIdentity(eapId)
-                        .setEapSimConfig(subId, APPTYPE_USIM)
-                        .setEapAkaConfig(subId, APPTYPE_USIM)
-                        .build();
-
-        final IkeSessionParams params =
-                createBuilderMinimum().setAuthEap(serverCaCert, eapConfig).build();
+        final IkeSessionParams params = createBuilderMinimumWithEap().build();
         verifyPersistableBundleEncodeDecodeIsLossless(params);
     }
 }