Merge "API review changes to CallDiagnosticService" into sc-dev
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
index 29bcfe0..1bd90a8 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7197701"
     target: "CtsShim"
     source_file: "aosp_arm64/CtsShimPriv.apk"
   }
@@ -8,5 +8,5 @@
   version: ""
   version_group: ""
   git_project: "platform/frameworks/base"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
 }
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
index be172e6..544bca02 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__arm_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7197701"
     target: "CtsShim"
     source_file: "aosp_arm64/CtsShim.apk"
   }
@@ -8,5 +8,5 @@
   version: ""
   version_group: ""
   git_project: "platform/frameworks/base"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
 }
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
index 13eca13..72386bb 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShimPriv_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7197701"
     target: "CtsShim"
     source_file: "aosp_x86_64/CtsShimPriv.apk"
   }
@@ -8,5 +8,5 @@
   version: ""
   version_group: ""
   git_project: "platform/frameworks/base"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
 }
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
index 2e863fe..893eac2 100644
--- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
+++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__x86_CtsShim_apk.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "6508977"
+    build_id: "7197701"
     target: "CtsShim"
     source_file: "aosp_x86_64/CtsShim.apk"
   }
@@ -8,5 +8,5 @@
   version: ""
   version_group: ""
   git_project: "platform/frameworks/base"
-  git_branch: "rvc-dev"
+  git_branch: "sc-dev"
 }
diff --git a/Android.bp b/Android.bp
index 622b2c6..9690969 100644
--- a/Android.bp
+++ b/Android.bp
@@ -665,9 +665,8 @@
     ],
     required: [
         "framework-platform-compat-config",
-        // TODO: remove gps_debug, cec_config.xml and protolog.conf.json when the build system propagates "required" properly.
+        // TODO: remove gps_debug and protolog.conf.json when the build system propagates "required" properly.
         "gps_debug.conf",
-        "cec_config.xml",
         "icu4j-platform-compat-config",
         "libcore-platform-compat-config",
         "protolog.conf.json.gz",
diff --git a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
index 752c36e..6cdf585 100644
--- a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
+++ b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
@@ -65,7 +65,7 @@
      * @return package names the system has white-listed to opt out of power save restrictions,
      * except for device idle mode.
      *
-     * @hide Should be migrated to PowerWhitelistManager
+     * @hide Should be migrated to PowerExemptionManager
      */
     @TestApi
     public @NonNull String[] getSystemPowerWhitelistExceptIdle() {
@@ -80,7 +80,7 @@
      * @return package names the system has white-listed to opt out of power save restrictions for
      * all modes.
      *
-     * @hide Should be migrated to PowerWhitelistManager
+     * @hide Should be migrated to PowerExemptionManager
      */
     @TestApi
     public @NonNull String[] getSystemPowerWhitelist() {
diff --git a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
index 43d4873..9d18dfe 100644
--- a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
+++ b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl
@@ -42,7 +42,7 @@
     boolean isPowerSaveWhitelistExceptIdleApp(String name);
     boolean isPowerSaveWhitelistApp(String name);
     @UnsupportedAppUsage(maxTargetSdk = 30,
-     publicAlternatives = "Use SystemApi {@code PowerWhitelistManager#whitelistAppTemporarily(String, int, String)}.")
+     publicAlternatives = "Use SystemApi {@code PowerExemptionManager#addToTemporaryAllowList(String, int, int, String)}.")
     void addPowerSaveTempWhitelistApp(String name, long duration, int userId, int reasonCode, String reason);
     long addPowerSaveTempWhitelistAppForMms(String name, int userId, int reasonCode, String reason);
     long addPowerSaveTempWhitelistAppForSms(String name, int userId, int reasonCode, String reason);
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index 8445335..d9a49aa 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -170,7 +170,7 @@
     /** @hide */
     public static final int REASON_EXEMPTED_PACKAGE = 64;
     /** @hide */
-    public static final int REASON_ALLOWLISTED_PACKAGE  = 65;
+    public static final int REASON_ALLOWLISTED_PACKAGE = 65;
     /** @hide */
     public static final int REASON_APPOP = 66;
 
@@ -193,6 +193,10 @@
      * Set temp-allow-list for activity recognition.
      */
     public static final int REASON_ACTIVITY_RECOGNITION = 103;
+    /**
+     * Set temp-allow-list for transferring accounts between users.
+     */
+    public static final int REASON_ACCOUNT_TRANSFER = 104;
 
     /* Reason code range 200-299 are reserved for broadcast actions */
     /**
@@ -216,7 +220,7 @@
      * Device idle system allow list, including EXCEPT-IDLE
      * @hide
      */
-    public static final int REASON_SYSTEM_ALLOW_LISTED  = 300;
+    public static final int REASON_SYSTEM_ALLOW_LISTED = 300;
     /** @hide */
     public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = 301;
     /**
@@ -329,6 +333,7 @@
             REASON_PUSH_MESSAGING,
             REASON_PUSH_MESSAGING_OVER_QUOTA,
             REASON_ACTIVITY_RECOGNITION,
+            REASON_ACCOUNT_TRANSFER,
             REASON_BOOT_COMPLETED,
             REASON_PRE_BOOT_COMPLETED,
             REASON_LOCKED_BOOT_COMPLETED,
@@ -579,6 +584,8 @@
                 return "PUSH_MESSAGING_OVER_QUOTA";
             case REASON_ACTIVITY_RECOGNITION:
                 return "ACTIVITY_RECOGNITION";
+            case REASON_ACCOUNT_TRANSFER:
+                return "REASON_ACCOUNT_TRANSFER";
             case REASON_BOOT_COMPLETED:
                 return "BOOT_COMPLETED";
             case REASON_PRE_BOOT_COMPLETED:
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index b1b733a..eba39c7 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -16,13 +16,6 @@
 
 package android.os;
 
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
-import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
-import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
-import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT;
-import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
-import static android.app.ActivityManager.PROCESS_STATE_TOP;
-
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -33,7 +26,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Collections;
 import java.util.List;
 
 /**
@@ -43,9 +35,11 @@
  * placed on the temporary whitelist are removed from that whitelist after a predetermined amount of
  * time.
  *
+ * @deprecated Use {@link PowerExemptionManager} instead
  * @hide
  */
 @SystemApi
+@Deprecated
 @SystemService(Context.POWER_WHITELIST_MANAGER)
 public class PowerWhitelistManager {
     private final Context mContext;
@@ -53,21 +47,23 @@
     // TODO: migrate to PowerWhitelistController
     private final IDeviceIdleController mService;
 
+    private final PowerExemptionManager mPowerExemptionManager;
+
     /**
      * Indicates that an unforeseen event has occurred and the app should be whitelisted to handle
      * it.
      */
-    public static final int EVENT_UNSPECIFIED = 0;
+    public static final int EVENT_UNSPECIFIED = PowerExemptionManager.EVENT_UNSPECIFIED;
 
     /**
      * Indicates that an SMS event has occurred and the app should be whitelisted to handle it.
      */
-    public static final int EVENT_SMS = 1;
+    public static final int EVENT_SMS = PowerExemptionManager.EVENT_SMS;
 
     /**
      * Indicates that an MMS event has occurred and the app should be whitelisted to handle it.
      */
-    public static final int EVENT_MMS = 2;
+    public static final int EVENT_MMS = PowerExemptionManager.EVENT_MMS;
 
     /**
      * @hide
@@ -84,12 +80,14 @@
     /**
      * Allow the temp allowlist behavior, plus allow foreground service start from background.
      */
-    public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0;
+    public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED =
+            PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
     /**
      * Only allow the temp allowlist behavior, not allow foreground service start from
      * background.
      */
-    public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1;
+    public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED =
+            PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
 
     /**
      * The list of temp allowlist types.
@@ -107,73 +105,83 @@
      * BG-FGS-launch is denied.
      * @hide
      */
-    public static final int REASON_DENIED = -1;
+    public static final int REASON_DENIED = PowerExemptionManager.REASON_DENIED;
 
     /* Reason code range 0-9 are reserved for default reasons */
     /**
      * The default reason code if reason is unknown.
      */
-    public static final int REASON_UNKNOWN = 0;
+    public static final int REASON_UNKNOWN = PowerExemptionManager.REASON_UNKNOWN;
     /**
      * Use REASON_OTHER if there is no better choice.
      */
-    public static final int REASON_OTHER = 1;
+    public static final int REASON_OTHER = PowerExemptionManager.REASON_OTHER;
 
     /* Reason code range 10-49 are reserved for BG-FGS-launch allowed proc states */
     /** @hide */
-    public static final int REASON_PROC_STATE_PERSISTENT = 10;
+    public static final int REASON_PROC_STATE_PERSISTENT =
+            PowerExemptionManager.REASON_PROC_STATE_PERSISTENT;
     /** @hide */
-    public static final int REASON_PROC_STATE_PERSISTENT_UI = 11;
+    public static final int REASON_PROC_STATE_PERSISTENT_UI =
+            PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI;
     /** @hide */
-    public static final int REASON_PROC_STATE_TOP = 12;
+    public static final int REASON_PROC_STATE_TOP = PowerExemptionManager.REASON_PROC_STATE_TOP;
     /** @hide */
-    public static final int REASON_PROC_STATE_BTOP = 13;
+    public static final int REASON_PROC_STATE_BTOP = PowerExemptionManager.REASON_PROC_STATE_BTOP;
     /** @hide */
-    public static final int REASON_PROC_STATE_FGS = 14;
+    public static final int REASON_PROC_STATE_FGS = PowerExemptionManager.REASON_PROC_STATE_FGS;
     /** @hide */
-    public static final int REASON_PROC_STATE_BFGS = 15;
+    public static final int REASON_PROC_STATE_BFGS = PowerExemptionManager.REASON_PROC_STATE_BFGS;
 
     /* Reason code range 50-99 are reserved for BG-FGS-launch allowed reasons */
     /** @hide */
-    public static final int REASON_UID_VISIBLE = 50;
+    public static final int REASON_UID_VISIBLE = PowerExemptionManager.REASON_UID_VISIBLE;
     /** @hide */
-    public static final int REASON_SYSTEM_UID = 51;
+    public static final int REASON_SYSTEM_UID = PowerExemptionManager.REASON_SYSTEM_UID;
     /** @hide */
-    public static final int REASON_ACTIVITY_STARTER = 52;
+    public static final int REASON_ACTIVITY_STARTER = PowerExemptionManager.REASON_ACTIVITY_STARTER;
     /** @hide */
-    public static final int REASON_START_ACTIVITY_FLAG = 53;
+    public static final int REASON_START_ACTIVITY_FLAG =
+            PowerExemptionManager.REASON_START_ACTIVITY_FLAG;
     /** @hide */
-    public static final int REASON_FGS_BINDING = 54;
+    public static final int REASON_FGS_BINDING = PowerExemptionManager.REASON_FGS_BINDING;
     /** @hide */
-    public static final int REASON_DEVICE_OWNER = 55;
+    public static final int REASON_DEVICE_OWNER = PowerExemptionManager.REASON_DEVICE_OWNER;
     /** @hide */
-    public static final int REASON_PROFILE_OWNER = 56;
+    public static final int REASON_PROFILE_OWNER = PowerExemptionManager.REASON_PROFILE_OWNER;
     /** @hide */
-    public static final int REASON_COMPANION_DEVICE_MANAGER = 57;
+    public static final int REASON_COMPANION_DEVICE_MANAGER =
+            PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER;
     /**
      * START_ACTIVITIES_FROM_BACKGROUND permission.
      * @hide
      */
-    public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION = 58;
+    public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION =
+            PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
     /**
      * START_FOREGROUND_SERVICES_FROM_BACKGROUND permission.
      * @hide
      */
-    public static final int REASON_BACKGROUND_FGS_PERMISSION = 59;
+    public static final int REASON_BACKGROUND_FGS_PERMISSION =
+            PowerExemptionManager.REASON_BACKGROUND_FGS_PERMISSION;
     /** @hide */
-    public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 60;
+    public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION =
+            PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
     /** @hide */
-    public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION = 61;
+    public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION =
+            PowerExemptionManager.REASON_INSTR_BACKGROUND_FGS_PERMISSION;
     /** @hide */
-    public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION = 62;
+    public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION =
+            PowerExemptionManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
     /** @hide */
-    public static final int REASON_DEVICE_DEMO_MODE = 63;
+    public static final int REASON_DEVICE_DEMO_MODE = PowerExemptionManager.REASON_DEVICE_DEMO_MODE;
     /** @hide */
-    public static final int REASON_EXEMPTED_PACKAGE = 64;
+    public static final int REASON_EXEMPTED_PACKAGE = PowerExemptionManager.REASON_EXEMPTED_PACKAGE;
     /** @hide */
-    public static final int REASON_ALLOWLISTED_PACKAGE  = 65;
+    public static final int REASON_ALLOWLISTED_PACKAGE =
+            PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE;
     /** @hide */
-    public static final int REASON_APPOP = 66;
+    public static final int REASON_APPOP = PowerExemptionManager.REASON_APPOP;
 
     /* BG-FGS-launch is allowed by temp-allowlist or system-allowlist.
        Reason code for temp and system allowlist starts here.
@@ -181,117 +189,128 @@
     /**
      * Set temp-allowlist for location geofence purpose.
      */
-    public static final int REASON_GEOFENCING = 100;
+    public static final int REASON_GEOFENCING = PowerExemptionManager.REASON_GEOFENCING;
     /**
      * Set temp-allowlist for server push messaging.
      */
-    public static final int REASON_PUSH_MESSAGING = 101;
+    public static final int REASON_PUSH_MESSAGING = PowerExemptionManager.REASON_PUSH_MESSAGING;
     /**
      * Set temp-allowlist for server push messaging over the quota.
      */
-    public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102;
+    public static final int REASON_PUSH_MESSAGING_OVER_QUOTA =
+            PowerExemptionManager.REASON_PUSH_MESSAGING_OVER_QUOTA;
     /**
      * Set temp-allowlist for activity recognition.
      */
-    public static final int REASON_ACTIVITY_RECOGNITION = 103;
+    public static final int REASON_ACTIVITY_RECOGNITION =
+            PowerExemptionManager.REASON_ACTIVITY_RECOGNITION;
 
     /* Reason code range 200-299 are reserved for broadcast actions */
     /**
      * Broadcast ACTION_BOOT_COMPLETED.
      * @hide
      */
-    public static final int REASON_BOOT_COMPLETED = 200;
+    public static final int REASON_BOOT_COMPLETED = PowerExemptionManager.REASON_BOOT_COMPLETED;
     /**
      * Broadcast ACTION_PRE_BOOT_COMPLETED.
      * @hide
      */
-    public static final int REASON_PRE_BOOT_COMPLETED = 201;
+    public static final int REASON_PRE_BOOT_COMPLETED =
+            PowerExemptionManager.REASON_PRE_BOOT_COMPLETED;
     /**
      * Broadcast ACTION_LOCKED_BOOT_COMPLETED.
      * @hide
      */
-    public static final int REASON_LOCKED_BOOT_COMPLETED = 202;
+    public static final int REASON_LOCKED_BOOT_COMPLETED =
+            PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED;
 
     /* Reason code range 300-399 are reserved for other internal reasons */
     /**
      * Device idle system allowlist, including EXCEPT-IDLE
      * @hide
      */
-    public static final int REASON_SYSTEM_ALLOW_LISTED  = 300;
+    public static final int REASON_SYSTEM_ALLOW_LISTED =
+            PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED;
     /** @hide */
-    public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = 301;
+    public static final int REASON_ALARM_MANAGER_ALARM_CLOCK =
+            PowerExemptionManager.REASON_ALARM_MANAGER_ALARM_CLOCK;
     /**
      * AlarmManagerService.
      * @hide
      */
-    public static final int REASON_ALARM_MANAGER_WHILE_IDLE = 302;
+    public static final int REASON_ALARM_MANAGER_WHILE_IDLE =
+            PowerExemptionManager.REASON_ALARM_MANAGER_WHILE_IDLE;
     /**
      * ActiveServices.
      * @hide
      */
-    public static final int REASON_SERVICE_LAUNCH = 303;
+    public static final int REASON_SERVICE_LAUNCH = PowerExemptionManager.REASON_SERVICE_LAUNCH;
     /**
      * KeyChainSystemService.
      * @hide
      */
-    public static final int REASON_KEY_CHAIN = 304;
+    public static final int REASON_KEY_CHAIN = PowerExemptionManager.REASON_KEY_CHAIN;
     /**
      * PackageManagerService.
      * @hide
      */
-    public static final int REASON_PACKAGE_VERIFIER = 305;
+    public static final int REASON_PACKAGE_VERIFIER = PowerExemptionManager.REASON_PACKAGE_VERIFIER;
     /**
      * SyncManager.
      * @hide
      */
-    public static final int REASON_SYNC_MANAGER = 306;
+    public static final int REASON_SYNC_MANAGER = PowerExemptionManager.REASON_SYNC_MANAGER;
     /**
      * DomainVerificationProxyV1.
      * @hide
      */
-    public static final int REASON_DOMAIN_VERIFICATION_V1 = 307;
+    public static final int REASON_DOMAIN_VERIFICATION_V1 =
+            PowerExemptionManager.REASON_DOMAIN_VERIFICATION_V1;
     /**
      * DomainVerificationProxyV2.
      * @hide
      */
-    public static final int REASON_DOMAIN_VERIFICATION_V2 = 308;
+    public static final int REASON_DOMAIN_VERIFICATION_V2 =
+            PowerExemptionManager.REASON_DOMAIN_VERIFICATION_V2;
     /** @hide */
     public static final int REASON_VPN = 309;
     /**
      * NotificationManagerService.
      * @hide
      */
-    public static final int REASON_NOTIFICATION_SERVICE = 310;
+    public static final int REASON_NOTIFICATION_SERVICE =
+            PowerExemptionManager.REASON_NOTIFICATION_SERVICE;
     /**
      * Broadcast ACTION_MY_PACKAGE_REPLACED.
      * @hide
      */
-    public static final int REASON_PACKAGE_REPLACED = 311;
+    public static final int REASON_PACKAGE_REPLACED = PowerExemptionManager.REASON_PACKAGE_REPLACED;
     /**
      * LocationProviderManager.
      * @hide
      */
-    public static final int REASON_LOCATION_PROVIDER = 312;
+    public static final int REASON_LOCATION_PROVIDER =
+            PowerExemptionManager.REASON_LOCATION_PROVIDER;
     /**
      * MediaButtonReceiver.
      * @hide
      */
-    public static final int REASON_MEDIA_BUTTON = 313;
+    public static final int REASON_MEDIA_BUTTON = PowerExemptionManager.REASON_MEDIA_BUTTON;
     /**
      * InboundSmsHandler.
      * @hide
      */
-    public static final int REASON_EVENT_SMS = 314;
+    public static final int REASON_EVENT_SMS = PowerExemptionManager.REASON_EVENT_SMS;
     /**
      * InboundSmsHandler.
      * @hide
      */
-    public static final int REASON_EVENT_MMS = 315;
+    public static final int REASON_EVENT_MMS = PowerExemptionManager.REASON_EVENT_MMS;
     /**
      * Shell app.
      * @hide
      */
-    public static final int REASON_SHELL = 316;
+    public static final int REASON_SHELL = PowerExemptionManager.REASON_SHELL;
 
     /**
      * The list of BG-FGS-Launch and temp-allowlist reason code.
@@ -360,26 +379,29 @@
     public PowerWhitelistManager(@NonNull Context context) {
         mContext = context;
         mService = context.getSystemService(DeviceIdleManager.class).getService();
+        mPowerExemptionManager = context.getSystemService(PowerExemptionManager.class);
     }
 
     /**
      * Add the specified package to the permanent power save whitelist.
+     *
+     * @deprecated Use {@link PowerExemptionManager#addToPermanentAllowList(String)} instead
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
     public void addToWhitelist(@NonNull String packageName) {
-        addToWhitelist(Collections.singletonList(packageName));
+        mPowerExemptionManager.addToPermanentAllowList(packageName);
     }
 
     /**
      * Add the specified packages to the permanent power save whitelist.
+     *
+     * @deprecated Use {@link PowerExemptionManager#addToPermanentAllowList(List)} instead
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
     public void addToWhitelist(@NonNull List<String> packageNames) {
-        try {
-            mService.addPowerSaveWhitelistApps(packageNames);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        mPowerExemptionManager.addToPermanentAllowList(packageNames);
     }
 
     /**
@@ -388,19 +410,13 @@
      *
      * @param includingIdle Set to true if the app should be whitelisted from device idle as well
      *                      as other power save restrictions
+     * @deprecated Use {@link PowerExemptionManager#getAllowListedAppIds(boolean)} instead
      * @hide
      */
+    @Deprecated
     @NonNull
     public int[] getWhitelistedAppIds(boolean includingIdle) {
-        try {
-            if (includingIdle) {
-                return mService.getAppIdWhitelist();
-            } else {
-                return mService.getAppIdWhitelistExceptIdle();
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return mPowerExemptionManager.getAllowListedAppIds(includingIdle);
     }
 
     /**
@@ -409,18 +425,12 @@
      *
      * @param includingIdle Set to true if the app should be whitelisted from device
      *                      idle as well as other power save restrictions
+     * @deprecated Use {@link PowerExemptionManager#isAllowListed(String, boolean)} instead
      * @hide
      */
+    @Deprecated
     public boolean isWhitelisted(@NonNull String packageName, boolean includingIdle) {
-        try {
-            if (includingIdle) {
-                return mService.isPowerSaveWhitelistApp(packageName);
-            } else {
-                return mService.isPowerSaveWhitelistExceptIdleApp(packageName);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return mPowerExemptionManager.isAllowListed(packageName, includingIdle);
     }
 
     /**
@@ -429,14 +439,12 @@
      * whitelisted by default by the system cannot be removed.
      *
      * @param packageName The app to remove from the whitelist
+     * @deprecated Use {@link PowerExemptionManager#removeFromAllowList(String)} instead
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
     public void removeFromWhitelist(@NonNull String packageName) {
-        try {
-            mService.removePowerSaveWhitelistApp(packageName);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        mPowerExemptionManager.removeFromAllowList(packageName);
     }
 
     /**
@@ -446,16 +454,14 @@
      * @param durationMs  How long to keep the app on the temp whitelist for (in milliseconds)
      * @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure.
      * @param reason a optional human readable reason string, could be null or empty string.
+     * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowList(
+     *             String, long, int, String)} instead
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
     public void whitelistAppTemporarily(@NonNull String packageName, long durationMs,
             @ReasonCode int reasonCode, @Nullable String reason) {
-        try {
-            mService.addPowerSaveTempWhitelistApp(packageName, durationMs, mContext.getUserId(),
-                    reasonCode, reason);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        mPowerExemptionManager.addToTemporaryAllowList(packageName, durationMs, reasonCode, reason);
     }
 
     /**
@@ -463,12 +469,14 @@
      *
      * @param packageName The package to add to the temp whitelist
      * @param durationMs  How long to keep the app on the temp whitelist for (in milliseconds)
-     * @deprecated Use {@link #whitelistAppTemporarily(String, long, int, String)} instead
+     * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowList(
+     *             String, long, int, String)} instead
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
     public void whitelistAppTemporarily(@NonNull String packageName, long durationMs) {
-        whitelistAppTemporarily(packageName, durationMs, REASON_UNKNOWN, packageName);
+        mPowerExemptionManager.addToTemporaryAllowList(
+                packageName, durationMs, REASON_UNKNOWN, packageName);
     }
 
     /**
@@ -481,13 +489,15 @@
      * @param reason      A human-readable reason explaining why the app is temp whitelisted. Only
      *                    used for logging purposes. Could be null or empty string.
      * @return The duration (in milliseconds) that the app is whitelisted for
-     * @deprecated Use {@link #whitelistAppTemporarilyForEvent(String, int, int, String)} instead
+     * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowListForEvent(
+     *             String, int, int, String)} instead
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
     public long whitelistAppTemporarilyForEvent(@NonNull String packageName,
             @WhitelistEvent int event, @Nullable String reason) {
-        return whitelistAppTemporarilyForEvent(packageName, event, REASON_UNKNOWN, reason);
+        return mPowerExemptionManager.addToTemporaryAllowListForEvent(
+                packageName, event, REASON_UNKNOWN, reason);
     }
 
     /**
@@ -501,47 +511,25 @@
      * @param reason      A human-readable reason explaining why the app is temp whitelisted. Only
      *                    used for logging purposes. Could be null or empty string.
      * @return The duration (in milliseconds) that the app is whitelisted for
+     * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowListForEvent(
+     *             String, int, int, String)} instead
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST)
     public long whitelistAppTemporarilyForEvent(@NonNull String packageName,
             @WhitelistEvent int event, @ReasonCode int reasonCode, @Nullable String reason) {
-        try {
-            switch (event) {
-                case EVENT_MMS:
-                    return mService.addPowerSaveTempWhitelistAppForMms(
-                            packageName, mContext.getUserId(), reasonCode, reason);
-                case EVENT_SMS:
-                    return mService.addPowerSaveTempWhitelistAppForSms(
-                            packageName, mContext.getUserId(), reasonCode, reason);
-                case EVENT_UNSPECIFIED:
-                default:
-                    return mService.whitelistAppTemporarily(
-                            packageName, mContext.getUserId(), reasonCode, reason);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return mPowerExemptionManager.addToTemporaryAllowListForEvent(
+                packageName, event, reasonCode, reason);
     }
 
     /**
      * @hide
+     *
+     * @deprecated Use {@link PowerExemptionManager#getReasonCodeFromProcState(int)} instead
      */
+    @Deprecated
     public static @ReasonCode int getReasonCodeFromProcState(int procState) {
-        if (procState <= PROCESS_STATE_PERSISTENT) {
-            return REASON_PROC_STATE_PERSISTENT;
-        } else if (procState <= PROCESS_STATE_PERSISTENT_UI) {
-            return REASON_PROC_STATE_PERSISTENT_UI;
-        } else if (procState <= PROCESS_STATE_TOP) {
-            return REASON_PROC_STATE_TOP;
-        } else if (procState <= PROCESS_STATE_BOUND_TOP) {
-            return REASON_PROC_STATE_BTOP;
-        } else if (procState <= PROCESS_STATE_FOREGROUND_SERVICE) {
-            return REASON_PROC_STATE_FGS;
-        } else if (procState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
-            return REASON_PROC_STATE_BFGS;
-        } else {
-            return REASON_DENIED;
-        }
+        return PowerExemptionManager.getReasonCodeFromProcState(procState);
     }
 
     /**
@@ -549,111 +537,10 @@
      * @hide
      * @param reasonCode
      * @return string name of the reason code.
+     * @deprecated Use {@link PowerExemptionManager#reasonCodeToString(int)} instead
      */
+    @Deprecated
     public static String reasonCodeToString(@ReasonCode int reasonCode) {
-        switch (reasonCode) {
-            case REASON_DENIED:
-                return "DENIED";
-            case REASON_UNKNOWN:
-                return "UNKNOWN";
-            case REASON_OTHER:
-                return "OTHER";
-            case REASON_PROC_STATE_PERSISTENT:
-                return "PROC_STATE_PERSISTENT";
-            case REASON_PROC_STATE_PERSISTENT_UI:
-                return "PROC_STATE_PERSISTENT_UI";
-            case REASON_PROC_STATE_TOP:
-                return "PROC_STATE_TOP";
-            case REASON_PROC_STATE_BTOP:
-                return "PROC_STATE_BTOP";
-            case REASON_PROC_STATE_FGS:
-                return "PROC_STATE_FGS";
-            case REASON_PROC_STATE_BFGS:
-                return "PROC_STATE_BFGS";
-            case REASON_UID_VISIBLE:
-                return "UID_VISIBLE";
-            case REASON_SYSTEM_UID:
-                return "SYSTEM_UID";
-            case REASON_ACTIVITY_STARTER:
-                return "ACTIVITY_STARTER";
-            case REASON_START_ACTIVITY_FLAG:
-                return "START_ACTIVITY_FLAG";
-            case REASON_FGS_BINDING:
-                return "FGS_BINDING";
-            case REASON_DEVICE_OWNER:
-                return "DEVICE_OWNER";
-            case REASON_PROFILE_OWNER:
-                return "PROFILE_OWNER";
-            case REASON_COMPANION_DEVICE_MANAGER:
-                return "COMPANION_DEVICE_MANAGER";
-            case REASON_BACKGROUND_ACTIVITY_PERMISSION:
-                return "BACKGROUND_ACTIVITY_PERMISSION";
-            case REASON_BACKGROUND_FGS_PERMISSION:
-                return "BACKGROUND_FGS_PERMISSION";
-            case REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION:
-                return "INSTR_BACKGROUND_ACTIVITY_PERMISSION";
-            case REASON_INSTR_BACKGROUND_FGS_PERMISSION:
-                return "INSTR_BACKGROUND_FGS_PERMISSION";
-            case REASON_SYSTEM_ALERT_WINDOW_PERMISSION:
-                return "SYSTEM_ALERT_WINDOW_PERMISSION";
-            case REASON_DEVICE_DEMO_MODE:
-                return "DEVICE_DEMO_MODE";
-            case REASON_EXEMPTED_PACKAGE:
-                return "EXEMPTED_PACKAGE";
-            case REASON_ALLOWLISTED_PACKAGE:
-                return "ALLOWLISTED_PACKAGE";
-            case REASON_APPOP:
-                return "APPOP";
-            case REASON_GEOFENCING:
-                return "GEOFENCING";
-            case REASON_PUSH_MESSAGING:
-                return "PUSH_MESSAGING";
-            case REASON_PUSH_MESSAGING_OVER_QUOTA:
-                return "PUSH_MESSAGING_OVER_QUOTA";
-            case REASON_ACTIVITY_RECOGNITION:
-                return "ACTIVITY_RECOGNITION";
-            case REASON_BOOT_COMPLETED:
-                return "BOOT_COMPLETED";
-            case REASON_PRE_BOOT_COMPLETED:
-                return "PRE_BOOT_COMPLETED";
-            case REASON_LOCKED_BOOT_COMPLETED:
-                return "LOCKED_BOOT_COMPLETED";
-            case REASON_SYSTEM_ALLOW_LISTED:
-                return "SYSTEM_ALLOW_LISTED";
-            case REASON_ALARM_MANAGER_ALARM_CLOCK:
-                return "ALARM_MANAGER_ALARM_CLOCK";
-            case REASON_ALARM_MANAGER_WHILE_IDLE:
-                return "ALARM_MANAGER_WHILE_IDLE";
-            case REASON_SERVICE_LAUNCH:
-                return "SERVICE_LAUNCH";
-            case REASON_KEY_CHAIN:
-                return "KEY_CHAIN";
-            case REASON_PACKAGE_VERIFIER:
-                return "PACKAGE_VERIFIER";
-            case REASON_SYNC_MANAGER:
-                return "SYNC_MANAGER";
-            case REASON_DOMAIN_VERIFICATION_V1:
-                return "DOMAIN_VERIFICATION_V1";
-            case REASON_DOMAIN_VERIFICATION_V2:
-                return "DOMAIN_VERIFICATION_V2";
-            case REASON_VPN:
-                return "VPN";
-            case REASON_NOTIFICATION_SERVICE:
-                return "NOTIFICATION_SERVICE";
-            case REASON_PACKAGE_REPLACED:
-                return "PACKAGE_REPLACED";
-            case REASON_LOCATION_PROVIDER:
-                return "LOCATION_PROVIDER";
-            case REASON_MEDIA_BUTTON:
-                return "MEDIA_BUTTON";
-            case REASON_EVENT_SMS:
-                return "EVENT_SMS";
-            case REASON_EVENT_MMS:
-                return "EVENT_MMS";
-            case REASON_SHELL:
-                return "SHELL";
-            default:
-                return  "(unknown:" + reasonCode + ")";
-        }
+        return PowerExemptionManager.reasonCodeToString(reasonCode);
     }
 }
diff --git a/config/preloaded-classes b/config/preloaded-classes
index c6ec376..7c3fd8c 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -1844,11 +1844,9 @@
 android.ddm.DdmHandleAppName$Names
 android.ddm.DdmHandleAppName
 android.ddm.DdmHandleExit
-android.ddm.DdmHandleHeap
 android.ddm.DdmHandleHello
 android.ddm.DdmHandleNativeHeap
 android.ddm.DdmHandleProfiling
-android.ddm.DdmHandleThread
 android.ddm.DdmHandleViewDebug
 android.ddm.DdmRegister
 android.debug.AdbManager
diff --git a/core/api/current.txt b/core/api/current.txt
index 54aa356..4a1c325 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -329,6 +329,7 @@
     field public static final int apiKey = 16843281; // 0x1010211
     field public static final int appCategory = 16844101; // 0x1010545
     field public static final int appComponentFactory = 16844154; // 0x101057a
+    field public static final int attributionTags = 16844353; // 0x1010641
     field public static final int author = 16843444; // 0x10102b4
     field public static final int authorities = 16842776; // 0x1010018
     field public static final int autoAdvanceViewId = 16843535; // 0x101030f
@@ -8490,7 +8491,7 @@
     field public static final int WIDGET_CATEGORY_HOME_SCREEN = 1; // 0x1
     field public static final int WIDGET_CATEGORY_KEYGUARD = 2; // 0x2
     field public static final int WIDGET_CATEGORY_SEARCHBOX = 4; // 0x4
-    field public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 3; // 0x3
+    field public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 4; // 0x4
     field public static final int WIDGET_FEATURE_HIDE_FROM_PICKER = 2; // 0x2
     field public static final int WIDGET_FEATURE_RECONFIGURABLE = 1; // 0x1
     field public int autoAdvanceViewId;
@@ -10500,6 +10501,7 @@
     field public static final String DEVICE_POLICY_SERVICE = "device_policy";
     field public static final String DISPLAY_HASH_SERVICE = "display_hash";
     field public static final String DISPLAY_SERVICE = "display";
+    field public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
     field public static final String DOWNLOAD_SERVICE = "download";
     field public static final String DROPBOX_SERVICE = "dropbox";
     field public static final String EUICC_SERVICE = "euicc";
@@ -26137,10 +26139,6 @@
     ctor public NetworkSpecifier();
   }
 
-  public class ParseException extends java.lang.RuntimeException {
-    field public String response;
-  }
-
   public abstract class PlatformVpnProfile {
     method public final int getType();
     method @NonNull public final String getTypeString();
@@ -30427,6 +30425,7 @@
     field public static final String BASE_OS;
     field public static final String CODENAME;
     field public static final String INCREMENTAL;
+    field public static final int MEDIA_PERFORMANCE_CLASS;
     field public static final int PREVIEW_SDK_INT;
     field public static final String RELEASE;
     field @NonNull public static final String RELEASE_OR_CODENAME;
@@ -39601,22 +39600,22 @@
     method public final void conferenceRemoteConnections(android.telecom.RemoteConnection, android.telecom.RemoteConnection);
     method public final void connectionServiceFocusReleased();
     method @Nullable public final android.telecom.RemoteConference createRemoteIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
-    method public final android.telecom.RemoteConnection createRemoteIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method @Nullable public final android.telecom.RemoteConnection createRemoteIncomingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
     method @Nullable public final android.telecom.RemoteConference createRemoteOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
-    method public final android.telecom.RemoteConnection createRemoteOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method @Nullable public final android.telecom.RemoteConnection createRemoteOutgoingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
     method public final java.util.Collection<android.telecom.Conference> getAllConferences();
     method public final java.util.Collection<android.telecom.Connection> getAllConnections();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConference(android.telecom.Connection, android.telecom.Connection);
     method public void onConnectionServiceFocusGained();
     method public void onConnectionServiceFocusLost();
-    method @Nullable public android.telecom.Conference onCreateIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
+    method @Nullable public android.telecom.Conference onCreateIncomingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
     method public void onCreateIncomingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
-    method @Nullable public android.telecom.Conference onCreateOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
-    method public void onCreateOutgoingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
+    method @Nullable public android.telecom.Conference onCreateOutgoingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
+    method public void onCreateOutgoingConferenceFailed(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
@@ -40614,14 +40613,6 @@
   public static final class CarrierConfigManager.Iwlan {
     field public static final int AUTHENTICATION_METHOD_CERT = 1; // 0x1
     field public static final int AUTHENTICATION_METHOD_EAP_ONLY = 0; // 0x0
-    field public static final int DH_GROUP_1024_BIT_MODP = 2; // 0x2
-    field public static final int DH_GROUP_1536_BIT_MODP = 5; // 0x5
-    field public static final int DH_GROUP_2048_BIT_MODP = 14; // 0xe
-    field public static final int DH_GROUP_3072_BIT_MODP = 15; // 0xf
-    field public static final int DH_GROUP_4096_BIT_MODP = 16; // 0x10
-    field public static final int DH_GROUP_NONE = 0; // 0x0
-    field public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12; // 0xc
-    field public static final int ENCRYPTION_ALGORITHM_AES_CTR = 13; // 0xd
     field public static final int EPDG_ADDRESS_CELLULAR_LOC = 3; // 0x3
     field public static final int EPDG_ADDRESS_PCO = 2; // 0x2
     field public static final int EPDG_ADDRESS_PLMN = 1; // 0x1
@@ -40629,12 +40620,6 @@
     field public static final int ID_TYPE_FQDN = 2; // 0x2
     field public static final int ID_TYPE_KEY_ID = 11; // 0xb
     field public static final int ID_TYPE_RFC822_ADDR = 3; // 0x3
-    field public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5; // 0x5
-    field public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2; // 0x2
-    field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12; // 0xc
-    field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13; // 0xd
-    field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14; // 0xe
-    field public static final int INTEGRITY_ALGORITHM_NONE = 0; // 0x0
     field public static final String KEY_ADD_KE_TO_CHILD_SESSION_REKEY_BOOL = "iwlan.add_ke_to_child_session_rekey_bool";
     field public static final String KEY_CHILD_SA_REKEY_HARD_TIMER_SEC_INT = "iwlan.child_sa_rekey_hard_timer_sec_int";
     field public static final String KEY_CHILD_SA_REKEY_SOFT_TIMER_SEC_INT = "iwlan.child_sa_rekey_soft_timer_sec_int";
@@ -40654,10 +40639,6 @@
     field public static final String KEY_IKE_REMOTE_ID_TYPE_INT = "iwlan.ike_remote_id_type_int";
     field public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_encryption_aes_cbc_key_size_int_array";
     field public static final String KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY = "iwlan.ike_session_encryption_aes_ctr_key_size_int_array";
-    field public static final int KEY_LEN_AES_128 = 128; // 0x80
-    field public static final int KEY_LEN_AES_192 = 192; // 0xc0
-    field public static final int KEY_LEN_AES_256 = 256; // 0x100
-    field public static final int KEY_LEN_UNUSED = 0; // 0x0
     field public static final String KEY_MAX_RETRIES_INT = "iwlan.max_retries_int";
     field public static final String KEY_MCC_MNCS_STRING_ARRAY = "iwlan.mcc_mncs_string_array";
     field public static final String KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT = "iwlan.natt_keep_alive_timer_sec_int";
@@ -40667,11 +40648,6 @@
     field public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY = "iwlan.supported_ike_session_encryption_algorithms_int_array";
     field public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY = "iwlan.supported_integrity_algorithms_int_array";
     field public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY = "iwlan.supported_prf_algorithms_int_array";
-    field public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4; // 0x4
-    field public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; // 0x2
-    field public static final int PSEUDORANDOM_FUNCTION_SHA2_256 = 5; // 0x5
-    field public static final int PSEUDORANDOM_FUNCTION_SHA2_384 = 6; // 0x6
-    field public static final int PSEUDORANDOM_FUNCTION_SHA2_512 = 7; // 0x7
   }
 
   public abstract class CellIdentity implements android.os.Parcelable {
@@ -42040,6 +42016,7 @@
     method public static int getDefaultSmsSubscriptionId();
     method public static int getDefaultSubscriptionId();
     method public static int getDefaultVoiceSubscriptionId();
+    method public int getDeviceToDeviceStatusSharing(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions();
     method public static int getSlotIndex(int);
     method @Nullable public int[] getSubscriptionIds(int);
@@ -42052,6 +42029,7 @@
     method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
     method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void removeSubscriptionsFromGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharing(int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunistic(boolean, int);
     method public void setSubscriptionOverrideCongested(int, boolean, long);
     method public void setSubscriptionOverrideCongested(int, boolean, @NonNull int[], long);
@@ -42063,6 +42041,11 @@
     field public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED";
     field public static final String ACTION_MANAGE_SUBSCRIPTION_PLANS = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
     field public static final String ACTION_REFRESH_SUBSCRIPTION_PLANS = "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS";
+    field public static final int D2D_SHARING_ALL = 3; // 0x3
+    field public static final int D2D_SHARING_ALL_CONTACTS = 1; // 0x1
+    field public static final int D2D_SHARING_DISABLED = 0; // 0x0
+    field public static final int D2D_SHARING_STARRED_CONTACTS = 2; // 0x2
+    field public static final String D2D_STATUS_SHARING = "d2d_sharing_status";
     field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
     field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
     field public static final int DEFAULT_SUBSCRIPTION_ID = 2147483647; // 0x7fffffff
@@ -54983,6 +54966,7 @@
     ctor public RemoteViews(@NonNull java.util.Map<android.util.SizeF,android.widget.RemoteViews>);
     ctor public RemoteViews(android.widget.RemoteViews);
     ctor public RemoteViews(android.os.Parcel);
+    method public void addStableView(@IdRes int, @NonNull android.widget.RemoteViews, int);
     method public void addView(@IdRes int, android.widget.RemoteViews);
     method public android.view.View apply(android.content.Context, android.view.ViewGroup);
     method @Deprecated public android.widget.RemoteViews clone();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 057e16c..18b0a43 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -59,6 +59,10 @@
     method @NonNull public android.os.UserHandle getUser();
   }
 
+  public class Intent implements java.lang.Cloneable android.os.Parcelable {
+    field public static final String ACTION_CLEAR_DNS_CACHE = "android.intent.action.CLEAR_DNS_CACHE";
+  }
+
 }
 
 package android.content.pm {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index cd69021..afa9a7e 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2162,7 +2162,6 @@
     field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
     field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
     field public static final String CONTEXTHUB_SERVICE = "contexthub";
-    field public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
     field public static final String ETHERNET_SERVICE = "ethernet";
     field public static final String EUICC_CARD_SERVICE = "euicc_card";
     field public static final String FONT_SERVICE = "font";
@@ -2174,7 +2173,6 @@
     field public static final String OEM_LOCK_SERVICE = "oem_lock";
     field public static final String PERMISSION_SERVICE = "permission";
     field public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
-    field public static final String POWER_EXEMPTION_SERVICE = "power_exemption";
     field public static final String REBOOT_READINESS_SERVICE = "reboot_readiness";
     field public static final String ROLLBACK_SERVICE = "rollback";
     field public static final String SEARCH_UI_SERVICE = "search_ui";
@@ -2901,7 +2899,7 @@
   }
 
   public class FontManager {
-    method @NonNull public android.text.FontConfig getFontConfig();
+    method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public android.text.FontConfig getFontConfig();
     method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest, @IntRange(from=0) int);
     field public static final int RESULT_ERROR_DOWNGRADING = -5; // 0xfffffffb
     field public static final int RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE = -1; // 0xffffffff
@@ -7521,12 +7519,12 @@
 package android.net.vcn {
 
   public class VcnManager {
-    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener);
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.vcn.VcnNetworkPolicyResult applyVcnNetworkPolicy(@NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties);
-    method public void removeVcnNetworkPolicyListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void removeVcnNetworkPolicyChangeListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener);
   }
 
-  public static interface VcnManager.VcnNetworkPolicyListener {
+  public static interface VcnManager.VcnNetworkPolicyChangeListener {
     method public void onPolicyChanged();
   }
 
@@ -8184,6 +8182,7 @@
     field public static final int EVENT_MMS = 2; // 0x2
     field public static final int EVENT_SMS = 1; // 0x1
     field public static final int EVENT_UNSPECIFIED = 0; // 0x0
+    field public static final int REASON_ACCOUNT_TRANSFER = 104; // 0x68
     field public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
     field public static final int REASON_GEOFENCING = 100; // 0x64
     field public static final int REASON_OTHER = 1; // 0x1
@@ -8224,25 +8223,25 @@
     field public static final int USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS = 1; // 0x1
   }
 
-  public class PowerWhitelistManager {
-    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
-    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromWhitelist(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long, int, @Nullable String);
+  @Deprecated public class PowerWhitelistManager {
+    method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromWhitelist(@NonNull String);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long, int, @Nullable String);
     method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long);
     method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @Nullable String);
-    method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, int, @Nullable String);
-    field public static final int EVENT_MMS = 2; // 0x2
-    field public static final int EVENT_SMS = 1; // 0x1
-    field public static final int EVENT_UNSPECIFIED = 0; // 0x0
-    field public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
-    field public static final int REASON_GEOFENCING = 100; // 0x64
-    field public static final int REASON_OTHER = 1; // 0x1
-    field public static final int REASON_PUSH_MESSAGING = 101; // 0x65
-    field public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; // 0x66
-    field public static final int REASON_UNKNOWN = 0; // 0x0
-    field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
-    field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
+    method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, int, @Nullable String);
+    field @Deprecated public static final int EVENT_MMS = 2; // 0x2
+    field @Deprecated public static final int EVENT_SMS = 1; // 0x1
+    field @Deprecated public static final int EVENT_UNSPECIFIED = 0; // 0x0
+    field @Deprecated public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67
+    field @Deprecated public static final int REASON_GEOFENCING = 100; // 0x64
+    field @Deprecated public static final int REASON_OTHER = 1; // 0x1
+    field @Deprecated public static final int REASON_PUSH_MESSAGING = 101; // 0x65
+    field @Deprecated public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; // 0x66
+    field @Deprecated public static final int REASON_UNKNOWN = 0; // 0x0
+    field @Deprecated public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0
+    field @Deprecated public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1
   }
 
   public class RecoverySystem {
@@ -10251,6 +10250,7 @@
     ctor public HotwordDetectionService();
     method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
     method public void onDetectFromDspSource(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, long, @NonNull android.service.voice.HotwordDetectionService.DspHotwordDetectionCallback);
+    method public void onUpdateState(@Nullable android.os.Bundle, @Nullable android.os.SharedMemory);
     field public static final String SERVICE_INTERFACE = "android.service.voice.HotwordDetectionService";
   }
 
@@ -10261,10 +10261,8 @@
 
   public class VoiceInteractionService extends android.app.Service {
     method @NonNull public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, android.service.voice.AlwaysOnHotwordDetector.Callback);
+    method @NonNull public final android.service.voice.AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(String, java.util.Locale, @Nullable android.os.Bundle, @Nullable android.os.SharedMemory, android.service.voice.AlwaysOnHotwordDetector.Callback);
     method @NonNull @RequiresPermission("android.permission.MANAGE_VOICE_KEYPHRASES") public final android.media.voice.KeyphraseModelManager createKeyphraseModelManager();
-    method public final int setHotwordDetectionConfig(@Nullable android.os.Bundle);
-    field public static final int HOTWORD_CONFIG_FAILURE = 1; // 0x1
-    field public static final int HOTWORD_CONFIG_SUCCESS = 0; // 0x0
   }
 
 }
@@ -11473,7 +11471,7 @@
   }
 
   public static interface TelephonyCallback.AllowedNetworkTypesListener {
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(@NonNull java.util.Map<java.lang.Integer,java.lang.Long>);
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(int, long);
   }
 
   public static interface TelephonyCallback.CallAttributesListener {
@@ -11957,7 +11955,7 @@
     method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV4(int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>);
-    method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(int);
+    method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(@IntRange(from=android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET, to=15) int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryDurationMillis(long);
     method @NonNull public android.telephony.data.DataCallResponse.Builder setSliceInfo(@Nullable android.telephony.data.SliceInfo);
@@ -12052,7 +12050,6 @@
   }
 
   public final class EpsBearerQosSessionAttributes implements android.os.Parcelable android.net.QosSessionAttributes {
-    method @NonNull public static android.telephony.data.EpsBearerQosSessionAttributes create(@NonNull android.os.Parcel);
     method public int describeContents();
     method public long getGuaranteedDownlinkBitRate();
     method public long getGuaranteedUplinkBitRate();
@@ -13035,6 +13032,7 @@
     method public void onAutoConfigurationErrorReceived(int, @NonNull String);
     method public void onConfigurationChanged(@NonNull byte[]);
     method public void onConfigurationReset();
+    method public void onPreProvisioningReceived(@NonNull byte[]);
     method public void onRemoved();
   }
 
@@ -13504,6 +13502,7 @@
     method public int getConfigInt(int);
     method public String getConfigString(int);
     method public final void notifyAutoConfigurationErrorReceived(int, @NonNull String);
+    method public final void notifyPreProvisioningReceived(@NonNull byte[]);
     method public final void notifyProvisionedValueChanged(int, int);
     method public final void notifyProvisionedValueChanged(int, String);
     method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean);
@@ -13835,6 +13834,7 @@
 package android.uwb {
 
   public final class AngleMeasurement implements android.os.Parcelable {
+    ctor public AngleMeasurement(double, double, double);
     method public int describeContents();
     method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel();
     method @FloatRange(from=0.0, to=3.141592653589793) public double getErrorRadians();
@@ -13843,14 +13843,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.uwb.AngleMeasurement> CREATOR;
   }
 
-  public static final class AngleMeasurement.Builder {
-    ctor public AngleMeasurement.Builder();
-    method @NonNull public android.uwb.AngleMeasurement build();
-    method @NonNull public android.uwb.AngleMeasurement.Builder setConfidenceLevel(double);
-    method @NonNull public android.uwb.AngleMeasurement.Builder setErrorRadians(double);
-    method @NonNull public android.uwb.AngleMeasurement.Builder setRadians(double);
-  }
-
   public final class AngleOfArrivalMeasurement implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.uwb.AngleMeasurement getAltitude();
@@ -13860,10 +13852,9 @@
   }
 
   public static final class AngleOfArrivalMeasurement.Builder {
-    ctor public AngleOfArrivalMeasurement.Builder();
+    ctor public AngleOfArrivalMeasurement.Builder(@NonNull android.uwb.AngleMeasurement);
     method @NonNull public android.uwb.AngleOfArrivalMeasurement build();
     method @NonNull public android.uwb.AngleOfArrivalMeasurement.Builder setAltitude(@NonNull android.uwb.AngleMeasurement);
-    method @NonNull public android.uwb.AngleOfArrivalMeasurement.Builder setAzimuth(@NonNull android.uwb.AngleMeasurement);
   }
 
   public final class DistanceMeasurement implements android.os.Parcelable {
@@ -13963,7 +13954,7 @@
   public final class UwbManager {
     method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public long elapsedRealtimeResolutionNanos();
     method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.PersistableBundle getSpecificationInfo();
-    method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public AutoCloseable openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
+    method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.CancellationSignal openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
     method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void registerAdapterStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdapterStateCallback);
     method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void unregisterAdapterStateCallback(@NonNull android.uwb.UwbManager.AdapterStateCallback);
   }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 72d3976..d951405 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -414,6 +414,7 @@
     method public long getLastNetworkLogRetrievalTime();
     method public long getLastSecurityLogRetrievalTime();
     method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle);
+    method @NonNull @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public java.util.Set<java.lang.String> getPolicyExemptApps();
     method public boolean isCurrentInputMethodSetByOwner();
     method public boolean isFactoryResetProtectionPolicySupported();
     method @RequiresPermission(anyOf={"android.permission.MARK_DEVICE_ORGANIZATION_OWNED", "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName);
@@ -698,7 +699,8 @@
     field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
     field public static final String DREAM_SERVICE = "dream";
     field public static final String FONT_SERVICE = "font";
-    field public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
+    field public static final String POWER_EXEMPTION_SERVICE = "power_exemption";
+    field @Deprecated public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
     field public static final String TEST_NETWORK_SERVICE = "test_network";
   }
 
@@ -706,6 +708,10 @@
     method public int getDisplayId();
   }
 
+  public class SyncAdapterType implements android.os.Parcelable {
+    method @Nullable public String getPackageName();
+  }
+
 }
 
 package android.content.integrity {
@@ -940,6 +946,12 @@
     method public void splitVertically(@NonNull android.graphics.Rect...);
   }
 
+  public class Typeface {
+    method @NonNull public static java.util.Map<java.lang.String,android.graphics.Typeface> deserializeFontMap(@NonNull java.nio.ByteBuffer) throws java.io.IOException;
+    method @Nullable public static android.os.SharedMemory getSystemFontMapSharedMemory();
+    method @NonNull public static android.os.SharedMemory serializeFontMap(@NonNull java.util.Map<java.lang.String,android.graphics.Typeface>) throws android.system.ErrnoException, java.io.IOException;
+  }
+
 }
 
 package android.graphics.drawable {
@@ -962,7 +974,7 @@
 package android.graphics.fonts {
 
   public class FontManager {
-    method @NonNull public android.text.FontConfig getFontConfig();
+    method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public android.text.FontConfig getFontConfig();
     method @RequiresPermission(android.Manifest.permission.UPDATE_FONTS) public int updateFontFamily(@NonNull android.graphics.fonts.FontFamilyUpdateRequest, @IntRange(from=0) int);
     field public static final int RESULT_ERROR_DOWNGRADING = -5; // 0xfffffffb
     field public static final int RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE = -1; // 0xffffffff
@@ -1485,6 +1497,14 @@
 
 package android.os {
 
+  public final class BatteryStatsManager {
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void resetBattery(boolean);
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setBatteryLevel(int, boolean);
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setChargerAcOnline(boolean, boolean);
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void suspendBatteryInput();
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void unplugBattery(boolean);
+  }
+
   public class Build {
     method public static boolean is64BitAbi(String);
     field public static final boolean IS_EMULATOR;
@@ -2538,6 +2558,7 @@
     method public default int getDisplayImePolicy(int);
     method public default void holdLock(android.os.IBinder, int);
     method public default void setDisplayImePolicy(int, int);
+    method public default void setForceCrossWindowBlurDisabled(boolean);
     method public default void setShouldShowSystemDecors(int, boolean);
     method public default void setShouldShowWithInsecureKeyguard(int, boolean);
     method public default boolean shouldShowSystemDecors(int);
@@ -2915,7 +2936,7 @@
     method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo);
     method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo);
     method @CallSuper @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.window.TaskAppearedInfo> registerOrganizer();
-    method @BinderThread public void removeStartingWindow(int);
+    method @BinderThread public void removeStartingWindow(int, @Nullable android.view.SurfaceControl, @Nullable android.graphics.Rect, boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean);
     method @CallSuper @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void unregisterOrganizer();
   }
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 8e53b5b..7dbbc54 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -57,6 +57,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.DeadSystemException;
+import android.os.Environment;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
@@ -120,6 +121,8 @@
     /** {@hide} */
     private static final String VALUE_CMF_COLOR =
             android.os.SystemProperties.get("ro.boot.hardware.color");
+    /** {@hide} */
+    private static final String WALLPAPER_CMF_PATH = "/wallpaper/image/";
 
     /**
      * Activity Action: Show settings for choosing wallpaper. Do not use directly to construct
@@ -2066,22 +2069,17 @@
             return null;
         } else {
             whichProp = PROP_WALLPAPER;
-            final int defaultColorResId = context.getResources().getIdentifier(
-                    "default_wallpaper_" + VALUE_CMF_COLOR, "drawable", "android");
-            defaultResId =
-                    defaultColorResId == 0 ? com.android.internal.R.drawable.default_wallpaper
-                            : defaultColorResId;
+            defaultResId = com.android.internal.R.drawable.default_wallpaper;
         }
         final String path = SystemProperties.get(whichProp);
-        if (!TextUtils.isEmpty(path)) {
-            final File file = new File(path);
-            if (file.exists()) {
-                try {
-                    return new FileInputStream(file);
-                } catch (IOException e) {
-                    // Ignored, fall back to platform default below
-                }
-            }
+        final InputStream wallpaperInputStream = getWallpaperInputStream(path);
+        if (wallpaperInputStream != null) {
+            return wallpaperInputStream;
+        }
+        final String cmfPath = getCmfWallpaperPath();
+        final InputStream cmfWallpaperInputStream = getWallpaperInputStream(cmfPath);
+        if (cmfWallpaperInputStream != null) {
+            return cmfWallpaperInputStream;
         }
         try {
             return context.getResources().openRawResource(defaultResId);
@@ -2091,6 +2089,25 @@
         return null;
     }
 
+    private static InputStream getWallpaperInputStream(String path) {
+        if (!TextUtils.isEmpty(path)) {
+            final File file = new File(path);
+            if (file.exists()) {
+                try {
+                    return new FileInputStream(file);
+                } catch (IOException e) {
+                    // Ignored, fall back to platform default
+                }
+            }
+        }
+        return null;
+    }
+
+    private static String getCmfWallpaperPath() {
+        return Environment.getProductDirectory() + WALLPAPER_CMF_PATH + "default_wallpaper_"
+                + VALUE_CMF_COLOR;
+    }
+
     /**
      * Return {@link ComponentName} of the default live wallpaper, or
      * {@code null} if none is defined.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 30fb858..930717b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -13726,4 +13726,22 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Lists apps that are exempt from policies (such as
+     * {@link #setPackagesSuspended(ComponentName, String[], boolean)}).
+     *
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(value = android.Manifest.permission.MANAGE_DEVICE_ADMINS)
+    public @NonNull Set<String> getPolicyExemptApps() {
+        if (mService == null) return Collections.emptySet();
+
+        try {
+            return new HashSet<>(mService.listPolicyExemptApps());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 25ca599..e98720c 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -177,6 +177,7 @@
 
     String[] setPackagesSuspended(in ComponentName admin, in String callerPackage, in String[] packageNames, boolean suspended);
     boolean isPackageSuspended(in ComponentName admin, in String callerPackage, String packageName);
+    List<String> listPolicyExemptApps();
 
     boolean installCaCert(in ComponentName admin, String callerPackage, in byte[] certBuffer);
     void uninstallCaCerts(in ComponentName admin, String callerPackage, in String[] aliases);
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 6ac1c1a..1cbb2fb 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -121,7 +121,7 @@
      *
      * @see #widgetFeatures
      */
-    public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 3;
+    public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 4;
 
     /** @hide */
     @IntDef(flag = true, prefix = { "FLAG_" }, value = {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 0509e3f..2523459 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3713,6 +3713,9 @@
      * usage statistics.
      * <dt> {@link #HARDWARE_PROPERTIES_SERVICE} ("hardware_properties")
      * <dd> A {@link android.os.HardwarePropertiesManager} for accessing hardware properties.
+     * <dt> {@link #DOMAIN_VERIFICATION_SERVICE} ("domain_verification")
+     * <dd> A {@link android.content.pm.verify.domain.DomainVerificationManager} for accessing
+     * web domain approval state.
      * </dl>
      *
      * <p>Note:  System services obtained via this API may be closely associated with
@@ -3794,6 +3797,8 @@
      * @see android.app.usage.NetworkStatsManager
      * @see android.os.HardwarePropertiesManager
      * @see #HARDWARE_PROPERTIES_SERVICE
+     * @see #DOMAIN_VERIFICATION_SERVICE
+     * @see android.content.pm.verify.domain.DomainVerificationManager
      */
     public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
 
@@ -3813,7 +3818,8 @@
      * {@link android.view.inputmethod.InputMethodManager},
      * {@link android.app.UiModeManager}, {@link android.app.DownloadManager},
      * {@link android.os.BatteryManager}, {@link android.app.job.JobScheduler},
-     * {@link android.app.usage.NetworkStatsManager}.
+     * {@link android.app.usage.NetworkStatsManager},
+     * {@link android.content.pm.verify.domain.DomainVerificationManager}.
      * </p>
      *
      * <p>
@@ -4833,7 +4839,8 @@
      * @hide
      */
     @TestApi
-    @SuppressLint("ServiceName")  // TODO: This should be renamed to POWER_WHITELIST_SERVICE
+    @Deprecated
+    @SuppressLint("ServiceName")
     public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
 
     /**
@@ -4842,7 +4849,7 @@
      * @see #getSystemService(String)
      * @hide
      */
-    @SystemApi
+    @TestApi
     public static final String POWER_EXEMPTION_SERVICE = "power_exemption";
 
     /**
@@ -5544,12 +5551,13 @@
     public static final String GAME_SERVICE = "game";
 
     /**
-     * Use with {@link #getSystemService(String)} to access domain verification service.
+     * Use with {@link #getSystemService(String)} to access
+     * {@link android.content.pm.verify.domain.DomainVerificationManager} to retrieve approval and
+     * user state for declared web domains.
      *
      * @see #getSystemService(String)
-     * @hide
+     * @see android.content.pm.verify.domain.DomainVerificationManager
      */
-    @SystemApi
     public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification";
 
     /**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index de17fda..04f93ca 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2331,6 +2331,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final String ACTION_CLEAR_DNS_CACHE = "android.intent.action.CLEAR_DNS_CACHE";
     /**
      * Alarm Changed Action: This is broadcast when the AlarmClock
diff --git a/core/java/android/content/SyncAdapterType.java b/core/java/android/content/SyncAdapterType.java
index 1c21b2a..47c333c 100644
--- a/core/java/android/content/SyncAdapterType.java
+++ b/core/java/android/content/SyncAdapterType.java
@@ -17,6 +17,7 @@
 package android.content;
 
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
@@ -168,6 +169,7 @@
      *
      * @hide
      */
+    @TestApi
     public @Nullable String getPackageName() {
         return packageName;
     }
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 5a17753..58f83a7 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1086,6 +1086,13 @@
      */
     public WindowLayout windowLayout;
 
+    /**
+     * Attribution tags for finer grained calls if a {@android.content.Context#sendBroadcast(Intent,
+     * String)} is used with a permission.
+     * @hide
+     */
+    public String[] attributionTags;
+
     public ActivityInfo() {
     }
 
@@ -1114,6 +1121,7 @@
         maxAspectRatio = orig.maxAspectRatio;
         minAspectRatio = orig.minAspectRatio;
         supportsSizeChanges = orig.supportsSizeChanges;
+        attributionTags = orig.attributionTags;
     }
 
     /**
@@ -1361,6 +1369,15 @@
         if (supportsSizeChanges) {
             pw.println(prefix + "supportsSizeChanges=true");
         }
+        if (attributionTags != null && attributionTags.length > 0) {
+            StringBuilder tags = new StringBuilder();
+            tags.append(attributionTags[0]);
+            for (int i = 1; i < attributionTags.length; i++) {
+                tags.append(", ");
+                tags.append(attributionTags[i]);
+            }
+            pw.println(prefix + "attributionTags=[" + tags + "]");
+        }
         super.dumpBack(pw, prefix, dumpFlags);
     }
 
@@ -1406,6 +1423,7 @@
         dest.writeFloat(maxAspectRatio);
         dest.writeFloat(minAspectRatio);
         dest.writeBoolean(supportsSizeChanges);
+        dest.writeString8Array(attributionTags);
     }
 
     /**
@@ -1525,6 +1543,7 @@
         maxAspectRatio = source.readFloat();
         minAspectRatio = source.readFloat();
         supportsSizeChanges = source.readBoolean();
+        attributionTags = source.createString8Array();
     }
 
     /**
diff --git a/core/java/android/content/pm/IDataLoaderStatusListener.aidl b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
index 745c39b..79b70f2 100644
--- a/core/java/android/content/pm/IDataLoaderStatusListener.aidl
+++ b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
@@ -23,32 +23,34 @@
 oneway interface IDataLoaderStatusListener {
     /** The DataLoader process died, binder disconnected or class destroyed. */
     const int DATA_LOADER_DESTROYED = 0;
+    /** The system is in process of binding to the DataLoader. */
+    const int DATA_LOADER_BINDING = 1;
     /** DataLoader process is running and bound to. */
-    const int DATA_LOADER_BOUND = 1;
+    const int DATA_LOADER_BOUND = 2;
     /** DataLoader has handled onCreate(). */
-    const int DATA_LOADER_CREATED = 2;
+    const int DATA_LOADER_CREATED = 3;
 
     /** DataLoader can receive missing pages and read pages notifications,
      *  and ready to provide data. */
-    const int DATA_LOADER_STARTED = 3;
+    const int DATA_LOADER_STARTED = 4;
     /** DataLoader no longer ready to provide data and is not receiving
     *   any notifications from IncFS. */
-    const int DATA_LOADER_STOPPED = 4;
+    const int DATA_LOADER_STOPPED = 5;
 
     /** DataLoader streamed everything necessary to continue installation. */
-    const int DATA_LOADER_IMAGE_READY = 5;
+    const int DATA_LOADER_IMAGE_READY = 6;
     /** Installation can't continue as DataLoader failed to stream necessary data. */
-    const int DATA_LOADER_IMAGE_NOT_READY = 6;
+    const int DATA_LOADER_IMAGE_NOT_READY = 7;
 
     /** DataLoader instance can't run at the moment, but might recover later.
      *  It's up to system to decide if the app is still usable. */
-    const int DATA_LOADER_UNAVAILABLE = 7;
+    const int DATA_LOADER_UNAVAILABLE = 8;
 
     /** DataLoader reports that this instance is invalid and can never be restored.
     *   Warning: this is a terminal status that data loader should use carefully and
     *            the system should almost never use - e.g. only if all recovery attempts
     *            fail and all retry limits are exceeded. */
-    const int DATA_LOADER_UNRECOVERABLE = 8;
+    const int DATA_LOADER_UNRECOVERABLE = 9;
 
     /** There are no known issues with the data stream. */
     const int STREAM_HEALTHY = 0;
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
index 0d5b33c..8f9a0d7 100644
--- a/core/java/android/content/pm/dex/DexMetadataHelper.java
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -29,6 +29,7 @@
 import android.util.jar.StrictJarFile;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.security.VerityUtils;
 
 import java.io.File;
 import java.io.IOException;
@@ -76,7 +77,8 @@
      * Returns whether fs-verity is required to install a dex metadata
      */
     public static boolean isFsVerityRequired() {
-        return SystemProperties.getBoolean(PROPERTY_DM_FSVERITY_REQUIRED, false);
+        return VerityUtils.isFsVeritySupported()
+                && SystemProperties.getBoolean(PROPERTY_DM_FSVERITY_REQUIRED, false);
     }
 
     /**
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index 9a84ded..b660a00 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -482,6 +482,7 @@
         ai.rotationAnimation = a.getRotationAnimation();
         ai.colorMode = a.getColorMode();
         ai.windowLayout = a.getWindowLayout();
+        ai.attributionTags = a.getAttributionTags();
         if ((flags & PackageManager.GET_META_DATA) != 0) {
             ai.metaData = a.getMetaData();
         }
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
index 6f478ac..9285ccb 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivity.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java
@@ -82,6 +82,9 @@
     @Nullable
     ActivityInfo.WindowLayout windowLayout;
 
+    @Nullable
+    String[] attributionTags;
+
     public ParsedActivity(ParsedActivity other) {
         super(other);
         this.theme = other.theme;
@@ -107,6 +110,7 @@
         this.rotationAnimation = other.rotationAnimation;
         this.colorMode = other.colorMode;
         this.windowLayout = other.windowLayout;
+        this.attributionTags = other.attributionTags;
     }
 
     /**
@@ -172,6 +176,7 @@
         alias.requestedVrComponent = target.requestedVrComponent;
         alias.directBootAware = target.directBootAware;
         alias.setProcessName(target.getProcessName());
+        alias.attributionTags = target.attributionTags;
         return alias;
 
         // Not all attributes from the target ParsedActivity are copied to the alias.
@@ -299,6 +304,7 @@
         } else {
             dest.writeBoolean(false);
         }
+        dest.writeString8Array(this.attributionTags);
     }
 
     public ParsedActivity() {
@@ -332,6 +338,7 @@
         if (in.readBoolean()) {
             windowLayout = new ActivityInfo.WindowLayout(in);
         }
+        this.attributionTags = in.createString8Array();
     }
 
     public static final Parcelable.Creator<ParsedActivity> CREATOR = new Creator<ParsedActivity>() {
@@ -445,4 +452,9 @@
     public ActivityInfo.WindowLayout getWindowLayout() {
         return windowLayout;
     }
+
+    @Nullable
+    public String[] getAttributionTags() {
+        return attributionTags;
+    }
 }
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 0f4aa06..d99c410 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -210,6 +210,11 @@
                 pkg.setVisibleToInstantApps(true);
             }
 
+            String attributionTags = sa.getString(R.styleable.AndroidManifestActivity_attributionTags);
+            if (attributionTags != null) {
+                activity.attributionTags = attributionTags.split("\\|");
+            }
+
             return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
                     false /*isAlias*/, visibleToEphemeral, input,
                     R.styleable.AndroidManifestActivity_parentActivityName,
diff --git a/core/java/android/ddm/DdmHandleHeap.java b/core/java/android/ddm/DdmHandleHeap.java
index e24aeb2..8fa2352 100644
--- a/core/java/android/ddm/DdmHandleHeap.java
+++ b/core/java/android/ddm/DdmHandleHeap.java
@@ -30,15 +30,7 @@
  */
 public class DdmHandleHeap extends ChunkHandler {
 
-    public static final int CHUNK_HPIF = type("HPIF");
-    public static final int CHUNK_HPSG = type("HPSG");
-    public static final int CHUNK_HPDU = type("HPDU");
-    public static final int CHUNK_HPDS = type("HPDS");
-    public static final int CHUNK_NHSG = type("NHSG");
     public static final int CHUNK_HPGC = type("HPGC");
-    public static final int CHUNK_REAE = type("REAE");
-    public static final int CHUNK_REAQ = type("REAQ");
-    public static final int CHUNK_REAL = type("REAL");
 
     private static DdmHandleHeap mInstance = new DdmHandleHeap();
 
@@ -50,15 +42,7 @@
      * Register for the messages we're interested in.
      */
     public static void register() {
-        DdmServer.registerHandler(CHUNK_HPIF, mInstance);
-        DdmServer.registerHandler(CHUNK_HPSG, mInstance);
-        DdmServer.registerHandler(CHUNK_HPDU, mInstance);
-        DdmServer.registerHandler(CHUNK_HPDS, mInstance);
-        DdmServer.registerHandler(CHUNK_NHSG, mInstance);
         DdmServer.registerHandler(CHUNK_HPGC, mInstance);
-        DdmServer.registerHandler(CHUNK_REAE, mInstance);
-        DdmServer.registerHandler(CHUNK_REAQ, mInstance);
-        DdmServer.registerHandler(CHUNK_REAL, mInstance);
     }
 
     /**
@@ -81,24 +65,8 @@
             Log.v("ddm-heap", "Handling " + name(request.type) + " chunk");
         int type = request.type;
 
-        if (type == CHUNK_HPIF) {
-            return handleHPIF(request);
-        } else if (type == CHUNK_HPSG) {
-            return handleHPSGNHSG(request, false);
-        } else if (type == CHUNK_HPDU) {
-            return handleHPDU(request);
-        } else if (type == CHUNK_HPDS) {
-            return handleHPDS(request);
-        } else if (type == CHUNK_NHSG) {
-            return handleHPSGNHSG(request, true);
-        } else if (type == CHUNK_HPGC) {
+        if (type == CHUNK_HPGC) {
             return handleHPGC(request);
-        } else if (type == CHUNK_REAE) {
-            return handleREAE(request);
-        } else if (type == CHUNK_REAQ) {
-            return handleREAQ(request);
-        } else if (type == CHUNK_REAL) {
-            return handleREAL(request);
         } else {
             throw new RuntimeException("Unknown packet "
                 + ChunkHandler.name(type));
@@ -106,112 +74,6 @@
     }
 
     /*
-     * Handle a "HeaP InFo" request.
-     */
-    private Chunk handleHPIF(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-
-        int when = in.get();
-        if (false)
-            Log.v("ddm-heap", "Heap segment enable: when=" + when);
-
-        boolean ok = DdmVmInternal.heapInfoNotify(when);
-        if (!ok) {
-            return createFailChunk(1, "Unsupported HPIF what");
-        } else {
-            return null;        // empty response
-        }
-    }
-
-    /*
-     * Handle a "HeaP SeGment" or "Native Heap SeGment" request.
-     */
-    private Chunk handleHPSGNHSG(Chunk request, boolean isNative) {
-        ByteBuffer in = wrapChunk(request);
-
-        int when = in.get();
-        int what = in.get();
-        if (false)
-            Log.v("ddm-heap", "Heap segment enable: when=" + when
-                + ", what=" + what + ", isNative=" + isNative);
-
-        boolean ok = DdmVmInternal.heapSegmentNotify(when, what, isNative);
-        if (!ok) {
-            return createFailChunk(1, "Unsupported HPSG what/when");
-        } else {
-            // TODO: if "when" is non-zero and we want to see a dump
-            //       right away, initiate a GC.
-            return null;        // empty response
-        }
-    }
-
-    /*
-     * Handle a "HeaP DUmp" request.
-     *
-     * This currently just returns a result code.  We could pull up
-     * the entire contents of the file and return them, but hprof dump
-     * files can be a few megabytes.
-     */
-    private Chunk handleHPDU(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-        byte result;
-
-        /* get the filename for the output file */
-        int len = in.getInt();
-        String fileName = getString(in, len);
-        if (false)
-            Log.d("ddm-heap", "Heap dump: file='" + fileName + "'");
-
-        try {
-            Debug.dumpHprofData(fileName);
-            result = 0;
-        } catch (UnsupportedOperationException uoe) {
-            Log.w("ddm-heap", "hprof dumps not supported in this VM");
-            result = -1;
-        } catch (IOException ioe) {
-            result = -1;
-        } catch (RuntimeException re) {
-            result = -1;
-        }
-
-        /* create a non-empty reply so the handler fires on completion */
-        byte[] reply = { result };
-        return new Chunk(CHUNK_HPDU, reply, 0, reply.length);
-    }
-
-    /*
-     * Handle a "HeaP Dump Streaming" request.
-     *
-     * This tells the VM to create a heap dump and send it directly to
-     * DDMS.  The dumps are large enough that we don't want to copy the
-     * data into a byte[] and send it from here.
-     */
-    private Chunk handleHPDS(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-        byte result;
-
-        /* get the filename for the output file */
-        if (false)
-            Log.d("ddm-heap", "Heap dump: [DDMS]");
-
-        String failMsg = null;
-        try {
-            Debug.dumpHprofDataDdms();
-        } catch (UnsupportedOperationException uoe) {
-            failMsg = "hprof dumps not supported in this VM";
-        } catch (RuntimeException re) {
-            failMsg = "Exception: " + re.getMessage();
-        }
-
-        if (failMsg != null) {
-            Log.w("ddm-heap", failMsg);
-            return createFailChunk(1, failMsg);
-        } else {
-            return null;
-        }
-    }
-
-    /*
      * Handle a "HeaP Garbage Collection" request.
      */
     private Chunk handleHPGC(Chunk request) {
@@ -223,47 +85,4 @@
 
         return null;        // empty response
     }
-
-    /*
-     * Handle a "REcent Allocation Enable" request.
-     */
-    private Chunk handleREAE(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-        boolean enable;
-
-        enable = (in.get() != 0);
-
-        if (false)
-            Log.d("ddm-heap", "Recent allocation enable request: " + enable);
-
-        DdmVmInternal.enableRecentAllocations(enable);
-
-        return null;        // empty response
-    }
-
-    /*
-     * Handle a "REcent Allocation Query" request.
-     */
-    private Chunk handleREAQ(Chunk request) {
-        //ByteBuffer in = wrapChunk(request);
-
-        byte[] reply = new byte[1];
-        reply[0] = DdmVmInternal.getRecentAllocationStatus() ? (byte)1 :(byte)0;
-        return new Chunk(CHUNK_REAQ, reply, 0, reply.length);
-    }
-
-    /*
-     * Handle a "REcent ALlocations" request.
-     */
-    private Chunk handleREAL(Chunk request) {
-        //ByteBuffer in = wrapChunk(request);
-
-        if (false)
-            Log.d("ddm-heap", "Recent allocations request");
-
-        /* generate the reply in a ready-to-go format */
-        byte[] reply = DdmVmInternal.getRecentAllocations();
-        return new Chunk(CHUNK_REAL, reply, 0, reply.length);
-    }
 }
-
diff --git a/core/java/android/ddm/DdmHandleThread.java b/core/java/android/ddm/DdmHandleThread.java
deleted file mode 100644
index 613ab75..0000000
--- a/core/java/android/ddm/DdmHandleThread.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2007 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.ddm;
-
-import org.apache.harmony.dalvik.ddmc.Chunk;
-import org.apache.harmony.dalvik.ddmc.ChunkHandler;
-import org.apache.harmony.dalvik.ddmc.DdmServer;
-import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
-import android.util.Log;
-import java.nio.ByteBuffer;
-
-/**
- * Handle thread-related traffic.
- */
-public class DdmHandleThread extends ChunkHandler {
-
-    public static final int CHUNK_THEN = type("THEN");
-    public static final int CHUNK_THCR = type("THCR");
-    public static final int CHUNK_THDE = type("THDE");
-    public static final int CHUNK_THST = type("THST");
-    public static final int CHUNK_STKL = type("STKL");
-
-    private static DdmHandleThread mInstance = new DdmHandleThread();
-
-
-    /* singleton, do not instantiate */
-    private DdmHandleThread() {}
-
-    /**
-     * Register for the messages we're interested in.
-     */
-    public static void register() {
-        DdmServer.registerHandler(CHUNK_THEN, mInstance);
-        DdmServer.registerHandler(CHUNK_THST, mInstance);
-        DdmServer.registerHandler(CHUNK_STKL, mInstance);
-    }
-
-    /**
-     * Called when the DDM server connects.  The handler is allowed to
-     * send messages to the server.
-     */
-    public void connected() {}
-
-    /**
-     * Called when the DDM server disconnects.  Can be used to disable
-     * periodic transmissions or clean up saved state.
-     */
-    public void disconnected() {}
-
-    /**
-     * Handle a chunk of data.
-     */
-    public Chunk handleChunk(Chunk request) {
-        if (false)
-            Log.v("ddm-thread", "Handling " + name(request.type) + " chunk");
-        int type = request.type;
-
-        if (type == CHUNK_THEN) {
-            return handleTHEN(request);
-        } else if (type == CHUNK_THST) {
-            return handleTHST(request);
-        } else if (type == CHUNK_STKL) {
-            return handleSTKL(request);
-        } else {
-            throw new RuntimeException("Unknown packet "
-                + ChunkHandler.name(type));
-        }
-    }
-
-    /*
-     * Handle a "THread notification ENable" request.
-     */
-    private Chunk handleTHEN(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-
-        boolean enable = (in.get() != 0);
-        //Log.i("ddm-thread", "Thread notify enable: " + enable);
-
-        DdmVmInternal.threadNotify(enable);
-        return null;        // empty response
-    }
-
-    /*
-     * Handle a "THread STatus" request.  This is constructed by the VM.
-     */
-    private Chunk handleTHST(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-        // currently nothing to read from "in"
-
-        //Log.d("ddm-thread", "Thread status request");
-
-        byte[] status = DdmVmInternal.getThreadStats();
-        if (status != null)
-            return new Chunk(CHUNK_THST, status, 0, status.length);
-        else
-            return createFailChunk(1, "Can't build THST chunk");
-    }
-
-    /*
-     * Handle a STacK List request.
-     *
-     * This is done by threadId, which isn't great since those are
-     * recycled.  We need a thread serial ID.  The Linux tid is an okay
-     * answer as it's unlikely to recycle at the exact wrong moment.
-     * However, we're using the short threadId in THST messages, so we
-     * use them here for consistency.  (One thought is to keep the current
-     * thread ID in the low 16 bits and somehow serialize the top 16 bits.)
-     */
-    private Chunk handleSTKL(Chunk request) {
-        ByteBuffer in = wrapChunk(request);
-        int threadId;
-
-        threadId = in.getInt();
-
-        //Log.d("ddm-thread", "Stack list request " + threadId);
-
-        StackTraceElement[] trace = DdmVmInternal.getStackTraceById(threadId);
-        if (trace == null) {
-            return createFailChunk(1, "Stack trace unavailable");
-        } else {
-            return createStackChunk(trace, threadId);
-        }
-    }
-
-    /*
-     * Serialize a StackTraceElement[] into an STKL chunk.
-     *
-     * We include the threadId in the response so the other side doesn't have
-     * to match up requests and responses as carefully.
-     */
-    private Chunk createStackChunk(StackTraceElement[] trace, int threadId) {
-        int bufferSize = 0;
-
-        bufferSize += 4;            // version, flags, whatever
-        bufferSize += 4;            // thread ID
-        bufferSize += 4;            // frame count
-        for (StackTraceElement elem : trace) {
-            bufferSize += 4 + elem.getClassName().length() * 2;
-            bufferSize += 4 + elem.getMethodName().length() * 2;
-            bufferSize += 4;
-            if (elem.getFileName() != null)
-                bufferSize += elem.getFileName().length() * 2;
-            bufferSize += 4;        // line number
-        }
-
-        ByteBuffer out = ByteBuffer.allocate(bufferSize);
-        out.putInt(0);
-        out.putInt(threadId);
-        out.putInt(trace.length);
-        for (StackTraceElement elem : trace) {
-            out.putInt(elem.getClassName().length());
-            putString(out, elem.getClassName());
-            out.putInt(elem.getMethodName().length());
-            putString(out, elem.getMethodName());
-            if (elem.getFileName() != null) {
-                out.putInt(elem.getFileName().length());
-                putString(out, elem.getFileName());
-            } else {
-                out.putInt(0);
-            }
-            out.putInt(elem.getLineNumber());
-        }
-
-        return new Chunk(CHUNK_STKL, out);
-    }
-}
-
diff --git a/core/java/android/ddm/DdmRegister.java b/core/java/android/ddm/DdmRegister.java
index e0faa51..ca10312 100644
--- a/core/java/android/ddm/DdmRegister.java
+++ b/core/java/android/ddm/DdmRegister.java
@@ -16,9 +16,10 @@
 
 package android.ddm;
 
-import org.apache.harmony.dalvik.ddmc.DdmServer;
 import android.util.Log;
 
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+
 /**
  * Just a place to stick handler registrations, instead of scattering
  * them around.
@@ -46,7 +47,6 @@
         if (false)
             Log.v("ddm", "Registering DDM message handlers");
         DdmHandleHello.register();
-        DdmHandleThread.register();
         DdmHandleHeap.register();
         DdmHandleNativeHeap.register();
         DdmHandleProfiling.register();
diff --git a/core/java/android/graphics/fonts/FontManager.java b/core/java/android/graphics/fonts/FontManager.java
index 7bf692f..fa2ccbc 100644
--- a/core/java/android/graphics/fonts/FontManager.java
+++ b/core/java/android/graphics/fonts/FontManager.java
@@ -195,6 +195,7 @@
      * @return The current font configuration. null if failed to fetch information from the system
      *         service.
      */
+    @RequiresPermission(Manifest.permission.UPDATE_FONTS)
     public @NonNull FontConfig getFontConfig() {
         try {
             return mIFontManager.getFontConfig();
diff --git a/core/java/android/hardware/SensorPrivacyManagerInternal.java b/core/java/android/hardware/SensorPrivacyManagerInternal.java
new file mode 100644
index 0000000..d12e9f8
--- /dev/null
+++ b/core/java/android/hardware/SensorPrivacyManagerInternal.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+/**
+ * SensorPrivacyManager calls for within the system server
+ * @hide
+ */
+public abstract class SensorPrivacyManagerInternal {
+
+    /**
+     * A class implementing this interface can register to receive a callback when state changes.
+     */
+    public interface OnSensorPrivacyChangedListener {
+        /**
+         * The callback invoked when the state changes.
+         */
+        void onSensorPrivacyChanged(boolean enabled);
+    }
+
+    /**
+     * A class implementing this interface can register to receive a callback when state changes for
+     * any user.
+     */
+    public interface OnUserSensorPrivacyChangedListener {
+        /**
+         * The callback invoked when the state changes.
+         */
+        void onSensorPrivacyChanged(int userId, boolean enabled);
+    }
+
+    /**
+     * Get the individual sensor privacy state for a given user.
+     */
+    public abstract boolean isSensorPrivacyEnabled(int userId, int sensor);
+
+    /**
+     * Registers a new listener to receive notification when the state of sensor privacy
+     * changes.
+     */
+    public abstract void addSensorPrivacyListener(int userId, int sensor,
+            OnSensorPrivacyChangedListener listener);
+
+    /**
+     * Registers a new listener to receive notification when the state of sensor privacy
+     * changes for any user.
+     */
+    public abstract void addSensorPrivacyListenerForAllUsers(int sensor,
+            OnUserSensorPrivacyChangedListener listener);
+}
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 788afe3..365dea6 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -65,7 +65,7 @@
     private static final int CAPPED_SAMPLING_RATE_LEVEL = SensorDirectChannel.RATE_NORMAL;
 
     private static final String HIGH_SAMPLING_RATE_SENSORS_PERMISSION =
-                                        "android.permisison.HIGH_SAMPLING_RATE_SENSORS";
+                                        "android.permission.HIGH_SAMPLING_RATE_SENSORS";
     /**
      * For apps targeting S and above, a SecurityException is thrown when they do not have
      * HIGH_SAMPLING_RATE_SENSORS permission, run in debug mode, and request sampling rates that
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 8ebf757..062438c 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -73,7 +73,8 @@
 public class VcnManager {
     @NonNull private static final String TAG = VcnManager.class.getSimpleName();
 
-    private static final Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+    private static final Map<
+                    VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder>
             REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
 
     @NonNull private final Context mContext;
@@ -93,13 +94,13 @@
     }
 
     /**
-     * Get all currently registered VcnNetworkPolicyListeners for testing purposes.
+     * Get all currently registered VcnNetworkPolicyChangeListeners for testing purposes.
      *
      * @hide
      */
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     @NonNull
-    public static Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+    public static Map<VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder>
             getAllPolicyListeners() {
         return Collections.unmodifiableMap(REGISTERED_POLICY_LISTENERS);
     }
@@ -162,14 +163,14 @@
     }
 
     // TODO(b/180537630): remove all VcnUnderlyingNetworkPolicyListener refs once Telephony is using
-    // the new VcnNetworkPolicyListener API
+    // the new VcnNetworkPolicyChangeListener API
     /**
      * VcnUnderlyingNetworkPolicyListener is the interface through which internal system components
      * can register to receive updates for VCN-underlying Network policies from the System Server.
      *
      * @hide
      */
-    public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyListener {}
+    public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyChangeListener {}
 
     /**
      * Add a listener for VCN-underlying network policy updates.
@@ -185,7 +186,7 @@
     @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public void addVcnUnderlyingNetworkPolicyListener(
             @NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) {
-        addVcnNetworkPolicyListener(executor, listener);
+        addVcnNetworkPolicyChangeListener(executor, listener);
     }
 
     /**
@@ -198,7 +199,7 @@
      */
     public void removeVcnUnderlyingNetworkPolicyListener(
             @NonNull VcnUnderlyingNetworkPolicyListener listener) {
-        removeVcnNetworkPolicyListener(listener);
+        removeVcnNetworkPolicyChangeListener(listener);
     }
 
     /**
@@ -233,20 +234,20 @@
     }
 
     /**
-     * VcnNetworkPolicyListener is the interface through which internal system components (e.g.
-     * Network Factories) can register to receive updates for VCN-underlying Network policies from
-     * the System Server.
+     * VcnNetworkPolicyChangeListener is the interface through which internal system components
+     * (e.g. Network Factories) can register to receive updates for VCN-underlying Network policies
+     * from the System Server.
      *
      * <p>Any Network Factory that brings up Networks capable of being VCN-underlying Networks
-     * should register a VcnNetworkPolicyListener. VcnManager will then use this listener to notify
-     * the registrant when VCN Network policies change. Upon receiving this signal, the listener
-     * must check {@link VcnManager} for the current Network policy result for each of its Networks
-     * via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}.
+     * should register a VcnNetworkPolicyChangeListener. VcnManager will then use this listener to
+     * notify the registrant when VCN Network policies change. Upon receiving this signal, the
+     * listener must check {@link VcnManager} for the current Network policy result for each of its
+     * Networks via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}.
      *
      * @hide
      */
     @SystemApi
-    public interface VcnNetworkPolicyListener {
+    public interface VcnNetworkPolicyChangeListener {
         /**
          * Notifies the implementation that the VCN's underlying Network policy has changed.
          *
@@ -260,20 +261,21 @@
     /**
      * Add a listener for VCN-underlying Network policy updates.
      *
-     * <p>A {@link VcnNetworkPolicyListener} is eligible to begin receiving callbacks once it is
-     * registered. No callbacks are guaranteed upon registration.
+     * <p>A {@link VcnNetworkPolicyChangeListener} is eligible to begin receiving callbacks once it
+     * is registered. No callbacks are guaranteed upon registration.
      *
      * @param executor the Executor that will be used for invoking all calls to the specified
      *     Listener
-     * @param listener the VcnNetworkPolicyListener to be added
+     * @param listener the VcnNetworkPolicyChangeListener to be added
      * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
-     * @throws IllegalStateException if the specified VcnNetworkPolicyListener is already registered
+     * @throws IllegalStateException if the specified VcnNetworkPolicyChangeListener is already
+     *     registered
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
-    public void addVcnNetworkPolicyListener(
-            @NonNull Executor executor, @NonNull VcnNetworkPolicyListener listener) {
+    public void addVcnNetworkPolicyChangeListener(
+            @NonNull Executor executor, @NonNull VcnNetworkPolicyChangeListener listener) {
         requireNonNull(executor, "executor must not be null");
         requireNonNull(listener, "listener must not be null");
 
@@ -292,15 +294,18 @@
     }
 
     /**
-     * Remove the specified VcnNetworkPolicyListener from VcnManager.
+     * Remove the specified VcnNetworkPolicyChangeListener from VcnManager.
      *
      * <p>If the specified listener is not currently registered, this is a no-op.
      *
-     * @param listener the VcnNetworkPolicyListener that will be removed
+     * @param listener the VcnNetworkPolicyChangeListener that will be removed
+     * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
      * @hide
      */
     @SystemApi
-    public void removeVcnNetworkPolicyListener(@NonNull VcnNetworkPolicyListener listener) {
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    public void removeVcnNetworkPolicyChangeListener(
+            @NonNull VcnNetworkPolicyChangeListener listener) {
         requireNonNull(listener, "listener must not be null");
 
         VcnUnderlyingNetworkPolicyListenerBinder binder =
@@ -320,8 +325,9 @@
      * Applies the network policy for a {@link android.net.Network} with the given parameters.
      *
      * <p>Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy
-     * may have changed via {@link VcnNetworkPolicyListener#onPolicyChanged()}, a Network Provider
-     * MUST poll for the updated Network policy based on that Network's capabilities and properties.
+     * may have changed via {@link VcnNetworkPolicyChangeListener#onPolicyChanged()}, a Network
+     * Provider MUST poll for the updated Network policy based on that Network's capabilities and
+     * properties.
      *
      * @param networkCapabilities the NetworkCapabilities to be used in determining the Network
      *     policy result for this Network.
@@ -532,17 +538,18 @@
     }
 
     /**
-     * Binder wrapper for added VcnNetworkPolicyListeners to receive signals from System Server.
+     * Binder wrapper for added VcnNetworkPolicyChangeListeners to receive signals from System
+     * Server.
      *
      * @hide
      */
     private static class VcnUnderlyingNetworkPolicyListenerBinder
             extends IVcnUnderlyingNetworkPolicyListener.Stub {
         @NonNull private final Executor mExecutor;
-        @NonNull private final VcnNetworkPolicyListener mListener;
+        @NonNull private final VcnNetworkPolicyChangeListener mListener;
 
         private VcnUnderlyingNetworkPolicyListenerBinder(
-                Executor executor, VcnNetworkPolicyListener listener) {
+                Executor executor, VcnNetworkPolicyChangeListener listener) {
             mExecutor = executor;
             mListener = listener;
         }
diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java
index a86237d..97ec594 100644
--- a/core/java/android/os/BatteryManagerInternal.java
+++ b/core/java/android/os/BatteryManagerInternal.java
@@ -83,4 +83,29 @@
      * wait on the battery service lock.
      */
     public abstract int getInvalidCharger();
+
+    /**
+     * Sets battery AC charger to enabled/disabled, and freezes the battery state.
+     */
+    public abstract void setChargerAcOnline(boolean online, boolean forceUpdate);
+
+    /**
+     * Sets battery level, and freezes the battery state.
+     */
+    public abstract void setBatteryLevel(int level, boolean forceUpdate);
+
+    /**
+     * Unplugs battery, and freezes the battery state.
+     */
+    public abstract void unplugBattery(boolean forceUpdate);
+
+    /**
+     * Unfreezes battery state, returning to current hardware values.
+     */
+    public abstract void resetBattery(boolean forceUpdate);
+
+    /**
+     * Suspend charging even if plugged in.
+     */
+    public abstract void suspendBatteryInput();
 }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 66f7bd9..4c26e2f 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -995,6 +995,15 @@
         public abstract long getScreenOnMeasuredBatteryConsumptionUC();
 
         /**
+         * Returns the battery consumption (in microcoulombs) of the uid's cpu usage, derived from
+         * on device power measurement data.
+         * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+         *
+         * {@hide}
+         */
+        public abstract long getCpuMeasuredBatteryConsumptionUC();
+
+        /**
          * Returns the battery consumption (in microcoulombs) used by this uid for each
          * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
          * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}).
@@ -2521,6 +2530,15 @@
     public abstract long getScreenDozeMeasuredBatteryConsumptionUC();
 
     /**
+     * Returns the battery consumption (in microcoulombs) of the cpu, derived from on device power
+     * measurement data.
+     * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable.
+     *
+     * {@hide}
+     */
+    public abstract long getCpuMeasuredBatteryConsumptionUC();
+
+    /**
      * Returns the battery consumption (in microcoulombs) that each
      * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer
      * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}) consumed.
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 1905d70..e47478a 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -23,6 +23,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.net.NetworkStack;
 import android.os.connectivity.CellularBatteryStats;
@@ -487,4 +488,74 @@
         return isActive ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
                 : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
     }
-}
+
+    /**
+     * Sets battery AC charger to enabled/disabled, and freezes the battery state.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void setChargerAcOnline(boolean online, boolean forceUpdate) {
+        try {
+            mBatteryStats.setChargerAcOnline(online, forceUpdate);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Sets battery level, and freezes the battery state.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void setBatteryLevel(int level, boolean forceUpdate) {
+        try {
+            mBatteryStats.setBatteryLevel(level, forceUpdate);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unplugs battery, and freezes the battery state.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void unplugBattery(boolean forceUpdate) {
+        try {
+            mBatteryStats.unplugBattery(forceUpdate);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unfreezes battery state, returning to current hardware values.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void resetBattery(boolean forceUpdate) {
+        try {
+            mBatteryStats.resetBattery(forceUpdate);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Suspend charging even if plugged in.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void suspendBatteryInput() {
+        try {
+            mBatteryStats.suspendBatteryInput();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 83f78a5..0d9f715 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -27,6 +27,7 @@
 import android.app.Application;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.sysprop.DeviceProperties;
 import android.sysprop.SocProperties;
 import android.sysprop.TelephonyProperties;
 import android.text.TextUtils;
@@ -298,6 +299,19 @@
                 "ro.build.version.security_patch", "");
 
         /**
+         * The media performance class of the device or 0 if none.
+         * <p>
+         * If this value is not <code>0</code>, the device conforms to the media performance class
+         * definition of the SDK version of this value. This value never changes while a device is
+         * booted, but it may increase when the hardware manufacturer provides an OTA update.
+         * <p>
+         * Possible non-zero values are defined in {@link Build.VERSION_CODES} starting with
+         * {@link Build.VERSION_CODES#S}.
+         */
+        public static final int MEDIA_PERFORMANCE_CLASS =
+                DeviceProperties.media_performance_class().orElse(0);
+
+        /**
          * The user-visible SDK version of the framework in its raw String
          * representation; use {@link #SDK_INT} instead.
          *
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 124c0b0..21bf8b8 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -728,11 +728,11 @@
     /**
      * Standard directory in which to place any audio files that should be
      * in the regular list of music for the user.
-     * This may be combined with
+     * This may be combined with {@link #DIRECTORY_AUDIOBOOKS},
      * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
-     * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
-     * of directories to categories a particular audio file as more than one
-     * type.
+     * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES}, and
+     * {@link #DIRECTORY_RECORDINGS} as a series of directories to
+     * categorize a particular audio file as more than one type.
      */
     public static String DIRECTORY_MUSIC = "Music";
 
@@ -741,10 +741,10 @@
      * in the list of podcasts that the user can select (not as regular
      * music).
      * This may be combined with {@link #DIRECTORY_MUSIC},
-     * {@link #DIRECTORY_NOTIFICATIONS},
-     * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
-     * of directories to categories a particular audio file as more than one
-     * type.
+     * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_NOTIFICATIONS},
+     * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES}, and
+     * {@link #DIRECTORY_RECORDINGS} as a series of directories to
+     * categorize a particular audio file as more than one type.
      */
     public static String DIRECTORY_PODCASTS = "Podcasts";
 
@@ -753,10 +753,10 @@
      * in the list of ringtones that the user can select (not as regular
      * music).
      * This may be combined with {@link #DIRECTORY_MUSIC},
-     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS}, and
-     * {@link #DIRECTORY_ALARMS} as a series
-     * of directories to categories a particular audio file as more than one
-     * type.
+     * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS},
+     * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_ALARMS},
+     * and {@link #DIRECTORY_RECORDINGS} as a series of directories
+     * to categorize a particular audio file as more than one type.
      */
     public static String DIRECTORY_RINGTONES = "Ringtones";
 
@@ -765,10 +765,10 @@
      * in the list of alarms that the user can select (not as regular
      * music).
      * This may be combined with {@link #DIRECTORY_MUSIC},
-     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
-     * and {@link #DIRECTORY_RINGTONES} as a series
-     * of directories to categories a particular audio file as more than one
-     * type.
+     * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS},
+     * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_RINGTONES},
+     * and {@link #DIRECTORY_RECORDINGS} as a series of directories
+     * to categorize a particular audio file as more than one type.
      */
     public static String DIRECTORY_ALARMS = "Alarms";
 
@@ -777,10 +777,10 @@
      * in the list of notifications that the user can select (not as regular
      * music).
      * This may be combined with {@link #DIRECTORY_MUSIC},
-     * {@link #DIRECTORY_PODCASTS},
-     * {@link #DIRECTORY_ALARMS}, and {@link #DIRECTORY_RINGTONES} as a series
-     * of directories to categories a particular audio file as more than one
-     * type.
+     * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS},
+     * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES}, and
+     * {@link #DIRECTORY_RECORDINGS} as a series of directories to
+     * categorize a particular audio file as more than one type.
      */
     public static String DIRECTORY_NOTIFICATIONS = "Notifications";
 
@@ -831,14 +831,26 @@
     public static String DIRECTORY_SCREENSHOTS = "Screenshots";
 
     /**
-     * Standard directory in which to place any audio files which are
-     * audiobooks.
+     * Standard directory in which to place any audio files that should be
+     * in the list of audiobooks that the user can select (not as regular
+     * music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_PODCASTS}, {@link #DIRECTORY_NOTIFICATIONS},
+     * {@link #DIRECTORY_ALARMS}, {@link #DIRECTORY_RINGTONES},
+     * and {@link #DIRECTORY_RECORDINGS} as a series of directories
+     * to categorize a particular audio file as more than one type.
      */
     public static String DIRECTORY_AUDIOBOOKS = "Audiobooks";
 
     /**
-     * Standard directory in which to place any audio files which are
-     * recordings.
+     * Standard directory in which to place any audio files that should be
+     * in the list of voice recordings recorded by voice recorder apps that
+     * the user can select (not as regular music).
+     * This may be combined with {@link #DIRECTORY_MUSIC},
+     * {@link #DIRECTORY_AUDIOBOOKS}, {@link #DIRECTORY_PODCASTS},
+     * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_ALARMS},
+     * and {@link #DIRECTORY_RINGTONES} as a series of directories
+     * to categorize a particular audio file as more than one type.
      */
     @NonNull
     // The better way is that expose a static method getRecordingDirectories.
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index dc6f63a..047c05a 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -30,6 +30,7 @@
 import com.android.internal.annotations.GuardedBy;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -265,6 +266,13 @@
     }
 
     /**
+     * Checks if an fd corresponds to a file on a mounted Incremental File System.
+     */
+    public static boolean isIncrementalFileFd(@NonNull FileDescriptor fd) {
+        return nativeIsIncrementalFd(fd.getInt$());
+    }
+
+    /**
      * Returns raw signature for file if it's on Incremental File System.
      * Unsafe, use only if you are sure what you are doing.
      */
@@ -421,9 +429,22 @@
         storage.unregisterStorageHealthListener();
     }
 
+    /**
+     * Returns the metrics of an Incremental Storage.
+     */
+    public IncrementalMetrics getMetrics(@NonNull String codePath) {
+        final IncrementalStorage storage = openStorage(codePath);
+        if (storage == null) {
+            // storage does not exist, package not installed
+            return null;
+        }
+        return new IncrementalMetrics(storage.getMetrics());
+    }
+
     /* Native methods */
     private static native boolean nativeIsEnabled();
     private static native boolean nativeIsV2Available();
     private static native boolean nativeIsIncrementalPath(@NonNull String path);
+    private static native boolean nativeIsIncrementalFd(@NonNull int fd);
     private static native byte[] nativeUnsafeGetFileSignature(@NonNull String path);
 }
diff --git a/core/java/android/os/incremental/IncrementalMetrics.java b/core/java/android/os/incremental/IncrementalMetrics.java
new file mode 100644
index 0000000..44dea1b
--- /dev/null
+++ b/core/java/android/os/incremental/IncrementalMetrics.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os.incremental;
+
+import android.annotation.NonNull;
+import android.os.PersistableBundle;
+
+/**
+ * Provides methods to access metrics about an app installed via Incremental
+ * @hide
+ */
+public class IncrementalMetrics {
+    @NonNull private final PersistableBundle mData;
+
+    public IncrementalMetrics(@NonNull PersistableBundle data) {
+        mData = data;
+    }
+
+    /**
+     * @return Milliseconds between now and when the oldest pending read happened
+     */
+    public long getMillisSinceOldestPendingRead() {
+        return mData.getLong(IIncrementalService.METRICS_MILLIS_SINCE_OLDEST_PENDING_READ, -1);
+    }
+}
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index e6ce8cd..7cf0144 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.pm.DataLoaderParams;
 import android.content.pm.IDataLoaderStatusListener;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 
 import java.io.File;
@@ -601,4 +602,17 @@
             return;
         }
     }
+
+    /**
+     * Returns the metrics of the current storage.
+     * {@see IIncrementalService} for metrics keys.
+     */
+    public PersistableBundle getMetrics() {
+        try {
+            return mService.getMetrics(mId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return null;
+        }
+    }
 }
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 8a4812a..374de9c 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5310,5 +5310,12 @@
          * @hide
          */
         public static final String COLUMN_VOIMS_OPT_IN_STATUS = "voims_opt_in_status";
+
+        /**
+         * TelephonyProvider column name for device to device sharing status.
+         *
+         * @hide
+         */
+        public static final String COLUMN_D2D_STATUS_SHARING = "d2d_sharing_status";
     }
 }
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index c1d9d58..def13db 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -41,10 +41,12 @@
 import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.SharedMemory;
 import android.util.Slog;
 
 import com.android.internal.app.IHotwordRecognitionStatusCallback;
@@ -287,6 +289,7 @@
     private final Handler mHandler;
     private final IBinder mBinder = new Binder();
     private final int mTargetSdkVersion;
+    private final boolean mSupportHotwordDetectionService;
 
     private int mAvailability = STATE_NOT_READY;
 
@@ -488,11 +491,22 @@
      * @param callback A non-null Callback for receiving the recognition events.
      * @param modelManagementService A service that allows management of sound models.
      * @param targetSdkVersion The target SDK version.
+     * @param supportHotwordDetectionService {@code true} if hotword detection service should be
+     * triggered, otherwise {@code false}.
+     * @param options Application configuration data provided by the
+     * {@link VoiceInteractionService}. The system strips out any remotable objects or other
+     * contents that can be used to communicate with other processes.
+     * @param sharedMemory The unrestricted data blob provided by the
+     * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+     * such data to the trusted process.
+     *
      * @hide
      */
     public AlwaysOnHotwordDetector(String text, Locale locale, Callback callback,
             KeyphraseEnrollmentInfo keyphraseEnrollmentInfo,
-            IVoiceInteractionManagerService modelManagementService, int targetSdkVersion) {
+            IVoiceInteractionManagerService modelManagementService, int targetSdkVersion,
+            boolean supportHotwordDetectionService, @Nullable Bundle options,
+            @Nullable SharedMemory sharedMemory) {
         mText = text;
         mLocale = locale;
         mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo;
@@ -501,6 +515,10 @@
         mInternalCallback = new SoundTriggerListener(mHandler);
         mModelManagementService = modelManagementService;
         mTargetSdkVersion = targetSdkVersion;
+        mSupportHotwordDetectionService = supportHotwordDetectionService;
+        if (mSupportHotwordDetectionService) {
+            setHotwordDetectionServiceConfig(options, sharedMemory);
+        }
         try {
             Identity identity = new Identity();
             identity.packageName = ActivityThread.currentOpPackageName();
@@ -513,6 +531,38 @@
     }
 
     /**
+     * Set configuration and pass read-only data to hotword detection service.
+     *
+     * @param options Application configuration data provided by the
+     * {@link VoiceInteractionService}. The system strips out any remotable objects or other
+     * contents that can be used to communicate with other processes.
+     * @param sharedMemory The unrestricted data blob provided by the
+     * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+     * such data to the trusted process.
+     *
+     * @throws IllegalStateException if it doesn't support hotword detection service.
+     *
+     * @hide
+     */
+    public final void setHotwordDetectionServiceConfig(@Nullable Bundle options,
+            @Nullable SharedMemory sharedMemory) {
+        if (DBG) {
+            Slog.d(TAG, "setHotwordDetectionServiceConfig()");
+        }
+        if (!mSupportHotwordDetectionService) {
+            throw new IllegalStateException(
+                    "setHotwordDetectionServiceConfig called, but it doesn't support hotword"
+                            + " detection service");
+        }
+
+        try {
+            mModelManagementService.setHotwordDetectionServiceConfig(options, sharedMemory);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Gets the recognition modes supported by the associated keyphrase.
      *
      * @see #RECOGNITION_MODE_USER_IDENTIFICATION
@@ -839,6 +889,14 @@
         synchronized (mLock) {
             mAvailability = STATE_INVALID;
             notifyStateChangedLocked();
+
+            if (mSupportHotwordDetectionService) {
+                try {
+                    mModelManagementService.shutdownHotwordDetectionService();
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
         }
     }
 
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index 7f1c5ff96..fcef26f 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -27,13 +27,17 @@
 import android.app.Service;
 import android.content.Intent;
 import android.media.AudioFormat;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.SharedMemory;
 import android.util.Log;
 
+import java.util.Locale;
+
 /**
  * Implemented by an application that wants to offer detection for hotword. The system will
  * start the service after calling {@link VoiceInteractionService#setHotwordDetectionConfig}.
@@ -76,6 +80,17 @@
                     timeoutMillis,
                     new DspHotwordDetectionCallback(callback)));
         }
+
+        @Override
+        public void setConfig(Bundle options, SharedMemory sharedMemory) throws RemoteException {
+            if (DBG) {
+                Log.d(TAG, "#setConfig");
+            }
+            mHandler.sendMessage(obtainMessage(HotwordDetectionService::onUpdateState,
+                    HotwordDetectionService.this,
+                    options,
+                    sharedMemory));
+        }
     };
 
     @CallSuper
@@ -121,6 +136,25 @@
     }
 
     /**
+     * Called when the {@link VoiceInteractionService#createAlwaysOnHotwordDetector(String, Locale,
+     * Bundle, SharedMemory, AlwaysOnHotwordDetector.Callback)} or {@link AlwaysOnHotwordDetector#
+     * setHotwordDetectionServiceConfig(Bundle, SharedMemory)} requests an update of the hotword
+     * detection parameters.
+     *
+     * @param options Application configuration data provided by the
+     * {@link VoiceInteractionService}. The system strips out any remotable objects or other
+     * contents that can be used to communicate with other processes.
+     * @param sharedMemory The unrestricted data blob provided by the
+     * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+     * such data to the trusted process.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void onUpdateState(@Nullable Bundle options, @Nullable SharedMemory sharedMemory) {
+    }
+
+    /**
      * Callback for returning the detected result.
      *
      * @hide
diff --git a/core/java/android/service/voice/IHotwordDetectionService.aidl b/core/java/android/service/voice/IHotwordDetectionService.aidl
index cbe76e4..8f0874a 100644
--- a/core/java/android/service/voice/IHotwordDetectionService.aidl
+++ b/core/java/android/service/voice/IHotwordDetectionService.aidl
@@ -17,7 +17,9 @@
 package android.service.voice;
 
 import android.media.AudioFormat;
+import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.os.SharedMemory;
 import android.service.voice.IDspHotwordDetectionCallback;
 
 /**
@@ -31,4 +33,6 @@
     in AudioFormat audioFormat,
     long timeoutMillis,
     in IDspHotwordDetectionCallback callback);
+
+    void setConfig(in Bundle options, in SharedMemory sharedMemory);
 }
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 25f8090..048d9f5 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -17,7 +17,6 @@
 package android.service.voice;
 
 import android.Manifest;
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -36,6 +35,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SharedMemory;
 import android.provider.Settings;
 import android.util.ArraySet;
 import android.util.Log;
@@ -47,8 +47,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -73,32 +71,6 @@
     static final String TAG = VoiceInteractionService.class.getSimpleName();
 
     /**
-     * Indicates that the given configs have been set successfully after calling
-     * {@link VoiceInteractionService#setHotwordDetectionConfig}.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int HOTWORD_CONFIG_SUCCESS = 0;
-
-    /**
-     * Indicates that the given configs have been set unsuccessfully after calling
-     * {@link VoiceInteractionService#setHotwordDetectionConfig}.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final int HOTWORD_CONFIG_FAILURE = 1;
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true, prefix = { "HOTWORD_CONFIG_" }, value = {
-            HOTWORD_CONFIG_SUCCESS,
-            HOTWORD_CONFIG_FAILURE,
-    })
-    public @interface HotwordConfigResult {}
-
-    /**
      * The {@link Intent} that must be declared as handled by the service.
      * To be supported, the service must also require the
      * {@link android.Manifest.permission#BIND_VOICE_INTERACTION} permission so
@@ -330,35 +302,6 @@
     }
 
     /**
-     * Set hotword detection configuration.
-     *
-     * Note: Currently it will trigger hotword detection service after calling this function when
-     * all conditions meet the requirements.
-     *
-     * @param options Config data.
-     * @return {@link VoiceInteractionService#HOTWORD_CONFIG_SUCCESS} in case of success,
-     * {@link VoiceInteractionService#HOTWORD_CONFIG_FAILURE} in case of failure.
-     *
-     * @throws IllegalStateException if the function is called before onReady() is called.
-     *
-     * @hide
-     */
-    @SystemApi
-    @HotwordConfigResult
-    public final int setHotwordDetectionConfig(
-            @SuppressLint("NullableCollection") @Nullable Bundle options) {
-        if (mSystemService == null) {
-            throw new IllegalStateException("Not available until onReady() is called");
-        }
-
-        try {
-            return mSystemService.setHotwordDetectionConfig(options);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
      * This instance must be retained and used by the client.
      * Calling this a second time invalidates the previously created hotword detector
@@ -374,9 +317,61 @@
     @SystemApi
     @NonNull
     public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(
-            @SuppressLint("MissingNullability") String keyphrase,  // TODO: annotate nullability properly
+            @SuppressLint("MissingNullability") String keyphrase,  // TODO: nullability properly
             @SuppressLint({"MissingNullability", "UseIcu"}) Locale locale,
             @SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
+        return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
+                /* supportHotwordDetectionService= */ false, /* options= */ null,
+                /* sharedMemory= */ null, callback);
+    }
+
+    /**
+     * Create an {@link AlwaysOnHotwordDetector} and trigger a {@link HotwordDetectionService}
+     * service, then it will also pass the read-only data to hotword detection service.
+     *
+     * Like {@see #createAlwaysOnHotwordDetector(String, Locale, AlwaysOnHotwordDetector.Callback)
+     * }. Before calling this function, you should set a valid hotword detection service with
+     * android:hotwordDetectionService in an android.voice_interaction metadata file and set
+     * android:isolatedProcess="true" in the AndroidManifest.xml of hotword detection service.
+     * Otherwise it will throw IllegalStateException. After calling this function, the system will
+     * also trigger a hotword detection service and pass the read-only data back to it.
+     *
+     * <p>Note: The system will trigger hotword detection service after calling this function when
+     * all conditions meet the requirements.
+     *
+     * @param keyphrase The keyphrase that's being used, for example "Hello Android".
+     * @param locale The locale for which the enrollment needs to be performed.
+     * @param options Application configuration data provided by the
+     * {@link VoiceInteractionService}. The system strips out any remotable objects or other
+     * contents that can be used to communicate with other processes.
+     * @param sharedMemory The unrestricted data blob provided by the
+     * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+     * such data to the trusted process.
+     * @param callback The callback to notify of detection events.
+     * @return An always-on hotword detector for the given keyphrase and locale.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(
+            @SuppressLint("MissingNullability") String keyphrase,  // TODO: nullability properly
+            @SuppressLint({"MissingNullability", "UseIcu"}) Locale locale,
+            @Nullable Bundle options,
+            @Nullable SharedMemory sharedMemory,
+            @SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
+        return createAlwaysOnHotwordDetectorInternal(keyphrase, locale,
+                /* supportHotwordDetectionService= */ true, options,
+                sharedMemory, callback);
+    }
+
+    private AlwaysOnHotwordDetector createAlwaysOnHotwordDetectorInternal(
+            @SuppressLint("MissingNullability") String keyphrase,  // TODO: nullability properly
+            @SuppressLint({"MissingNullability", "UseIcu"}) Locale locale,
+            boolean supportHotwordDetectionService,
+            @Nullable Bundle options,
+            @Nullable SharedMemory sharedMemory,
+            @SuppressLint("MissingNullability") AlwaysOnHotwordDetector.Callback callback) {
         if (mSystemService == null) {
             throw new IllegalStateException("Not available until onReady() is called");
         }
@@ -385,7 +380,8 @@
             safelyShutdownHotwordDetector();
             mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback,
                     mKeyphraseEnrollmentInfo, mSystemService,
-                    getApplicationContext().getApplicationInfo().targetSdkVersion);
+                    getApplicationContext().getApplicationInfo().targetSdkVersion,
+                    supportHotwordDetectionService, options, sharedMemory);
         }
         return mHotwordDetector;
     }
@@ -432,7 +428,6 @@
     }
 
     private void safelyShutdownHotwordDetector() {
-        // TODO (b/178171906): Need to check if the HotwordDetectionService should be unbound.
         synchronized (mLock) {
             if (mHotwordDetector == null) {
                 return;
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index bbe887f..e9a79e7 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.compat.annotation.ChangeId;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Binder;
 import android.os.Build;
@@ -1577,7 +1576,7 @@
             // default implementation empty
         }
 
-        public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) {
+        public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) {
             // default implementation empty
         }
     }
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index 2cadda2..e3d3dec 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -546,9 +546,6 @@
     /**
      * Event for changes to allowed network list based on all active subscriptions.
      *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
-     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
-     *
      * @hide
      * @see AllowedNetworkTypesListener#onAllowedNetworkTypesChanged
      */
@@ -1265,30 +1262,34 @@
     public interface AllowedNetworkTypesListener {
         /**
          * Callback invoked when the current allowed network type list has changed on the
-         * registered subscription.
+         * registered subscription for a specified reason.
          * Note, the registered subscription is associated with {@link TelephonyManager} object
-         * on which
-         * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}
+         * on which {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)}
          * was called.
          * If this TelephonyManager object was created with
          * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
          * given subscription ID. Otherwise, this callback applies to
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          *
-         * @param allowedNetworkTypesList Map associating all allowed network type reasons
-         * ({@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER},
-         * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER},
-         * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER}, and
-         * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}) with reason's allowed
-         * network type values.
+         * @param reason an allowed network type reasons.
+         * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER
+         * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER
+         * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER
+         * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G
+         *
+         * @param allowedNetworkType an allowed network type bitmask value. (for example,
+         * the long bitmask value is {{@link TelephonyManager#NETWORK_TYPE_BITMASK_NR}|
+         * {@link TelephonyManager#NETWORK_TYPE_BITMASK_LTE}})
+         *
          * For example:
-         * map{{TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER, long type value},
-         *     {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER, long type value},
-         *     {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER, long type value},
-         *     {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, long type value}}
+         * If the latest allowed network type is changed by user, then the system
+         * notifies the {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER} and
+         * long type value}.
          */
         @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
-        void onAllowedNetworkTypesChanged(@NonNull Map<Integer, Long> allowedNetworkTypesList);
+        void onAllowedNetworkTypesChanged(
+                @TelephonyManager.AllowedNetworkTypesReason int reason,
+                @TelephonyManager.NetworkTypeBitMask long allowedNetworkType);
     }
 
     /**
@@ -1707,14 +1708,15 @@
                             enabled, reason)));
         }
 
-        public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) {
+        public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) {
             AllowedNetworkTypesListener listener =
                     (AllowedNetworkTypesListener) mTelephonyCallbackWeakRef.get();
             if (listener == null) return;
 
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(
-                            () -> listener.onAllowedNetworkTypesChanged(allowedNetworkTypesList)));
+                            () -> listener.onAllowedNetworkTypesChanged(reason,
+                                    allowedNetworkType)));
         }
     }
 }
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 9cda4ae..3fa63d8 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -825,16 +825,18 @@
     }
 
     /**
-     * Notify emergency number list changed on certain subscription.
-     *
-     * @param slotIndex for which emergency number list changed. Can be derived from subId except
-     * when subId is invalid.
-     * @param subId for which emergency number list changed.
+     * Notify the allowed network types has changed for a specific subscription and the specific
+     * reason.
+     * @param slotIndex for which allowed network types changed.
+     * @param subId for which allowed network types changed.
+     * @param reason an allowed network type reasons.
+     * @param allowedNetworkType an allowed network type bitmask value.
      */
     public void notifyAllowedNetworkTypesChanged(int slotIndex, int subId,
-            Map<Integer, Long> allowedNetworkTypeList) {
+            int reason, long allowedNetworkType) {
         try {
-            sRegistry.notifyAllowedNetworkTypesChanged(slotIndex, subId, allowedNetworkTypeList);
+            sRegistry.notifyAllowedNetworkTypesChanged(slotIndex, subId, reason,
+                    allowedNetworkType);
         } catch (RemoteException ex) {
             // system process is dead
         }
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index c97c995..7e6175c 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -200,10 +200,9 @@
         // physical memory.
 
         DataSource beforeApkSigningBlock =
-                new MemoryMappedFileDataSource(apkFileDescriptor, 0,
-                        signatureInfo.apkSigningBlockOffset);
+                DataSource.create(apkFileDescriptor, 0, signatureInfo.apkSigningBlockOffset);
         DataSource centralDir =
-                new MemoryMappedFileDataSource(
+                DataSource.create(
                         apkFileDescriptor, signatureInfo.centralDirOffset,
                         signatureInfo.eocdOffset - signatureInfo.centralDirOffset);
 
diff --git a/core/java/android/util/apk/DataSource.java b/core/java/android/util/apk/DataSource.java
index 82f3800..dd6389d 100644
--- a/core/java/android/util/apk/DataSource.java
+++ b/core/java/android/util/apk/DataSource.java
@@ -16,6 +16,10 @@
 
 package android.util.apk;
 
+import android.annotation.NonNull;
+import android.os.incremental.IncrementalManager;
+
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.security.DigestException;
 
@@ -35,4 +39,22 @@
      */
     void feedIntoDataDigester(DataDigester md, long offset, int size)
             throws IOException, DigestException;
+
+    /**
+     * Creates a DataSource that can handle the passed fd in the most efficient and safe manner.
+     * @param fd file descriptor to read from
+     * @param pos starting offset
+     * @param size size of the region
+     * @return created DataSource object
+     */
+    static @NonNull DataSource create(@NonNull FileDescriptor fd, long pos, long size) {
+        if (IncrementalManager.isIncrementalFileFd(fd)) {
+            // IncFS-based files may have missing pages, and reading those via mmap() results
+            // in a SIGBUS signal. Java doesn't have a good way of catching it, ending up killing
+            // the process by default. Going back to read() is the safest option for these files.
+            return new ReadFileDataSource(fd, pos, size);
+        } else {
+            return new MemoryMappedFileDataSource(fd, pos, size);
+        }
+    }
 }
diff --git a/core/java/android/util/apk/MemoryMappedFileDataSource.java b/core/java/android/util/apk/MemoryMappedFileDataSource.java
index 8d2b1e32..69a526d 100644
--- a/core/java/android/util/apk/MemoryMappedFileDataSource.java
+++ b/core/java/android/util/apk/MemoryMappedFileDataSource.java
@@ -40,6 +40,7 @@
     /**
      * Constructs a new {@code MemoryMappedFileDataSource} for the specified region of the file.
      *
+     * @param fd file descriptor to read from.
      * @param position start position of the region in the file.
      * @param size size (in bytes) of the region.
      */
diff --git a/core/java/android/util/apk/ReadFileDataSource.java b/core/java/android/util/apk/ReadFileDataSource.java
new file mode 100644
index 0000000..d0e1140
--- /dev/null
+++ b/core/java/android/util/apk/ReadFileDataSource.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.apk;
+
+import android.system.ErrnoException;
+import android.system.Os;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.DigestException;
+
+/**
+ * {@link DataSource} which provides data from a file descriptor by reading the sections
+ * of the file via raw read() syscall. This is slower than memory-mapping but safer.
+ */
+class ReadFileDataSource implements DataSource {
+    private final FileDescriptor mFd;
+    private final long mFilePosition;
+    private final long mSize;
+
+    private static final int CHUNK_SIZE = 1024 * 1024;
+
+    /**
+     * Constructs a new {@code ReadFileDataSource} for the specified region of the file.
+     *
+     * @param fd file descriptor to read from.
+     * @param position start position of the region in the file.
+     * @param size size (in bytes) of the region.
+     */
+    ReadFileDataSource(FileDescriptor fd, long position, long size) {
+        mFd = fd;
+        mFilePosition = position;
+        mSize = size;
+    }
+
+    @Override
+    public long size() {
+        return mSize;
+    }
+
+    @Override
+    public void feedIntoDataDigester(DataDigester md, long offset, int size)
+            throws IOException, DigestException {
+        try {
+            final byte[] buffer = new byte[Math.min(size, CHUNK_SIZE)];
+            final long start = mFilePosition + offset;
+            final long end = start + size;
+            for (long pos = start, curSize = Math.min(size, CHUNK_SIZE);
+                    pos < end; curSize = Math.min(end - pos, CHUNK_SIZE)) {
+                final int readSize = Os.pread(mFd, buffer, 0, (int) curSize, pos);
+                md.consume(ByteBuffer.wrap(buffer, 0, readSize));
+                pos += readSize;
+            }
+        } catch (ErrnoException e) {
+            throw new IOException(e);
+        }
+    }
+}
diff --git a/core/java/android/util/apk/TEST_MAPPING b/core/java/android/util/apk/TEST_MAPPING
index 8544e82..4598b4f 100644
--- a/core/java/android/util/apk/TEST_MAPPING
+++ b/core/java/android/util/apk/TEST_MAPPING
@@ -1,6 +1,17 @@
 {
   "presubmit": [
     {
+      "name": "CtsContentTestCases",
+      "options": [
+        {
+          "include-filter": "android.content.pm.cts.PackageManagerShellCommandIncrementalTest"
+        },
+        {
+          "include-filter": "android.content.pm.cts.PackageManagerShellCommandTest"
+        }
+      ]
+    },
+    {
       "name": "FrameworksCoreTests",
       "options": [
         {
diff --git a/core/java/android/util/apk/VerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java
index 4596c6e..b0a5992 100644
--- a/core/java/android/util/apk/VerityBuilder.java
+++ b/core/java/android/util/apk/VerityBuilder.java
@@ -294,7 +294,7 @@
 
         // 1. Digest the whole file by chunks.
         consumeByChunk(digester,
-                new MemoryMappedFileDataSource(file.getFD(), 0, file.length()),
+                DataSource.create(file.getFD(), 0, file.length()),
                 MMAP_REGION_SIZE_BYTES);
 
         // 2. Pad 0s up to the nearest 4096-byte block before hashing.
@@ -315,7 +315,7 @@
 
         // 1. Digest from the beginning of the file, until APK Signing Block is reached.
         consumeByChunk(digester,
-                new MemoryMappedFileDataSource(apk.getFD(), 0, signatureInfo.apkSigningBlockOffset),
+                DataSource.create(apk.getFD(), 0, signatureInfo.apkSigningBlockOffset),
                 MMAP_REGION_SIZE_BYTES);
 
         // 2. Skip APK Signing Block and continue digesting, until the Central Directory offset
@@ -323,7 +323,7 @@
         long eocdCdOffsetFieldPosition =
                 signatureInfo.eocdOffset + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET;
         consumeByChunk(digester,
-                new MemoryMappedFileDataSource(apk.getFD(), signatureInfo.centralDirOffset,
+                DataSource.create(apk.getFD(), signatureInfo.centralDirOffset,
                     eocdCdOffsetFieldPosition - signatureInfo.centralDirOffset),
                 MMAP_REGION_SIZE_BYTES);
 
@@ -338,7 +338,7 @@
         long offsetAfterEocdCdOffsetField =
                 eocdCdOffsetFieldPosition + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE;
         consumeByChunk(digester,
-                new MemoryMappedFileDataSource(apk.getFD(), offsetAfterEocdCdOffsetField,
+                DataSource.create(apk.getFD(), offsetAfterEocdCdOffsetField,
                     apk.length() - offsetAfterEocdCdOffsetField),
                 MMAP_REGION_SIZE_BYTES);
 
diff --git a/core/java/android/util/imetracing/ImeTracing.java b/core/java/android/util/imetracing/ImeTracing.java
index 49ff237..b28cfb8 100644
--- a/core/java/android/util/imetracing/ImeTracing.java
+++ b/core/java/android/util/imetracing/ImeTracing.java
@@ -23,7 +23,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
-import android.os.ShellCommand;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 import android.view.inputmethod.InputMethodManager;
@@ -104,12 +103,6 @@
     public abstract void addToBuffer(ProtoOutputStream proto, int source);
 
     /**
-     * @param shell The shell command to process
-     * @return {@code 0} if the command was successfully processed, {@code -1} otherwise
-     */
-    public abstract int onShellCommand(ShellCommand shell);
-
-    /**
      * Starts a proto dump of the client side information.
      *
      * @param where Place where the trace was triggered.
diff --git a/core/java/android/util/imetracing/ImeTracingClientImpl.java b/core/java/android/util/imetracing/ImeTracingClientImpl.java
index 2c27639..35a81b7 100644
--- a/core/java/android/util/imetracing/ImeTracingClientImpl.java
+++ b/core/java/android/util/imetracing/ImeTracingClientImpl.java
@@ -20,7 +20,6 @@
 import android.inputmethodservice.AbstractInputMethodService;
 import android.os.RemoteException;
 import android.os.ServiceManager.ServiceNotFoundException;
-import android.os.ShellCommand;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 import android.view.inputmethod.InputMethodManager;
@@ -45,11 +44,6 @@
     }
 
     @Override
-    public int onShellCommand(ShellCommand shell) {
-        return -1;
-    }
-
-    @Override
     public void triggerClientDump(String where, @NonNull InputMethodManager immInstance,
             ProtoOutputStream icProto) {
         if (!isEnabled() || !isAvailable()) {
diff --git a/core/java/android/util/imetracing/ImeTracingServerImpl.java b/core/java/android/util/imetracing/ImeTracingServerImpl.java
index e793c28..77f017a 100644
--- a/core/java/android/util/imetracing/ImeTracingServerImpl.java
+++ b/core/java/android/util/imetracing/ImeTracingServerImpl.java
@@ -22,7 +22,6 @@
 import android.inputmethodservice.AbstractInputMethodService;
 import android.os.RemoteException;
 import android.os.ServiceManager.ServiceNotFoundException;
-import android.os.ShellCommand;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceFileProto;
@@ -106,32 +105,6 @@
         }
     }
 
-    /**
-     * Responds to a shell command of the format "adb shell cmd input_method ime tracing <command>"
-     *
-     * @param shell The shell command to process
-     * @return {@code 0} if the command was valid and successfully processed, {@code -1} otherwise
-     */
-    @Override
-    public int onShellCommand(ShellCommand shell) {
-        PrintWriter pw = shell.getOutPrintWriter();
-        String cmd = shell.getNextArgRequired();
-        switch (cmd) {
-            case "start":
-                startTrace(pw);
-                return 0;
-            case "stop":
-                stopTrace(pw);
-                return 0;
-            default:
-                pw.println("Unknown command: " + cmd);
-                pw.println("Input method trace options:");
-                pw.println("  start: Start tracing");
-                pw.println("  stop: Stop tracing");
-                return -1;
-        }
-    }
-
     @Override
     public void triggerClientDump(String where, InputMethodManager immInstance,
             ProtoOutputStream icProto) {
diff --git a/core/java/android/uwb/AngleMeasurement.java b/core/java/android/uwb/AngleMeasurement.java
index 9df213b..8c771ba 100644
--- a/core/java/android/uwb/AngleMeasurement.java
+++ b/core/java/android/uwb/AngleMeasurement.java
@@ -38,9 +38,30 @@
     private final double mErrorRadians;
     private final double mConfidenceLevel;
 
-    private AngleMeasurement(double radians, double errorRadians, double confidenceLevel) {
+    /**
+     * Constructs a new {@link AngleMeasurement} object
+     *
+     * @param radians the angle in radians
+     * @param errorRadians the error of the angle measurement in radians
+     * @param confidenceLevel confidence level of the angle measurement
+     *
+     * @throws IllegalArgumentException if the radians, errorRadians, or confidenceLevel is out of
+     *                                  allowed range
+     */
+    public AngleMeasurement(double radians, double errorRadians, double confidenceLevel) {
+        if (radians < -Math.PI || radians > Math.PI) {
+            throw new IllegalArgumentException("Invalid radians: " + radians);
+        }
         mRadians = radians;
+
+        if (errorRadians < 0.0 || errorRadians > Math.PI) {
+            throw new IllegalArgumentException("Invalid error radians: " + errorRadians);
+        }
         mErrorRadians = errorRadians;
+
+        if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
+            throw new IllegalArgumentException("Invalid confidence level: " + confidenceLevel);
+        }
         mConfidenceLevel = confidenceLevel;
     }
 
@@ -122,11 +143,7 @@
             new Creator<AngleMeasurement>() {
                 @Override
                 public AngleMeasurement createFromParcel(Parcel in) {
-                    Builder builder = new Builder();
-                    builder.setRadians(in.readDouble());
-                    builder.setErrorRadians(in.readDouble());
-                    builder.setConfidenceLevel(in.readDouble());
-                    return builder.build();
+                    return new AngleMeasurement(in.readDouble(), in.readDouble(), in.readDouble());
                 }
 
                 @Override
@@ -134,82 +151,4 @@
                     return new AngleMeasurement[size];
                 }
     };
-
-    /**
-     * Builder class for {@link AngleMeasurement}.
-     */
-    public static final class Builder {
-        private double mRadians = Double.NaN;
-        private double mErrorRadians = Double.NaN;
-        private double mConfidenceLevel = Double.NaN;
-
-        /**
-         * Set the angle in radians
-         *
-         * @param radians angle in radians
-         * @throws IllegalArgumentException if angle exceeds allowed limits of [-Math.PI, +Math.PI]
-         */
-        @NonNull
-        public Builder setRadians(double radians) {
-            if (radians < -Math.PI || radians > Math.PI) {
-                throw new IllegalArgumentException("Invalid radians: " + radians);
-            }
-            mRadians = radians;
-            return this;
-        }
-
-        /**
-         * Set the angle error in radians
-         *
-         * @param errorRadians error of the angle in radians
-         * @throws IllegalArgumentException if the error exceeds the allowed limits of [0, +Math.PI]
-         */
-        @NonNull
-        public Builder setErrorRadians(double errorRadians) {
-            if (errorRadians < 0.0 || errorRadians > Math.PI) {
-                throw new IllegalArgumentException(
-                        "Invalid error radians: " + errorRadians);
-            }
-            mErrorRadians = errorRadians;
-            return this;
-        }
-
-        /**
-         * Set the angle confidence level
-         *
-         * @param confidenceLevel level of confidence of the angle measurement
-         * @throws IllegalArgumentException if the error exceeds the allowed limits of [0.0, 1.0]
-         */
-        @NonNull
-        public Builder setConfidenceLevel(double confidenceLevel) {
-            if (confidenceLevel < 0.0 || confidenceLevel > 1.0) {
-                throw new IllegalArgumentException(
-                        "Invalid confidence level: " + confidenceLevel);
-            }
-            mConfidenceLevel = confidenceLevel;
-            return this;
-        }
-
-        /**
-         * Build the {@link AngleMeasurement} object
-         *
-         * @throws IllegalStateException if angle, error, or confidence values are missing
-         */
-        @NonNull
-        public AngleMeasurement build() {
-            if (Double.isNaN(mRadians)) {
-                throw new IllegalStateException("Angle is not set");
-            }
-
-            if (Double.isNaN(mErrorRadians)) {
-                throw new IllegalStateException("Angle error is not set");
-            }
-
-            if (Double.isNaN(mConfidenceLevel)) {
-                throw new IllegalStateException("Angle confidence level is not set");
-            }
-
-            return new AngleMeasurement(mRadians, mErrorRadians, mConfidenceLevel);
-        }
-    }
 }
diff --git a/core/java/android/uwb/AngleOfArrivalMeasurement.java b/core/java/android/uwb/AngleOfArrivalMeasurement.java
index 3d8626b..db04ad1 100644
--- a/core/java/android/uwb/AngleOfArrivalMeasurement.java
+++ b/core/java/android/uwb/AngleOfArrivalMeasurement.java
@@ -116,9 +116,8 @@
             new Creator<AngleOfArrivalMeasurement>() {
                 @Override
                 public AngleOfArrivalMeasurement createFromParcel(Parcel in) {
-                    Builder builder = new Builder();
-
-                    builder.setAzimuth(in.readParcelable(AngleMeasurement.class.getClassLoader()));
+                    Builder builder =
+                            new Builder(in.readParcelable(AngleMeasurement.class.getClassLoader()));
 
                     builder.setAltitude(in.readParcelable(AngleMeasurement.class.getClassLoader()));
 
@@ -135,18 +134,16 @@
      * Builder class for {@link AngleOfArrivalMeasurement}.
      */
     public static final class Builder {
-        private AngleMeasurement mAzimuthAngleMeasurement = null;
+        private final AngleMeasurement mAzimuthAngleMeasurement;
         private AngleMeasurement mAltitudeAngleMeasurement = null;
 
         /**
-         * Set the azimuth angle
+         * Constructs an {@link AngleOfArrivalMeasurement} object
          *
-         * @param azimuthAngle azimuth angle
+         * @param azimuthAngle the azimuth angle of the measurement
          */
-        @NonNull
-        public Builder setAzimuth(@NonNull AngleMeasurement azimuthAngle) {
+        public Builder(@NonNull AngleMeasurement azimuthAngle) {
             mAzimuthAngleMeasurement = azimuthAngle;
-            return this;
         }
 
         /**
@@ -162,15 +159,9 @@
 
         /**
          * Build the {@link AngleOfArrivalMeasurement} object
-         *
-         * @throws IllegalStateException if the required azimuth angle is not provided
          */
         @NonNull
         public AngleOfArrivalMeasurement build() {
-            if (mAzimuthAngleMeasurement == null) {
-                throw new IllegalStateException("Azimuth angle measurement is not set");
-            }
-
             return new AngleOfArrivalMeasurement(mAzimuthAngleMeasurement,
                     mAltitudeAngleMeasurement);
         }
diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl
index 468a69c..4036892 100644
--- a/core/java/android/uwb/IUwbAdapter.aidl
+++ b/core/java/android/uwb/IUwbAdapter.aidl
@@ -62,9 +62,6 @@
   /**
    * Request to open a new ranging session
    *
-   * This function must return before calling any functions in
-   * IUwbAdapterCallbacks.
-   *
    * This function does not start the ranging session, but all necessary
    * components must be initialized and ready to start a new ranging
    * session prior to calling IUwbAdapterCallback#onRangingOpened.
@@ -77,12 +74,16 @@
    * RANGING_SESSION_OPEN_THRESHOLD_MS milliseconds of #openRanging being called
    * if the ranging session fails to be opened.
    *
+   * If the provided sessionHandle is already open for the calling client, then
+   * #onRangingOpenFailed must be called and the new session must not be opened.
+   *
+   * @param sessionHandle the session handle to open ranging for
    * @param rangingCallbacks the callbacks used to deliver ranging information
    * @param parameters the configuration to use for ranging
-   * @return a SessionHandle used to identify this ranging request
    */
-  SessionHandle openRanging(in IUwbRangingCallbacks rangingCallbacks,
-                            in PersistableBundle parameters);
+  void openRanging(in SessionHandle sessionHandle,
+                   in IUwbRangingCallbacks rangingCallbacks,
+                   in PersistableBundle parameters);
 
   /**
    * Request to start ranging
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
index c0d8187..85f2c1c 100644
--- a/core/java/android/uwb/RangingManager.java
+++ b/core/java/android/uwb/RangingManager.java
@@ -17,6 +17,7 @@
 package android.uwb;
 
 import android.annotation.NonNull;
+import android.os.CancellationSignal;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.util.Log;
@@ -32,6 +33,7 @@
 
     private final IUwbAdapter mAdapter;
     private final Hashtable<SessionHandle, RangingSession> mRangingSessionTable = new Hashtable<>();
+    private int mNextSessionId = 1;
 
     public RangingManager(IUwbAdapter adapter) {
         mAdapter = adapter;
@@ -44,29 +46,26 @@
      * @param executor {@link Executor} to run callbacks
      * @param callbacks {@link RangingSession.Callback} to associate with the {@link RangingSession}
      *                  that is being opened.
-     * @return a new {@link RangingSession}
+     * @return a {@link CancellationSignal} that may be used to cancel the opening of the
+     *         {@link RangingSession}.
      */
-    public RangingSession openSession(@NonNull PersistableBundle params, @NonNull Executor executor,
+    public CancellationSignal openSession(@NonNull PersistableBundle params,
+            @NonNull Executor executor,
             @NonNull RangingSession.Callback callbacks) {
-        SessionHandle sessionHandle;
-        try {
-            sessionHandle = mAdapter.openRanging(this, params);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-
         synchronized (this) {
-            if (hasSession(sessionHandle)) {
-                Log.w(TAG, "Newly created session unexpectedly reuses an active SessionHandle");
-                executor.execute(() -> callbacks.onClosed(
-                        RangingSession.Callback.REASON_GENERIC_ERROR,
-                        new PersistableBundle()));
-            }
-
+            SessionHandle sessionHandle = new SessionHandle(mNextSessionId++);
             RangingSession session =
                     new RangingSession(executor, callbacks, mAdapter, sessionHandle);
             mRangingSessionTable.put(sessionHandle, session);
-            return session;
+            try {
+                mAdapter.openRanging(sessionHandle, this, params);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+
+            CancellationSignal cancellationSignal = new CancellationSignal();
+            cancellationSignal.setOnCancelListener(() -> session.close());
+            return cancellationSignal;
         }
     }
 
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
index 63a6d05..844bbbe 100644
--- a/core/java/android/uwb/UwbManager.java
+++ b/core/java/android/uwb/UwbManager.java
@@ -25,6 +25,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
+import android.os.CancellationSignal;
 import android.os.IBinder;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
@@ -228,14 +229,14 @@
      * @param callbacks {@link RangingSession.Callback} to associate with the
      *                  {@link RangingSession} that is being opened.
      *
-     * @return an {@link AutoCloseable} that is able to be used to close or cancel the opening of a
+     * @return an {@link CancellationSignal} that is able to be used to cancel the opening of a
      *         {@link RangingSession} that has been requested through {@link #openRangingSession}
      *         but has not yet been made available by
      *         {@link RangingSession.Callback#onOpened(RangingSession)}.
      */
     @NonNull
     @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
-    public AutoCloseable openRangingSession(@NonNull PersistableBundle parameters,
+    public CancellationSignal openRangingSession(@NonNull PersistableBundle parameters,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull RangingSession.Callback callbacks) {
         return mRangingManager.openSession(parameters, executor, callbacks);
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index a8aaeb7..9aaf5c0 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -636,7 +636,9 @@
     public void getMaxBoundsMetrics(DisplayMetrics outMetrics, CompatibilityInfo compatInfo,
             Configuration configuration) {
         Rect bounds = configuration.windowConfiguration.getMaxBounds();
-        getMetricsWithSize(outMetrics, compatInfo, configuration, bounds.width(), bounds.height());
+        // Pass in null configuration to ensure width and height are not overridden to app bounds.
+        getMetricsWithSize(outMetrics, compatInfo, /* configuration= */ null,
+                bounds.width(), bounds.height());
     }
 
     public int getNaturalWidth() {
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index afbd249..ddb49786 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -142,7 +142,13 @@
      *
      * The system reparents the leash of navigation bar to the app when the recents animation starts
      * and Launcher should call this method to let system restore the navigation bar to its
-     * original position when the quick switch gesture is finished.
+     * original position when the quick switch gesture is finished and will run the fade-in
+     * animation If {@param moveHomeToTop} is {@code true}. Otherwise, restore the navigtation bar
+     * without animation.
+     *
+     * @param moveHomeToTop if {@code true}, the home activity should be moved to the top.
+     *                      Otherwise, the home activity is hidden and the user is returned to the
+     *                      app.
      */
-    void detachNavigationBarFromApp();
+    void detachNavigationBarFromApp(boolean moveHomeToTop);
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 5477800..b345b2e 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -816,4 +816,6 @@
      * @param listener the listener to be unregistered
      */
     void unregisterCrossWindowBlurEnabledListener(ICrossWindowBlurEnabledListener listener);
+
+    void setForceCrossWindowBlurDisabled(boolean disable);
 }
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index 02a9788..aa1acc1 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.input.InputManager;
 import android.os.Build;
@@ -25,6 +26,8 @@
 import android.util.AndroidRuntimeException;
 import android.util.SparseIntArray;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.text.Normalizer;
 
 /**
@@ -297,6 +300,8 @@
     private static native char nativeGetDisplayLabel(long ptr, int keyCode);
     private static native int nativeGetKeyboardType(long ptr);
     private static native KeyEvent[] nativeGetEvents(long ptr, char[] chars);
+    private static native KeyCharacterMap nativeObtainEmptyKeyCharacterMap(int deviceId);
+    private static native boolean nativeEquals(long ptr1, long ptr2);
 
     private KeyCharacterMap(Parcel in) {
         if (in == null) {
@@ -323,6 +328,18 @@
     }
 
     /**
+     * Obtain empty key character map
+     * @param deviceId The input device ID
+     * @return The KeyCharacterMap object
+     * @hide
+     */
+    @VisibleForTesting
+    @Nullable
+    public static KeyCharacterMap obtainEmptyMap(int deviceId) {
+        return nativeObtainEmptyKeyCharacterMap(deviceId);
+    }
+
+    /**
      * Loads the key character maps for the keyboard with the specified device id.
      *
      * @param deviceId The device id of the keyboard.
@@ -729,6 +746,18 @@
         return 0;
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !(obj instanceof KeyCharacterMap)) {
+            return false;
+        }
+        KeyCharacterMap peer = (KeyCharacterMap) obj;
+        if (mPtr == 0 || peer.mPtr == 0) {
+            return mPtr == peer.mPtr;
+        }
+        return nativeEquals(mPtr, peer.mPtr);
+    }
+
     /**
      * Thrown by {@link KeyCharacterMap#load} when a key character map could not be loaded.
      */
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index a86984f..31f6f6a 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -80,5 +80,6 @@
 per-file Window*.aidl = file:/services/core/java/com/android/server/wm/OWNERS
 
 # Scroll Capture
+per-file *ScrollCapture*.aidl = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
 per-file *ScrollCapture*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
 per-file *CaptureHelper*.java = file:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9fc415d..35726c0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -8572,6 +8572,17 @@
         }
 
         @Override
+        public void onDragEvent(boolean isExiting, float x, float y) {
+            // force DRAG_EXITED_EVENT if appropriate
+            DragEvent event = DragEvent.obtain(
+                    isExiting ? DragEvent.ACTION_DRAG_EXITED : DragEvent.ACTION_DRAG_LOCATION,
+                    x, y, 0 /* offsetX */, 0 /* offsetY */, null/* localState */,
+                    null/* description */, null /* data */, null /* dragSurface */,
+                    null /* dragAndDropPermissions */, false /* result */);
+            dispatchDragEvent(event);
+        }
+
+        @Override
         public void dispose() {
             unscheduleConsumeBatchedInput();
             super.dispose();
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 7338c7d..818a2b0 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -873,6 +873,20 @@
     default void removeCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
     }
 
+    /**
+     * Disables cross-window blurs device-wide. This includes window blur behind
+     * (see {@link LayoutParams#setBlurBehindRadius}) and window background blur
+     * (see {@link Window#setBackgroundBlurRadius}).
+     *
+     * @param disable specifies whether to disable the blur. Note that calling this
+     *                with 'disable=false' will not enable blurs if there is something
+     *                else disabling blurs.
+     * @hide
+     */
+    @TestApi
+    default void setForceCrossWindowBlurDisabled(boolean disable) {
+    }
+
     public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
         /**
          * X position for this window.  With the default gravity it is ignored.
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index b398707..e37522b 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -317,4 +317,13 @@
     public void removeCrossWindowBlurEnabledListener(@NonNull Consumer<Boolean> listener) {
         CrossWindowBlurListeners.getInstance().removeListener(listener);
     }
+
+    @Override
+    public void setForceCrossWindowBlurDisabled(boolean disable) {
+        try {
+            WindowManagerGlobal.getWindowManagerService()
+                .setForceCrossWindowBlurDisabled(disable);
+        } catch (RemoteException e) {
+        }
+    }
 }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index a8fff8b..53bbc0a 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -106,7 +106,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
 import java.lang.reflect.Proxy;
 import java.util.Arrays;
 import java.util.Collections;
@@ -413,7 +412,7 @@
      * The InputConnection that was last retrieved from the served view.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    ControlledInputConnectionWrapper mServedInputConnectionWrapper;
+    IInputConnectionWrapper mServedInputConnectionWrapper;
     /**
      * The completions that were last provided by the served view.
      */
@@ -740,8 +739,7 @@
         /**
          * Checks whether the active input connection (if any) is for the given view.
          *
-         * TODO(b/160968797): Remove this method and move mServedInputConnectionWrapper to
-         *  ImeFocusController.
+         * TODO(b/182259171): Clean-up hasActiveConnection to simplify the logic.
          *
          * Note that this method is only intended for restarting input after focus gain
          * (e.g. b/160391516), DO NOT leverage this method to do another check.
@@ -755,7 +753,7 @@
 
                 return mServedInputConnectionWrapper != null
                         && mServedInputConnectionWrapper.isActive()
-                        && mServedInputConnectionWrapper.mServedView.get() == view;
+                        && mServedInputConnectionWrapper.getServedView() == view;
             }
         }
     }
@@ -1022,77 +1020,6 @@
         }
     }
 
-    private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
-        private final InputMethodManager mParentInputMethodManager;
-        private final WeakReference<View> mServedView;
-
-        ControlledInputConnectionWrapper(Looper icLooper, InputConnection conn,
-                InputMethodManager inputMethodManager, View servedView) {
-            super(icLooper, conn);
-            mParentInputMethodManager = inputMethodManager;
-            mServedView = new WeakReference<>(servedView);
-        }
-
-        @Override
-        public boolean isActive() {
-            return mParentInputMethodManager.mActive && !isFinished();
-        }
-
-        @Override
-        public InputMethodManager getIMM() {
-            return mParentInputMethodManager;
-        }
-
-        void deactivate() {
-            if (isFinished()) {
-                // This is a small performance optimization.  Still only the 1st call of
-                // reportFinish() will take effect.
-                return;
-            }
-            closeConnection();
-
-            // Notify the app that the InputConnection was closed.
-            final View servedView = mServedView.get();
-            if (servedView != null) {
-                final Handler handler = servedView.getHandler();
-                // The handler is null if the view is already detached. When that's the case, for
-                // now, we simply don't dispatch this callback.
-                if (handler != null) {
-                    if (DEBUG) {
-                        Log.v(TAG, "Calling View.onInputConnectionClosed: view=" + servedView);
-                    }
-                    if (handler.getLooper().isCurrentThread()) {
-                        servedView.onInputConnectionClosedInternal();
-                    } else {
-                        handler.post(servedView::onInputConnectionClosedInternal);
-                    }
-                }
-            }
-        }
-
-        @Override
-        public String toString() {
-            return "ControlledInputConnectionWrapper{"
-                    + "connection=" + getInputConnection()
-                    + " finished=" + isFinished()
-                    + " mParentInputMethodManager.mActive=" + mParentInputMethodManager.mActive
-                    + " mServedView=" + mServedView.get()
-                    + "}";
-        }
-
-        void dumpDebug(ProtoOutputStream proto, long fieldId) {
-            // Check that the call is initiated in the main thread of the current InputConnection
-            // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
-            // executed on this thread. Otherwise the messages are dispatched to the correct thread
-            // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance
-            // reasons.
-            if (getInputConnection() instanceof DumpableInputConnection && Looper.myLooper()
-                    == getLooper()) {
-                ((DumpableInputConnection) getInputConnection()).dumpDebug(proto, fieldId);
-            }
-        }
-    }
-
     final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
         @Override
         protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
@@ -1256,8 +1183,7 @@
         mMainLooper = looper;
         mH = new H(looper);
         mDisplayId = displayId;
-        mIInputContext = new ControlledInputConnectionWrapper(looper, mDummyInputConnection, this,
-                null);
+        mIInputContext = new IInputConnectionWrapper(looper, mDummyInputConnection, this, null);
     }
 
     /**
@@ -2063,7 +1989,7 @@
                 mServedInputConnectionWrapper.deactivate();
                 mServedInputConnectionWrapper = null;
             }
-            ControlledInputConnectionWrapper servedContext;
+            IInputConnectionWrapper servedContext;
             final int missingMethodFlags;
             if (ic != null) {
                 mCursorSelStart = tba.initialSelStart;
@@ -2080,7 +2006,7 @@
                 } else {
                     icHandler = ic.getHandler();
                 }
-                servedContext = new ControlledInputConnectionWrapper(
+                servedContext = new IInputConnectionWrapper(
                         icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this, view);
             } else {
                 servedContext = null;
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 34fe51e..42d7535 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -200,6 +200,8 @@
         mTimeZone = toZoneId(a.getString(com.android.internal.R.styleable.AnalogClock_timeZone));
         createClock();
 
+        a.recycle();
+
         mDialWidth = mDial.getIntrinsicWidth();
         mDialHeight = mDial.getIntrinsicHeight();
     }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 012352d..7517b80 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3790,7 +3790,7 @@
         }
 
         public SuggestionsPopupWindow() {
-            mCursorWasVisibleBeforeSuggestions = mCursorVisible;
+            mCursorWasVisibleBeforeSuggestions = mTextView.isCursorVisibleFromAttr();
         }
 
         @Override
@@ -3957,7 +3957,7 @@
             }
 
             if (updateSuggestions()) {
-                mCursorWasVisibleBeforeSuggestions = mCursorVisible;
+                mCursorWasVisibleBeforeSuggestions = mTextView.isCursorVisibleFromAttr();
                 mTextView.setCursorVisible(false);
                 mIsShowingUp = true;
                 super.show();
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2328e58..d2f4cea 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -120,6 +120,7 @@
 import java.util.Stack;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 /**
  * A class that describes a view hierarchy that can be displayed in
@@ -1993,22 +1994,79 @@
         mIsRoot = false;
     }
 
+    private static boolean hasStableId(View view) {
+        Object tag = view.getTag(com.android.internal.R.id.remote_views_stable_id);
+        return tag != null;
+    }
+
+    private static int getStableId(View view) {
+        Integer id = (Integer) view.getTag(com.android.internal.R.id.remote_views_stable_id);
+        return id == null ? ViewGroupActionAdd.NO_ID : id;
+    }
+
+    private static void setStableId(View view, int stableId) {
+        view.setTagInternal(com.android.internal.R.id.remote_views_stable_id, stableId);
+    }
+
+    // Returns the next recyclable child of the view group, or -1 if there are none.
+    private static int getNextRecyclableChild(ViewGroup vg) {
+        Integer tag = (Integer) vg.getTag(com.android.internal.R.id.remote_views_next_child);
+        return tag == null ? -1 : tag;
+    }
+
+    private static int getViewLayoutId(View v) {
+        return (Integer) v.getTag(R.id.widget_frame);
+    }
+
+    private static void setNextRecyclableChild(ViewGroup vg, int nextChild, int numChildren) {
+        if (nextChild < 0 || nextChild >= numChildren) {
+            vg.setTagInternal(com.android.internal.R.id.remote_views_next_child, -1);
+        } else {
+            vg.setTagInternal(com.android.internal.R.id.remote_views_next_child, nextChild);
+        }
+    }
+
+    private void finalizeViewRecycling(ViewGroup root) {
+        // Remove any recyclable children that were not used. nextChild should either be -1 or point
+        // to the next recyclable child that hasn't been recycled.
+        int nextChild = getNextRecyclableChild(root);
+        if (nextChild >= 0 && nextChild < root.getChildCount()) {
+            root.removeViews(nextChild, root.getChildCount() - nextChild);
+        }
+        // Make sure on the next round, we don't try to recycle if removeAllViews is not called.
+        setNextRecyclableChild(root, -1, 0);
+        // Traverse the view tree.
+        for (int i = 0; i < root.getChildCount(); i++) {
+            View child = root.getChildAt(i);
+            if (child instanceof ViewGroup && !child.isRootNamespace()) {
+                finalizeViewRecycling((ViewGroup) child);
+            }
+        }
+    }
+
     /**
      * ViewGroup methods that are related to adding Views.
      */
     private class ViewGroupActionAdd extends Action {
+        static final int NO_ID = -1;
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
         private RemoteViews mNestedViews;
         private int mIndex;
+        private int mStableId;
 
         ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews) {
-            this(viewId, nestedViews, -1 /* index */);
+            this(viewId, nestedViews, -1 /* index */, NO_ID /* nestedViewId */);
         }
 
         ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews, int index) {
+            this(viewId, nestedViews, index, NO_ID /* nestedViewId */);
+        }
+
+        ViewGroupActionAdd(@IdRes int viewId, RemoteViews nestedViews, int index, int stableId) {
             this.viewId = viewId;
             mNestedViews = nestedViews;
             mIndex = index;
+            mStableId = stableId;
             if (nestedViews != null) {
                 configureRemoteViewsAsChild(nestedViews);
             }
@@ -2018,6 +2076,7 @@
                 int depth, Map<Class, Object> classCookies) {
             viewId = parcel.readInt();
             mIndex = parcel.readInt();
+            mStableId = parcel.readInt();
             mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth, classCookies);
             mNestedViews.addFlags(mApplyFlags);
         }
@@ -2025,6 +2084,7 @@
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(viewId);
             dest.writeInt(mIndex);
+            dest.writeInt(mStableId);
             mNestedViews.writeToParcel(dest, flags);
         }
 
@@ -2033,6 +2093,17 @@
             return mNestedViews.hasSameAppInfo(parentInfo);
         }
 
+        private int findViewIndexToRecycle(ViewGroup target, RemoteViews newContent) {
+            for (int nextChild = getNextRecyclableChild(target); nextChild < target.getChildCount();
+                    nextChild++) {
+                View child = target.getChildAt(nextChild);
+                if (getStableId(child) == mStableId) {
+                    return nextChild;
+                }
+            }
+            return -1;
+        }
+
         @Override
         public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
                 ColorResources colorResources) {
@@ -2043,10 +2114,45 @@
                 return;
             }
 
+            // If removeAllViews was called, this returns the next potential recycled view.
+            // If there are no more views to recycle (or removeAllViews was not called), this
+            // will return -1.
+            final int nextChild = getNextRecyclableChild(target);
+            RemoteViews rvToApply = mNestedViews.getRemoteViewsToApply(context);
+            if (nextChild >= 0 && mStableId != NO_ID) {
+                // At that point, the views starting at index nextChild are the ones recyclable but
+                // not yet recycled. All views added on that round of application are placed before.
+                // Find the next view with the same stable id, or -1.
+                int recycledViewIndex = findViewIndexToRecycle(target, rvToApply);
+                if (recycledViewIndex >= 0) {
+                    View child = target.getChildAt(recycledViewIndex);
+                    if (getViewLayoutId(child) == rvToApply.getLayoutId()) {
+                        if (nextChild < recycledViewIndex) {
+                            target.removeViews(nextChild, recycledViewIndex - nextChild);
+                        }
+                        setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
+                        rvToApply.reapply(context, child, handler, null /* size */, colorResources,
+                                false /* topLevel */);
+                        return;
+                    }
+                    // If we cannot recycle the views, we still remove all views in between to
+                    // avoid weird behaviors and insert the new view in place of the old one.
+                    target.removeViews(nextChild, recycledViewIndex - nextChild + 1);
+                }
+            }
+            // If we cannot recycle, insert the new view before the next recyclable child.
+
             // Inflate nested views and add as children
-            target.addView(
-                    mNestedViews.apply(context, target, handler, null /* size */, colorResources),
-                    mIndex);
+            View nestedView = rvToApply.apply(context, target, handler, null /* size */,
+                    colorResources);
+            if (mStableId != NO_ID) {
+                setStableId(nestedView, mStableId);
+            }
+            target.addView(nestedView, mIndex >= 0 ? mIndex : nextChild);
+            if (nextChild >= 0) {
+                // If we are at the end, there is no reason to try to recycle anymore
+                setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
+            }
         }
 
         @Override
@@ -2063,24 +2169,91 @@
 
             // Inflate nested views and perform all the async tasks for the child remoteView.
             final Context context = root.mRoot.getContext();
-            final AsyncApplyTask task = mNestedViews.getAsyncApplyTask(context, targetVg,
-                    null /* listener */, handler, null /* size */, colorResources);
+
+            // If removeAllViews was called, this returns the next potential recycled view.
+            // If there are no more views to recycle (or removeAllViews was not called), this
+            // will return -1.
+            final int nextChild = getNextRecyclableChild(targetVg);
+            if (nextChild >= 0 && mStableId != NO_ID) {
+                RemoteViews rvToApply = mNestedViews.getRemoteViewsToApply(context);
+                final int recycledViewIndex = target.findChildIndex(nextChild,
+                        view -> getStableId(view) == mStableId);
+                if (recycledViewIndex >= 0) {
+                    // At that point, the views starting at index nextChild are the ones
+                    // recyclable but not yet recycled. All views added on that round of
+                    // application are placed before.
+                    ViewTree recycled = target.mChildren.get(recycledViewIndex);
+                    // We can only recycle the view if the layout id is the same.
+                    if (getViewLayoutId(recycled.mRoot) == rvToApply.getLayoutId()) {
+                        if (recycledViewIndex > nextChild) {
+                            target.removeChildren(nextChild, recycledViewIndex - nextChild);
+                        }
+                        setNextRecyclableChild(targetVg, nextChild + 1, target.mChildren.size());
+                        final AsyncApplyTask reapplyTask = rvToApply.getInternalAsyncApplyTask(
+                                context,
+                                targetVg, null /* listener */, handler, null /* size */,
+                                colorResources,
+                                recycled.mRoot);
+                        final ViewTree tree = reapplyTask.doInBackground();
+                        if (tree == null) {
+                            throw new ActionException(reapplyTask.mError);
+                        }
+                        return new RuntimeAction() {
+                            @Override
+                            public void apply(View root, ViewGroup rootParent,
+                                    InteractionHandler handler, ColorResources colorResources)
+                                    throws ActionException {
+                                reapplyTask.onPostExecute(tree);
+                                if (recycledViewIndex > nextChild) {
+                                    targetVg.removeViews(nextChild, recycledViewIndex - nextChild);
+                                }
+                            }
+                        };
+                    }
+                    // If the layout id is different, still remove the children as if we recycled
+                    // the view, to insert at the same place.
+                    target.removeChildren(nextChild, recycledViewIndex - nextChild + 1);
+                    return insertNewView(context, target, handler, colorResources,
+                            () -> targetVg.removeViews(nextChild,
+                                    recycledViewIndex - nextChild + 1));
+
+                }
+            }
+            // If we cannot recycle, simply add the view at the same available slot.
+            return insertNewView(context, target, handler, colorResources, () -> {});
+        }
+
+        private Action insertNewView(Context context, ViewTree target, InteractionHandler handler,
+                ColorResources colorResources, Runnable finalizeAction) {
+            ViewGroup targetVg = (ViewGroup) target.mRoot;
+            int nextChild = getNextRecyclableChild(targetVg);
+            final AsyncApplyTask task = mNestedViews.getInternalAsyncApplyTask(context, targetVg,
+                    null /* listener */, handler, null /* size */, colorResources,
+                    null /* result */);
             final ViewTree tree = task.doInBackground();
 
             if (tree == null) {
                 throw new ActionException(task.mError);
             }
+            if (mStableId != NO_ID) {
+                setStableId(task.mResult, mStableId);
+            }
 
             // Update the global view tree, so that next call to findViewTreeById
             // goes through the subtree as well.
-            target.addChild(tree, mIndex);
+            final int insertIndex = mIndex >= 0 ? mIndex : nextChild;
+            target.addChild(tree, insertIndex);
+            if (nextChild >= 0) {
+                setNextRecyclableChild(targetVg, nextChild + 1, target.mChildren.size());
+            }
 
             return new RuntimeAction() {
                 @Override
                 public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
                         ColorResources colorResources) throws ActionException {
                     task.onPostExecute(tree);
-                    targetVg.addView(task.mResult, mIndex);
+                    finalizeAction.run();
+                    targetVg.addView(task.mResult, insertIndex);
                 }
             };
         }
@@ -2148,7 +2321,14 @@
             }
 
             if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
-                target.removeAllViews();
+                // Remote any view without a stable id
+                for (int i = target.getChildCount() - 1; i >= 0; i--) {
+                    if (!hasStableId(target.getChildAt(i))) {
+                        target.removeViewAt(i);
+                    }
+                }
+                // In the end, only children with a stable id (i.e. recyclable) are left.
+                setNextRecyclableChild(target, 0, target.getChildCount());
                 return;
             }
 
@@ -2170,8 +2350,8 @@
             final ViewGroup targetVg = (ViewGroup) target.mRoot;
 
             if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
-                // Clear all children when there's no excepted view
-                target.mChildren = null;
+                target.mChildren.removeIf(childTree -> !hasStableId(childTree.mRoot));
+                setNextRecyclableChild(targetVg, 0, target.mChildren.size());
             } else {
                 // Remove just the children which don't match the excepted view
                 target.mChildren.removeIf(childTree -> childTree.mRoot.getId() != mViewIdToKeep);
@@ -2184,7 +2364,11 @@
                 public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
                         ColorResources colorResources) throws ActionException {
                     if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
-                        targetVg.removeAllViews();
+                        for (int i = targetVg.getChildCount() - 1; i >= 0; i--) {
+                            if (!hasStableId(targetVg.getChildAt(i))) {
+                                targetVg.removeViewAt(i);
+                            }
+                        }
                         return;
                     }
 
@@ -3084,6 +3268,7 @@
         }
         mApplication = portrait.mApplication;
         mLayoutId = portrait.mLayoutId;
+        mViewId = portrait.mViewId;
         mLightBackgroundLayoutId = portrait.mLightBackgroundLayoutId;
 
         mLandscape = landscape;
@@ -3136,6 +3321,7 @@
         RemoteViews smallestView = findSmallestRemoteView();
         mApplication = smallestView.mApplication;
         mLayoutId = smallestView.mLayoutId;
+        mViewId = smallestView.mViewId;
         mLightBackgroundLayoutId = smallestView.mLightBackgroundLayoutId;
     }
 
@@ -3253,6 +3439,7 @@
                     ApplicationInfo.CREATOR.createFromParcel(parcel);
             mIdealSize = parcel.readInt() == 0 ? null : SizeF.CREATOR.createFromParcel(parcel);
             mLayoutId = parcel.readInt();
+            mViewId = parcel.readInt();
             mLightBackgroundLayoutId = parcel.readInt();
 
             readActionsFromParcel(parcel, depth);
@@ -3273,6 +3460,7 @@
             RemoteViews smallestView = findSmallestRemoteView();
             mApplication = smallestView.mApplication;
             mLayoutId = smallestView.mLayoutId;
+            mViewId = smallestView.mViewId;
             mLightBackgroundLayoutId = smallestView.mLightBackgroundLayoutId;
         } else {
             // MODE_HAS_LANDSCAPE_AND_PORTRAIT
@@ -3281,6 +3469,7 @@
                     mClassCookies);
             mApplication = mPortrait.mApplication;
             mLayoutId = mPortrait.mLayoutId;
+            mViewId = mPortrait.mViewId;
             mLightBackgroundLayoutId = mPortrait.mLightBackgroundLayoutId;
         }
         mApplyFlags = parcel.readInt();
@@ -3458,6 +3647,29 @@
     }
 
     /**
+     * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the given
+     * {@link RemoteViews}. If the {@link RemoteViews} may be re-inflated or updated,
+     * {@link #removeAllViews(int)} must be called on the same {@code viewId
+     * } before the first call to this method for the behavior of this method to be predictable.
+     *
+     * The {@code stableId} will be used to identify a potential view to recycled when the remote
+     * view is inflated. Views can be re-used if inserted in the same order, potentially with
+     * some views appearing / disappearing.
+     *
+     * Note: if a view is re-used, all the actions will be re-applied on it. However, its properties
+     * are not reset, so what was applied in previous round will have an effect. As a view may be
+     * re-created at any time by the host, the RemoteViews should not rely on keeping information
+     * from previous applications and always re-set all the properties they need.
+     *
+     * @param viewId The id of the parent {@link ViewGroup} to add child into.
+     * @param nestedView {@link RemoteViews} that describes the child.
+     * @param stableId An id that is stable across different versions of RemoteViews.
+     */
+    public void addStableView(@IdRes int viewId, @NonNull RemoteViews nestedView, int stableId) {
+        addAction(new ViewGroupActionAdd(viewId, nestedView, -1 /* index */, stableId));
+    }
+
+    /**
      * Equivalent to calling {@link ViewGroup#addView(View, int)} after inflating the
      * given {@link RemoteViews}.
      *
@@ -4870,23 +5082,24 @@
     public CancellationSignal applyAsync(Context context, ViewGroup parent,
             Executor executor, OnViewAppliedListener listener, InteractionHandler handler,
             SizeF size) {
-        return getAsyncApplyTask(context, parent, listener, handler, size, null /* themeColors */)
-                .startTaskOnExecutor(executor);
+        return applyAsync(context, parent, executor, listener, handler, size,
+                null /* themeColors */);
     }
 
     /** @hide */
     public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
             OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
             ColorResources colorResources) {
-        return getAsyncApplyTask(context, parent, listener, handler, size, colorResources)
-                .startTaskOnExecutor(executor);
+        return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
+                handler, colorResources, null /* result */,
+                true /* topLevel */).startTaskOnExecutor(executor);
     }
 
-    private AsyncApplyTask getAsyncApplyTask(Context context, ViewGroup parent,
+    private AsyncApplyTask getInternalAsyncApplyTask(Context context, ViewGroup parent,
             OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
-            ColorResources colorResources) {
+            ColorResources colorResources, View result) {
         return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
-                handler, colorResources, null /* result */);
+                handler, colorResources, result, false /* topLevel */);
     }
 
     private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
@@ -4898,6 +5111,12 @@
         final OnViewAppliedListener mListener;
         final InteractionHandler mHandler;
         final ColorResources mColorResources;
+        /**
+         * Whether the remote view is the top-level one (i.e. not within an action).
+         *
+         * This is only used if the result is specified (i.e. the view is being recycled).
+         */
+        final boolean mTopLevel;
 
         private View mResult;
         private ViewTree mTree;
@@ -4906,13 +5125,15 @@
 
         private AsyncApplyTask(
                 RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
-                InteractionHandler handler, ColorResources colorResources, View result) {
+                InteractionHandler handler, ColorResources colorResources,
+                View result, boolean topLevel) {
             mRV = rv;
             mParent = parent;
             mContext = context;
             mListener = listener;
             mColorResources = colorResources;
             mHandler = handler;
+            mTopLevel = topLevel;
 
             mResult = result;
         }
@@ -4959,6 +5180,10 @@
                             a.apply(viewTree.mRoot, mParent, handler, mColorResources);
                         }
                     }
+                    // If the parent of the view is has is a root, resolve the recycling.
+                    if (mTopLevel && mResult instanceof ViewGroup) {
+                        finalizeViewRecycling((ViewGroup) mResult);
+                    }
                 } catch (Exception e) {
                     mError = e;
                 }
@@ -5011,6 +5236,14 @@
     /** @hide */
     public void reapply(Context context, View v, InteractionHandler handler, SizeF size,
             ColorResources colorResources) {
+        reapply(context, v, handler, size, colorResources, true);
+    }
+
+    // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
+    // should set it to false.
+    private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
+            ColorResources colorResources, boolean topLevel) {
+
         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
 
         // In the case that a view has this RemoteViews applied in one orientation or size, is
@@ -5025,6 +5258,11 @@
         }
 
         rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
+
+        // If the parent of the view is has is a root, resolve the recycling.
+        if (topLevel && v instanceof ViewGroup) {
+            finalizeViewRecycling((ViewGroup) v);
+        }
     }
 
     /**
@@ -5068,8 +5306,8 @@
         }
 
         return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
-                context, listener, handler, colorResources, v).startTaskOnExecutor(
-                executor);
+                context, listener, handler, colorResources, v, true /* topLevel */)
+                .startTaskOnExecutor(executor);
     }
 
     private void performApply(View v, ViewGroup parent, InteractionHandler handler,
@@ -5282,6 +5520,7 @@
                 mIdealSize.writeToParcel(dest, flags);
             }
             dest.writeInt(mLayoutId);
+            dest.writeInt(mViewId);
             dest.writeInt(mLightBackgroundLayoutId);
             writeActionsToParcel(dest);
         } else if (hasSizedRemoteViews()) {
@@ -5470,6 +5709,14 @@
             mChildren.add(index, child);
         }
 
+        public void removeChildren(int start, int count) {
+            if (mChildren != null) {
+                for (int i = 0; i < count; i++) {
+                    mChildren.remove(start);
+                }
+            }
+        }
+
         private void addViewChild(View v) {
             // ViewTree only contains Views which can be found using findViewById.
             // If isRootNamespace is true, this view is skipped.
@@ -5500,6 +5747,28 @@
                 }
             }
         }
+
+        /** Find the first child for which the condition is true and return its index. */
+        public int findChildIndex(Predicate<View> condition) {
+            return findChildIndex(0, condition);
+        }
+
+        /**
+         * Find the first child, starting at {@code startIndex}, for which the condition is true and
+         * return its index.
+         */
+        public int findChildIndex(int startIndex, Predicate<View> condition) {
+            if (mChildren == null) {
+                return -1;
+            }
+
+            for (int i = startIndex; i < mChildren.size(); i++) {
+                if (condition.test(mChildren.get(i).mRoot)) {
+                    return i;
+                }
+            }
+            return -1;
+        }
     }
 
     /**
@@ -5758,9 +6027,9 @@
     }
 
     /**
-     *  Set the ID of the top-level view of the XML layout.
+     * Set the ID of the top-level view of the XML layout.
      *
-     *  Set to {@link View#NO_ID} to reset and simply keep the id defined in the XML layout.
+     * Set to {@link View#NO_ID} to reset and simply keep the id defined in the XML layout.
      *
      * @throws UnsupportedOperationException if the method is called on a RemoteViews defined in
      * term of other RemoteViews (e.g. {@link #RemoteViews(RemoteViews, RemoteViews)}).
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9e97f9a..dba7fa9 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -503,7 +503,7 @@
     private boolean mImeIsConsumingInput;
 
     // Whether cursor is visible without regard to {@link mImeConsumesInput}.
-    // {code true} is the default value.
+    // {@code true} is the default value.
     private boolean mCursorVisibleFromAttr = true;
 
     static class Drawables {
@@ -9317,7 +9317,7 @@
         }
 
         for (int i = 0; i < n; i++) {
-            max = Math.max(max, layout.getLineWidth(i));
+            max = Math.max(max, layout.getLineMax(i));
         }
 
         return (int) Math.ceil(max);
@@ -10571,6 +10571,17 @@
         return mEditor == null ? true : mEditor.mCursorVisible;
     }
 
+    /**
+     * @return whether cursor is visible without regard to {@code mImeIsConsumingInput}.
+     * {@code true} is the default value.
+     *
+     * @see #setCursorVisible(boolean)
+     * @hide
+     */
+    public boolean isCursorVisibleFromAttr() {
+        return mCursorVisibleFromAttr;
+    }
+
     private boolean canMarquee() {
         int width = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();
         return width > 0 && (mLayout.getLineWidth(0) > width
diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl
index 8f541d0..3eb35c2 100644
--- a/core/java/android/window/ITaskOrganizer.aidl
+++ b/core/java/android/window/ITaskOrganizer.aidl
@@ -18,6 +18,7 @@
 
 import android.view.SurfaceControl;
 import android.app.ActivityManager;
+import android.graphics.Rect;
 import android.window.StartingWindowInfo;
 import android.window.WindowContainerToken;
 
@@ -38,8 +39,12 @@
 
     /**
      * Called when the Task want to remove the starting window.
+     * @param leash A persistent leash for the top window in this task.
+     * @param frame Window frame of the top window.
+     * @param playRevealAnimation Play vanish animation.
      */
-    void removeStartingWindow(int taskId);
+    void removeStartingWindow(int taskId, in SurfaceControl leash, in Rect frame,
+            in boolean playRevealAnimation);
 
     /**
      * Called when the Task want to copy the splash screen.
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 35ccfca..da445b8 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -46,6 +46,8 @@
 import com.android.internal.R;
 import com.android.internal.policy.DecorView;
 
+import java.util.function.Consumer;
+
 /**
  * <p>The view which allows an activity to customize its splash screen exit animation.</p>
  *
@@ -77,7 +79,8 @@
 
     private Animatable mAnimatableIcon;
     private ValueAnimator mAnimator;
-
+    private Runnable mAnimationFinishListener;
+    private Consumer<Canvas> mOnDrawCallback;
     // cache original window and status
     private Window mWindow;
     private boolean mDrawBarBackground;
@@ -85,7 +88,7 @@
     private int mNavigationBarColor;
 
     /**
-     * Internal builder to create a SplashScreenWindowView object.
+     * Internal builder to create a SplashScreenView object.
      * @hide
      */
     public static class Builder {
@@ -391,7 +394,7 @@
      * Get the initial background color of this view.
      * @hide
      */
-    @ColorInt int getInitBackgroundColor() {
+    public @ColorInt int getInitBackgroundColor() {
         return mInitBackgroundColor;
     }
 
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index d1c1e40..c7672dc 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -126,6 +126,12 @@
      */
     public int splashScreenThemeResId;
 
+    /**
+     * Is keyguard occluded on default display.
+     * @hide
+     */
+    public boolean isKeyguardOccluded = false;
+
     public StartingWindowInfo() {
 
     }
@@ -147,6 +153,7 @@
         dest.writeTypedObject(topOpaqueWindowLayoutParams, flags);
         dest.writeTypedObject(mainWindowLayoutParams, flags);
         dest.writeInt(splashScreenThemeResId);
+        dest.writeBoolean(isKeyguardOccluded);
     }
 
     void readFromParcel(@NonNull Parcel source) {
@@ -157,6 +164,7 @@
                 WindowManager.LayoutParams.CREATOR);
         mainWindowLayoutParams = source.readTypedObject(WindowManager.LayoutParams.CREATOR);
         splashScreenThemeResId = source.readInt();
+        isKeyguardOccluded = source.readBoolean();
     }
 
     @Override
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 04020ec..3340cf4 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -24,6 +24,7 @@
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.app.ActivityManager;
+import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.view.SurfaceControl;
@@ -100,9 +101,14 @@
 
     /**
      * Called when the Task want to remove the starting window.
+     * @param leash A persistent leash for the top window in this task. Release it once exit
+     *              animation has finished.
+     * @param frame Window frame of the top window.
+     * @param playRevealAnimation Play vanish animation.
      */
     @BinderThread
-    public void removeStartingWindow(int taskId) {}
+    public void removeStartingWindow(int taskId, @Nullable SurfaceControl leash,
+            @Nullable Rect frame, boolean playRevealAnimation) {}
 
     /**
      * Called when the Task want to copy the splash screen.
@@ -217,15 +223,16 @@
 
     private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() {
         @Override
-
         public void addStartingWindow(StartingWindowInfo windowInfo,
                 IBinder appToken) {
             mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(windowInfo, appToken));
         }
 
         @Override
-        public void removeStartingWindow(int taskId) {
-            mExecutor.execute(() -> TaskOrganizer.this.removeStartingWindow(taskId));
+        public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+                boolean playRevealAnimation) {
+            mExecutor.execute(() -> TaskOrganizer.this.removeStartingWindow(taskId, leash, frame,
+                    playRevealAnimation));
         }
 
         @Override
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index c1952c7..957e4169 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -166,4 +166,15 @@
 
     /** {@hide} */
     boolean setChargingStateUpdateDelayMillis(int delay);
+
+    /** Exposed as a test API. */
+    void setChargerAcOnline(boolean online, boolean forceUpdate);
+    /** Exposed as a test API. */
+    void setBatteryLevel(int level, boolean forceUpdate);
+    /** Exposed as a test API. */
+    void unplugBattery(boolean forceUpdate);
+    /** Exposed as a test API. */
+    void resetBattery(boolean forceUpdate);
+    /** Exposed as a test API. */
+    void suspendBatteryInput();
 }
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index ccb980e..592f7c7 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -21,6 +21,7 @@
 import android.media.permission.Identity;
 import android.os.Bundle;
 import android.os.RemoteCallback;
+import android.os.SharedMemory;
 
 import com.android.internal.app.IVoiceActionCheckCallback;
 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
@@ -225,14 +226,19 @@
             IBinder client);
 
     /**
-     * Sets hotword detection configuration.
+     * Set configuration and pass read-only data to hotword detection service.
      *
-     * Note: Currently it will trigger hotword detection service after calling this function when
-     * all conditions meet the requirements.
-     *
-     * @param options Config data.
-     * @return {@link VoiceInteractionService#HOTWORD_CONFIG_SUCCESS} in case of success,
-     * {@link VoiceInteractionService#HOTWORD_CONFIG_FAILURE} in case of failure.
+     * @param options Application configuration data provided by the
+     * {@link VoiceInteractionService}. The system strips out any remotable objects or other
+     * contents that can be used to communicate with other processes.
+     * @param sharedMemory The unrestricted data blob provided by the
+     * {@link VoiceInteractionService}. Use this to provide the hotword models data or other
+     * such data to the trusted process.
      */
-    int setHotwordDetectionConfig(in Bundle options);
+    void setHotwordDetectionServiceConfig(in Bundle options, in SharedMemory sharedMemory);
+
+    /**
+     * Requests to shutdown hotword detection service.
+     */
+    void shutdownHotwordDetectionService();
 }
diff --git a/core/java/com/android/internal/compat/AndroidBuildClassifier.java b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
index 0b937fa..364db06 100644
--- a/core/java/com/android/internal/compat/AndroidBuildClassifier.java
+++ b/core/java/com/android/internal/compat/AndroidBuildClassifier.java
@@ -31,4 +31,14 @@
     public boolean isFinalBuild() {
         return "REL".equals(Build.VERSION.CODENAME);
     }
+
+    /**
+     * The current platform SDK version.
+     */
+    public int platformTargetSdk() {
+        if (isFinalBuild()) {
+            return Build.VERSION.SDK_INT;
+        }
+        return Build.VERSION_CODES.CUR_DEVELOPMENT;
+    }
 }
diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java
index c0bbe50..e408be2 100644
--- a/core/java/com/android/internal/compat/OverrideAllowedState.java
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.java
@@ -34,7 +34,8 @@
             DISABLED_NON_TARGET_SDK,
             DISABLED_TARGET_SDK_TOO_HIGH,
             DEFERRED_VERIFICATION,
-            LOGGING_ONLY_CHANGE
+            LOGGING_ONLY_CHANGE,
+            PLATFORM_TOO_OLD
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface State {
@@ -65,6 +66,10 @@
      * Change is marked as logging only, and cannot be toggled.
      */
     public static final int LOGGING_ONLY_CHANGE = 5;
+    /**
+     * Change is gated by a target sdk version newer than the current platform sdk version.
+     */
+    public static final int PLATFORM_TOO_OLD = 6;
 
     @State
     public final int state;
@@ -123,6 +128,11 @@
                 throw new SecurityException(String.format(
                         "Cannot override %1$d because it is marked as a logging-only change.",
                         changeId));
+            case PLATFORM_TOO_OLD:
+                throw new SecurityException(String.format(
+                        "Cannot override %1$d for %2$s because the change's targetSdk threshold "
+                                + "(%3$d) is above the platform sdk.",
+                        changeId, packageName, changeIdTargetSdk));
         }
     }
 
@@ -170,6 +180,8 @@
                 return "DEFERRED_VERIFICATION";
             case LOGGING_ONLY_CHANGE:
                 return "LOGGING_ONLY_CHANGE";
+            case PLATFORM_TOO_OLD:
+                return "PLATFORM_TOO_OLD";
         }
         return "UNKNOWN";
     }
diff --git a/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java
index b4f216b..1d865c2 100644
--- a/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java
+++ b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java
@@ -75,7 +75,7 @@
             // Note: they don't _have_ to be ignored, for example, we could instead turn them
             // opaque. Traditionally, including outside Android, quantizers ignore transparent
             // pixels, so that strategy was chosen.
-            int alpha = (pixel >> 24);
+            int alpha = (pixel >> 24) & 0xff;
             if (alpha < 255) {
                 continue;
             }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 9ecb0ad..11466f4 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -6976,6 +6976,11 @@
         return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE);
     }
 
+    @Override
+    public long getCpuMeasuredBatteryConsumptionUC() {
+        return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
+    }
+
     /**
      * Returns the consumption (in microcoulombs) that the given standard power bucket consumed.
      * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable
@@ -8482,6 +8487,11 @@
             return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON);
         }
 
+        @Override
+        public long getCpuMeasuredBatteryConsumptionUC() {
+            return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU);
+        }
+
         void initNetworkActivityLocked() {
             detachIfNotNull(mNetworkByteActivityCounters);
             mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 97f727b..b15543a 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -85,12 +85,14 @@
                 builder.getUidBatteryConsumerBuilders();
         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
             final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
-            calculateApp(app, app.getBatteryStatsUid(), result);
+            calculateApp(app, app.getBatteryStatsUid(), query, result);
         }
     }
 
-    private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, Result result) {
-        calculatePowerAndDuration(u, BatteryStats.STATS_SINCE_CHARGED, result);
+    private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
+            BatteryUsageStatsQuery query, Result result) {
+        calculatePowerAndDuration(u, BatteryStats.STATS_SINCE_CHARGED,
+                query.shouldForceUsePowerProfileModel(), result);
 
         app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, result.powerMah)
                 .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, result.durationMs)
@@ -112,7 +114,7 @@
     }
 
     private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, Result result) {
-        calculatePowerAndDuration(u, statsType, result);
+        calculatePowerAndDuration(u, statsType, false, result);
 
         app.cpuPowerMah = result.powerMah;
         app.cpuTimeMs = result.durationMs;
@@ -120,46 +122,16 @@
         app.packageWithHighestDrain = result.packageWithHighestDrain;
     }
 
-    private void calculatePowerAndDuration(BatteryStats.Uid u, int statsType, Result result) {
+    private void calculatePowerAndDuration(BatteryStats.Uid u, int statsType,
+            boolean forceUsePowerProfileModel, Result result) {
         long durationMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
 
-        // Constant battery drain when CPU is active
-        double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime());
-
-        // Additional per-cluster battery drain
-        long[] cpuClusterTimes = u.getCpuClusterTimes();
-        if (cpuClusterTimes != null) {
-            if (cpuClusterTimes.length == mNumCpuClusters) {
-                for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
-                    double power = calculatePerCpuClusterPowerMah(cluster,
-                            cpuClusterTimes[cluster]);
-                    powerMah += power;
-                    if (DEBUG) {
-                        Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster
-                                + " clusterTimeMs=" + cpuClusterTimes[cluster]
-                                + " power=" + formatCharge(power));
-                    }
-                }
-            } else {
-                Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
-                        + mNumCpuClusters + " actual # " + cpuClusterTimes.length);
-            }
-        }
-
-        // Additional per-frequency battery drain
-        for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
-            final int speedsForCluster = mPerCpuFreqPowerEstimators[cluster].length;
-            for (int speed = 0; speed < speedsForCluster; speed++) {
-                final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
-                final double power = calculatePerCpuFreqPowerMah(cluster, speed,
-                        timeUs / 1000);
-                if (DEBUG) {
-                    Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
-                            + speed + " timeUs=" + timeUs + " power="
-                            + formatCharge(power));
-                }
-                powerMah += power;
-            }
+        final double powerMah;
+        final long consumptionUC = u.getCpuMeasuredBatteryConsumptionUC();
+        if (forceUsePowerProfileModel || consumptionUC == BatteryStats.POWER_DATA_UNAVAILABLE) {
+            powerMah = calculateUidModeledPowerMah(u, statsType);
+        } else {
+            powerMah = uCtoMah(consumptionUC);
         }
 
         if (DEBUG && (durationMs != 0 || powerMah != 0)) {
@@ -208,6 +180,48 @@
         result.packageWithHighestDrain = packageWithHighestDrain;
     }
 
+    private double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) {
+        // Constant battery drain when CPU is active
+        double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime());
+
+        // Additional per-cluster battery drain
+        long[] cpuClusterTimes = u.getCpuClusterTimes();
+        if (cpuClusterTimes != null) {
+            if (cpuClusterTimes.length == mNumCpuClusters) {
+                for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
+                    double power = calculatePerCpuClusterPowerMah(cluster,
+                            cpuClusterTimes[cluster]);
+                    powerMah += power;
+                    if (DEBUG) {
+                        Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster
+                                + " clusterTimeMs=" + cpuClusterTimes[cluster]
+                                + " power=" + formatCharge(power));
+                    }
+                }
+            } else {
+                Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
+                        + mNumCpuClusters + " actual # " + cpuClusterTimes.length);
+            }
+        }
+
+        // Additional per-frequency battery drain
+        for (int cluster = 0; cluster < mNumCpuClusters; cluster++) {
+            final int speedsForCluster = mPerCpuFreqPowerEstimators[cluster].length;
+            for (int speed = 0; speed < speedsForCluster; speed++) {
+                final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
+                final double power = calculatePerCpuFreqPowerMah(cluster, speed,
+                        timeUs / 1000);
+                if (DEBUG) {
+                    Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
+                            + speed + " timeUs=" + timeUs + " power="
+                            + formatCharge(power));
+                }
+                powerMah += power;
+            }
+        }
+        return powerMah;
+    }
+
     /**
      * Calculates active CPU power consumption.
      *
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 200e0dd..fea0751 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -257,4 +257,12 @@
      * file descriptor passed in.
      */
      void passThroughShellCommand(in String[] args, in ParcelFileDescriptor pfd);
+
+    /**
+     * Enables/disables the navigation bar luma sampling.
+     *
+     * @param displayId the id of the display to notify.
+     * @param enable {@code true} if enable, otherwise set to {@code false}.
+     */
+    void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable);
 }
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 85114e5..b57b4b9 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -72,5 +72,5 @@
     void onBarringInfoChanged(in BarringInfo barringInfo);
     void onPhysicalChannelConfigChanged(in List<PhysicalChannelConfig> configs);
     void onDataEnabledChanged(boolean enabled, int reason);
-    void onAllowedNetworkTypesChanged(in Map allowedNetworkTypeList);
+    void onAllowedNetworkTypesChanged(in int reason, in long allowedNetworkType);
 }
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 95e0a3b..83691ee 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -95,5 +95,5 @@
     void notifyPhysicalChannelConfigForSubscriber(in int subId,
             in List<PhysicalChannelConfig> configs);
     void notifyDataEnabled(in int phoneId, int subId, boolean enabled, int reason);
-    void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in Map allowedNetworkTypeList);
+    void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in int reason, in long allowedNetworkType);
 }
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 19506a3..d0c807d 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -31,8 +31,10 @@
 import android.util.imetracing.InputConnectionHelper;
 import android.util.proto.ProtoOutputStream;
 import android.view.KeyEvent;
+import android.view.View;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.DumpableInputConnection;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
@@ -49,7 +51,9 @@
 import com.android.internal.inputmethod.ISurroundingTextResultCallback;
 import com.android.internal.os.SomeArgs;
 
-public abstract class IInputConnectionWrapper extends IInputContext.Stub {
+import java.lang.ref.WeakReference;
+
+public final class IInputConnectionWrapper extends IInputContext.Stub {
     private static final String TAG = "IInputConnectionWrapper";
     private static final boolean DEBUG = false;
 
@@ -90,10 +94,13 @@
     private Looper mMainLooper;
     private Handler mH;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    private Object mLock = new Object();
+    private final Object mLock = new Object();
     @GuardedBy("mLock")
     private boolean mFinished = false;
 
+    private final InputMethodManager mParentInputMethodManager;
+    private final WeakReference<View> mServedView;
+
     class MyHandler extends Handler {
         MyHandler(Looper looper) {
             super(looper);
@@ -104,11 +111,15 @@
             executeMessage(msg);
         }
     }
-    
-    public IInputConnectionWrapper(Looper mainLooper, @NonNull InputConnection inputConnection) {
+
+    public IInputConnectionWrapper(@NonNull Looper mainLooper,
+            @NonNull InputConnection inputConnection,
+            @NonNull InputMethodManager inputMethodManager, @Nullable View servedView) {
         mInputConnection = inputConnection;
         mMainLooper = mainLooper;
         mH = new MyHandler(mMainLooper);
+        mParentInputMethodManager = inputMethodManager;
+        mServedView = new WeakReference<>(servedView);
     }
 
     @Nullable
@@ -118,21 +129,70 @@
         }
     }
 
-    protected Looper getLooper() {
-        synchronized (mMainLooper) {
-            return mMainLooper;
-        }
-    }
-
-    protected boolean isFinished() {
+    private boolean isFinished() {
         synchronized (mLock) {
             return mFinished;
         }
     }
 
-    protected abstract boolean isActive();
+    public boolean isActive() {
+        return mParentInputMethodManager.isActive() && !isFinished();
+    }
 
-    protected abstract InputMethodManager getIMM();
+    public View getServedView() {
+        return mServedView.get();
+    }
+
+    public void deactivate() {
+        if (isFinished()) {
+            // This is a small performance optimization.  Still only the 1st call of
+            // reportFinish() will take effect.
+            return;
+        }
+        closeConnection();
+
+        // Notify the app that the InputConnection was closed.
+        final View servedView = mServedView.get();
+        if (servedView != null) {
+            final Handler handler = servedView.getHandler();
+            // The handler is null if the view is already detached. When that's the case, for
+            // now, we simply don't dispatch this callback.
+            if (handler != null) {
+                if (DEBUG) {
+                    Log.v(TAG, "Calling View.onInputConnectionClosed: view=" + servedView);
+                }
+                if (handler.getLooper().isCurrentThread()) {
+                    servedView.onInputConnectionClosedInternal();
+                } else {
+                    handler.post(servedView::onInputConnectionClosedInternal);
+                }
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "IInputConnectionWrapper{"
+                + "connection=" + getInputConnection()
+                + " finished=" + isFinished()
+                + " mParentInputMethodManager.isActive()=" + mParentInputMethodManager.isActive()
+                + " mServedView=" + mServedView.get()
+                + "}";
+    }
+
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        synchronized (mLock) {
+            // Check that the call is initiated in the main thread of the current InputConnection
+            // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
+            // executed on this thread. Otherwise the messages are dispatched to the correct thread
+            // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance
+            // reasons.
+            if ((mInputConnection instanceof DumpableInputConnection)
+                    && Looper.myLooper() == mMainLooper) {
+                ((DumpableInputConnection) mInputConnection).dumpDebug(proto, fieldId);
+            }
+        }
+    }
 
     public void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback) {
         dispatchMessage(mH.obtainMessage(DO_GET_TEXT_AFTER_CURSOR, length, flags, callback));
@@ -161,6 +221,10 @@
         dispatchMessage(mH.obtainMessage(DO_GET_SURROUNDING_TEXT, flags, 0 /* unused */, args));
     }
 
+    public void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
+         // no-op
+    }
+
     public void getCursorCapsMode(int reqModes, IIntResultCallback callback) {
         dispatchMessage(
                 mH.obtainMessage(DO_GET_CURSOR_CAPS_MODE, reqModes, 0 /* unused */, callback));
@@ -309,7 +373,7 @@
                         icProto = InputConnectionHelper.buildGetTextAfterCursorProto(msg.arg1,
                                 msg.arg2, result);
                         ImeTracing.getInstance().triggerClientDump(
-                                TAG + "#getTextAfterCursor", getIMM(), icProto);
+                                TAG + "#getTextAfterCursor", mParentInputMethodManager, icProto);
                     }
                     try {
                         callback.onResult(result);
@@ -339,7 +403,7 @@
                         icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(msg.arg1,
                                 msg.arg2, result);
                         ImeTracing.getInstance().triggerClientDump(
-                                TAG + "#getTextBeforeCursor", getIMM(), icProto);
+                                TAG + "#getTextBeforeCursor", mParentInputMethodManager, icProto);
                     }
                     try {
                         callback.onResult(result);
@@ -368,7 +432,7 @@
                     if (ImeTracing.getInstance().isEnabled()) {
                         icProto = InputConnectionHelper.buildGetSelectedTextProto(msg.arg1, result);
                         ImeTracing.getInstance().triggerClientDump(
-                                TAG + "#getSelectedText", getIMM(), icProto);
+                                TAG + "#getSelectedText", mParentInputMethodManager, icProto);
                     }
                     try {
                         callback.onResult(result);
@@ -402,7 +466,7 @@
                         icProto = InputConnectionHelper.buildGetSurroundingTextProto(beforeLength,
                                 afterLength, flags, result);
                         ImeTracing.getInstance().triggerClientDump(
-                                TAG + "#getSurroundingText", getIMM(), icProto);
+                                TAG + "#getSurroundingText", mParentInputMethodManager, icProto);
                     }
                     try {
                         callback.onResult(result);
@@ -432,7 +496,7 @@
                         icProto = InputConnectionHelper.buildGetCursorCapsModeProto(msg.arg1,
                                 result);
                         ImeTracing.getInstance().triggerClientDump(
-                                TAG + "#getCursorCapsMode", getIMM(), icProto);
+                                TAG + "#getCursorCapsMode", mParentInputMethodManager, icProto);
                     }
                     try {
                         callback.onResult(result);
@@ -464,7 +528,7 @@
                         icProto = InputConnectionHelper.buildGetExtractedTextProto(request,
                                 msg.arg1, result);
                         ImeTracing.getInstance().triggerClientDump(
-                                TAG + "#getExtractedText", getIMM(), icProto);
+                                TAG + "#getExtractedText", mParentInputMethodManager, icProto);
                     }
                     try {
                         callback.onResult(result);
diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS
index d284d51..8e68be0 100644
--- a/core/java/com/android/internal/widget/OWNERS
+++ b/core/java/com/android/internal/widget/OWNERS
@@ -1,4 +1,6 @@
 per-file PointerLocationView.java = michaelwr@google.com, svv@google.com
+per-file RecyclerView.java = mount@google.com
+per-file ViewPager.java = mount@google.com
 
 # LockSettings related
 per-file *LockPattern* = file:/services/core/java/com/android/server/locksettings/OWNERS
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 3bc0ef4e..94ac183 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -637,6 +637,12 @@
     char saveResolvedClassesDelayMsOptsBuf[
             sizeof("-Xps-save-resolved-classes-delay-ms:")-1 + PROPERTY_VALUE_MAX];
     char madviseRandomOptsBuf[sizeof("-XX:MadviseRandomAccess:")-1 + PROPERTY_VALUE_MAX];
+    char madviseWillNeedFileSizeVdex[
+            sizeof("-XMadviseWillNeedVdexFileSize:")-1 + PROPERTY_VALUE_MAX];
+    char madviseWillNeedFileSizeOdex[
+            sizeof("-XMadviseWillNeedOdexFileSize:")-1 + PROPERTY_VALUE_MAX];
+    char madviseWillNeedFileSizeArt[
+            sizeof("-XMadviseWillNeedArtFileSize:")-1 + PROPERTY_VALUE_MAX];
     char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX];
     char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX];
     char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];
@@ -845,6 +851,22 @@
     parseRuntimeOption("dalvik.vm.madvise-random", madviseRandomOptsBuf, "-XX:MadviseRandomAccess:");
 
     /*
+     * Use default platform configuration as limits for madvising,
+     * when no properties are specified.
+     */
+    parseRuntimeOption("dalvik.vm.madvise.vdexfile.size",
+                       madviseWillNeedFileSizeVdex,
+                       "-XMadviseWillNeedVdexFileSize:");
+
+    parseRuntimeOption("dalvik.vm.madvise.odexfile.size",
+                       madviseWillNeedFileSizeOdex,
+                       "-XMadviseWillNeedOdexFileSize:");
+
+    parseRuntimeOption("dalvik.vm.madvise.artfile.size",
+                       madviseWillNeedFileSizeArt,
+                       "-XMadviseWillNeedArtFileSize:");
+
+    /*
      * Profile related options.
      */
     parseRuntimeOption("dalvik.vm.hot-startup-method-samples", hotstartupsamplesOptsBuf,
diff --git a/core/jni/android_os_incremental_IncrementalManager.cpp b/core/jni/android_os_incremental_IncrementalManager.cpp
index 2384efa..413bcef 100644
--- a/core/jni/android_os_incremental_IncrementalManager.cpp
+++ b/core/jni/android_os_incremental_IncrementalManager.cpp
@@ -41,6 +41,10 @@
     return (jboolean)IncFs_IsIncFsPath(path.c_str());
 }
 
+static jboolean nativeIsIncrementalFd(JNIEnv* env, jobject clazz, jint fd) {
+    return (jboolean)IncFs_IsIncFsFd(fd);
+}
+
 static jbyteArray nativeUnsafeGetFileSignature(JNIEnv* env, jobject clazz, jstring javaPath) {
     ScopedUtfChars path(env, javaPath);
 
@@ -61,6 +65,7 @@
         {{"nativeIsEnabled", "()Z", (void*)nativeIsEnabled},
          {"nativeIsV2Available", "()Z", (void*)nativeIsV2Available},
          {"nativeIsIncrementalPath", "(Ljava/lang/String;)Z", (void*)nativeIsIncrementalPath},
+         {"nativeIsIncrementalFd", "(I)Z", (void*)nativeIsIncrementalFd},
          {"nativeUnsafeGetFileSignature", "(Ljava/lang/String;)[B",
           (void*)nativeUnsafeGetFileSignature}};
 
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 96326f5..9746a07 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -34,8 +34,6 @@
 
 #include "core_jni_helpers.h"
 
-using android::base::Result;
-
 namespace android {
 
 // Log debug messages about the dispatch cycle.
@@ -199,9 +197,16 @@
     ScopedLocalRef<jobject> senderObj(env, NULL);
     bool skipCallbacks = false;
     for (;;) {
-        Result<InputPublisher::Finished> result = mInputPublisher.receiveFinishedSignal();
-        if (!result.ok()) {
-            const status_t status = result.error().code();
+        uint32_t publishedSeq;
+        bool handled;
+        std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)> callback =
+                [&publishedSeq, &handled](uint32_t inSeq, bool inHandled,
+                                          nsecs_t inConsumeTime) -> void {
+            publishedSeq = inSeq;
+            handled = inHandled;
+        };
+        status_t status = mInputPublisher.receiveFinishedSignal(callback);
+        if (status) {
             if (status == WOULD_BLOCK) {
                 return OK;
             }
@@ -210,7 +215,7 @@
             return status;
         }
 
-        auto it = mPublishedSeqMap.find(result->seq);
+        auto it = mPublishedSeqMap.find(publishedSeq);
         if (it == mPublishedSeqMap.end()) {
             continue;
         }
@@ -220,9 +225,9 @@
 
         if (kDebugDispatchCycle) {
             ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, "
-                  "pendingEvents=%zu.",
-                  getInputChannelName().c_str(), seq, result->handled ? "true" : "false",
-                  mPublishedSeqMap.size());
+                    "pendingEvents=%zu.",
+                    getInputChannelName().c_str(), seq, handled ? "true" : "false",
+                    mPublishedSeqMap.size());
         }
 
         if (!skipCallbacks) {
@@ -236,9 +241,8 @@
             }
 
             env->CallVoidMethod(senderObj.get(),
-                                gInputEventSenderClassInfo.dispatchInputEventFinished,
-                                static_cast<jint>(result->seq),
-                                static_cast<jboolean>(result->handled));
+                    gInputEventSenderClassInfo.dispatchInputEventFinished,
+                    jint(seq), jboolean(handled));
             if (env->ExceptionCheck()) {
                 ALOGE("Exception dispatching finished signal.");
                 skipCallbacks = true;
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index ebc507a..469e577 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -16,9 +16,10 @@
 
 #include <android_runtime/AndroidRuntime.h>
 
-#include <input/KeyCharacterMap.h>
-#include <input/Input.h>
 #include <binder/Parcel.h>
+#include <input/Input.h>
+#include <input/InputDevice.h>
+#include <input/KeyCharacterMap.h>
 
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
@@ -75,6 +76,10 @@
                           reinterpret_cast<jlong>(nativeMap));
 }
 
+static jobject nativeObtainEmptyKeyCharacterMap(JNIEnv* env, jobject /* clazz */, jint deviceId) {
+    return android_view_KeyCharacterMap_create(env, deviceId, nullptr);
+}
+
 static jlong nativeReadFromParcel(JNIEnv *env, jobject clazz, jobject parcelObj) {
     Parcel* parcel = parcelForJavaObject(env, parcelObj);
     if (!parcel) {
@@ -224,33 +229,37 @@
     return result;
 }
 
+static jboolean nativeEquals(JNIEnv* env, jobject clazz, jlong ptr1, jlong ptr2) {
+    const std::shared_ptr<KeyCharacterMap>& map1 =
+            (reinterpret_cast<NativeKeyCharacterMap*>(ptr1))->getMap();
+    const std::shared_ptr<KeyCharacterMap>& map2 =
+            (reinterpret_cast<NativeKeyCharacterMap*>(ptr2))->getMap();
+    if (map1 == nullptr || map2 == nullptr) {
+        return map1 == map2;
+    }
+    return static_cast<jboolean>(*map1 == *map2);
+}
 
 /*
  * JNI registration.
  */
 
 static const JNINativeMethod g_methods[] = {
-    /* name, signature, funcPtr */
-    { "nativeReadFromParcel", "(Landroid/os/Parcel;)J",
-            (void*)nativeReadFromParcel },
-    { "nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
-            (void*)nativeWriteToParcel },
-    { "nativeDispose", "(J)V",
-            (void*)nativeDispose },
-    { "nativeGetCharacter", "(JII)C",
-            (void*)nativeGetCharacter },
-    { "nativeGetFallbackAction", "(JIILandroid/view/KeyCharacterMap$FallbackAction;)Z",
-            (void*)nativeGetFallbackAction },
-    { "nativeGetNumber", "(JI)C",
-            (void*)nativeGetNumber },
-    { "nativeGetMatch", "(JI[CI)C",
-            (void*)nativeGetMatch },
-    { "nativeGetDisplayLabel", "(JI)C",
-            (void*)nativeGetDisplayLabel },
-    { "nativeGetKeyboardType", "(J)I",
-            (void*)nativeGetKeyboardType },
-    { "nativeGetEvents", "(J[C)[Landroid/view/KeyEvent;",
-            (void*)nativeGetEvents },
+        /* name, signature, funcPtr */
+        {"nativeReadFromParcel", "(Landroid/os/Parcel;)J", (void*)nativeReadFromParcel},
+        {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteToParcel},
+        {"nativeDispose", "(J)V", (void*)nativeDispose},
+        {"nativeGetCharacter", "(JII)C", (void*)nativeGetCharacter},
+        {"nativeGetFallbackAction", "(JIILandroid/view/KeyCharacterMap$FallbackAction;)Z",
+         (void*)nativeGetFallbackAction},
+        {"nativeGetNumber", "(JI)C", (void*)nativeGetNumber},
+        {"nativeGetMatch", "(JI[CI)C", (void*)nativeGetMatch},
+        {"nativeGetDisplayLabel", "(JI)C", (void*)nativeGetDisplayLabel},
+        {"nativeGetKeyboardType", "(J)I", (void*)nativeGetKeyboardType},
+        {"nativeGetEvents", "(J[C)[Landroid/view/KeyEvent;", (void*)nativeGetEvents},
+        {"nativeObtainEmptyKeyCharacterMap", "(I)Landroid/view/KeyCharacterMap;",
+         (void*)nativeObtainEmptyKeyCharacterMap},
+        {"nativeEquals", "(JJ)Z", (void*)nativeEquals},
 };
 
 int register_android_view_KeyCharacterMap(JNIEnv* env)
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index cf96fe6..0bed29b 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -193,7 +193,7 @@
  * If it exceeds 2s, PROC_START_TIMEOUT_MSG will kill the starting app anyway,
  * so it's fine to assume max retries is 5 mins.
  */
-static constexpr int STORAGE_DIR_CHECK_TIMEOUT_US = 1000 * 60 * 5;
+static constexpr int STORAGE_DIR_CHECK_TIMEOUT_US = 1000 * 1000 * 60 * 5;
 
 /**
  * A helper class containing accounting information for USAPs.
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index e62b5c1..ea5e7f72 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -14,9 +14,10 @@
 # Frameworks
 ogunwale@google.com
 jjaggi@google.com
+kwekua@google.com
 roosa@google.com
 per-file package_item_info.proto = toddke@google.com
-per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
+per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/android/app/usage/OWNERS
 per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS
 
 # Biometrics
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index f26bf7c..a7127ad 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -346,6 +346,7 @@
     optional int32 proc_id = 29;
     optional bool translucent = 30;
     optional bool pip_auto_enter_enabled = 31;
+    optional bool in_size_compat_mode = 32;
 }
 
 /* represents WindowToken */
@@ -406,7 +407,7 @@
     optional int64 finished_seamless_rotation_frame = 40;
     optional WindowFramesProto window_frames = 41;
     optional bool force_seamless_rotation = 42;
-    optional bool in_size_compat_mode = 43;
+    optional bool has_compat_scale = 43;
     optional float global_scale = 44;
 }
 
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7c446a9..5412db6 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8173,7 +8173,7 @@
             <flag name="hide_from_picker" value="0x2" />
               <!-- The widget provides a default configuration. The host may decide not to launch
                    the provided configuration activity. -->
-           <flag name="configuration_optional" value="0x3" />
+           <flag name="configuration_optional" value="0x4" />
         </attr>
         <!-- A resource identifier for a string containing a short description of the widget. -->
         <attr name="description" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index cc52655ad7..601d66e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -829,7 +829,6 @@
             {@code FLAG_ACTIVITY_MULTIPLE_TASK} is set.-->
         <enum name="singleInstancePerTask" value="4" />
     </attr>
-
     <!-- Specify the orientation an activity should be run in.  If not
          specified, it will run in the current preferred orientation
          of the screen.
@@ -1603,6 +1602,12 @@
        <enum name="sync" value="2" />
     </attr>
 
+    <!-- Attribution tag to be used for permission sub-attribution if a
+      permission is checked in  {@link android.content.Context#sendBroadcast(Intent, String)}.
+      Multiple tags can be specified separated by '|'.
+    -->
+    <attr name="attributionTags" format="string" />
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
@@ -2825,6 +2830,10 @@
 
              <p> See {@link android.content.pm.ActivityInfo#FLAG_PREFER_MINIMAL_POST_PROCESSING} -->
         <attr name="preferMinimalPostProcessing" format="boolean"/>
+        <!-- Specify the attributionTags to be used if a permission is required due to
+            {@link android.content.Context#sendBroadcast(Intent, String)} being used.
+            Multiple tags can be specified separated by '|'. -->
+        <attr name="attributionTags"/>
     </declare-styleable>
 
     <!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 397c646..f6fee88 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -324,9 +324,6 @@
         <item>"0,1"</item>
     </string-array>
 
-    <!-- The maximum duration (in milliseconds) we expect a network transition to take -->
-    <integer name="config_networkTransitionTimeout">60000</integer>
-
     <!-- Whether/how to notify the user on network switches. See LingerMonitor.java. -->
     <integer translatable="false" name="config_networkNotifySwitchType">0</integer>
 
@@ -339,16 +336,6 @@
          Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
     <integer translatable="false" name="config_networkAvoidBadWifi">1</integer>
 
-    <!-- Configuration hook for the URL returned by ConnectivityManager#getCaptivePortalServerUrl.
-         If empty, the returned value is controlled by Settings.Global.CAPTIVE_PORTAL_HTTP_URL,
-         and if that value is empty, the framework will use a hard-coded default.
-         This is *NOT* a URL that will always be used by the system network validation to detect
-         captive portals: NetworkMonitor may use different strategies and will not necessarily use
-         this URL. NetworkMonitor behaviour should be configured with NetworkStack resource overlays
-         instead. -->
-    <!--suppress CheckTagEmptyBody -->
-    <string translatable="false" name="config_networkCaptivePortalServerUrl"></string>
-
     <!-- If the hardware supports specially marking packets that caused a wakeup of the
          main CPU, set this value to the mark used. -->
     <integer name="config_networkWakeupPacketMark">0</integer>
@@ -456,14 +443,6 @@
          apps requested it. -->
     <bool name="config_vehicleInternalNetworkAlwaysRequested">false</bool>
 
-    <!-- Configuration of network interfaces that support WakeOnLAN -->
-    <string-array translatable="false" name="config_wakeonlan_supported_interfaces">
-        <!--
-        <item>wlan0</item>
-        <item>eth0</item>
-        -->
-    </string-array>
-
     <!-- This setting is deprecated, please use
          com.android.networkstack.tethering.R.array.config_mobile_hotspot_provision_app instead. -->
     <string-array translatable="false" name="config_mobile_hotspot_provision_app">
@@ -4743,4 +4722,97 @@
 
     <!-- Whether to allow the caching of the SIM PIN for verification after unattended reboot -->
     <bool name="config_allow_pin_storage_for_unattended_reboot">true</bool>
+
+    <!-- CEC Configuration -->
+    <bool name="config_cecHdmiCecEnabled_userConfigurable">true</bool>
+    <bool name="config_cecHdmiCecControlEnabled_allowed">true</bool>
+    <bool name="config_cecHdmiCecControlEnabled_default">true</bool>
+    <bool name="config_cecHdmiCecControlDisabled_allowed">true</bool>
+    <bool name="config_cecHdmiCecControlDisabled_default">false</bool>
+
+    <bool name="config_cecHdmiCecVersion_userConfigurable">true</bool>
+    <bool name="config_cecHdmiCecVersion14b_allowed">true</bool>
+    <bool name="config_cecHdmiCecVersion14b_default">true</bool>
+    <bool name="config_cecHdmiCecVersion20_allowed">true</bool>
+    <bool name="config_cecHdmiCecVersion20_default">false</bool>
+
+    <bool name="config_cecSendStandbyOnSleep_userConfigurable">true</bool>
+    <bool name="config_cecPowerControlModeTv_allowed">true</bool>
+    <bool name="config_cecPowerControlModeTv_default">true</bool>
+    <bool name="config_cecPowerControlModeBroadcast_allowed">true</bool>
+    <bool name="config_cecPowerControlModeBroadcast_default">false</bool>
+    <bool name="config_cecPowerControlModeNone_allowed">true</bool>
+    <bool name="config_cecPowerControlModeNone_default">false</bool>
+
+    <bool name="config_cecPowerStateChangeOnActiveSourceLost_userConfigurable">true</bool>
+    <bool name="config_cecPowerStateChangeOnActiveSourceLostNone_allowed">true</bool>
+    <bool name="config_cecPowerStateChangeOnActiveSourceLostNone_default">true</bool>
+    <bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed">true</bool>
+    <bool name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default">false</bool>
+
+    <bool name="config_cecSystemAudioModeMuting_userConfigurable">true</bool>
+    <bool name="config_cecSystemAudioModeMutingEnabled_allowed">true</bool>
+    <bool name="config_cecSystemAudioModeMutingEnabled_default">true</bool>
+    <bool name="config_cecSystemAudioModeMutingDisabled_allowed">true</bool>
+    <bool name="config_cecSystemAudioModeMutingDisabled_default">false</bool>
+
+    <bool name="config_cecVolumeControlMode_userConfigurable">true</bool>
+    <bool name="config_cecVolumeControlModeEnabled_allowed">true</bool>
+    <bool name="config_cecVolumeControlModeEnabled_default">true</bool>
+    <bool name="config_cecVolumeControlModeDisabled_allowed">true</bool>
+    <bool name="config_cecVolumeControlModeDisabled_default">false</bool>
+
+    <bool name="config_cecTvWakeOnOneTouchPlay_userConfigurable">true</bool>
+    <bool name="config_cecTvWakeOnOneTouchPlayEnabled_allowed">true</bool>
+    <bool name="config_cecTvWakeOnOneTouchPlayEnabled_default">true</bool>
+    <bool name="config_cecTvWakeOnOneTouchPlayDisabled_allowed">true</bool>
+    <bool name="config_cecTvWakeOnOneTouchPlayDisabled_default">false</bool>
+
+    <bool name="config_cecTvSendStandbyOnSleep_userConfigurable">true</bool>
+    <bool name="config_cecTvSendStandbyOnSleepEnabled_allowed">true</bool>
+    <bool name="config_cecTvSendStandbyOnSleepEnabled_default">true</bool>
+    <bool name="config_cecTvSendStandbyOnSleepDisabled_allowed">true</bool>
+    <bool name="config_cecTvSendStandbyOnSleepDisabled_default">false</bool>
+
+    <bool name="config_cecRcProfileTv_userConfigurable">true</bool>
+    <bool name="config_cecRcProfileTvNone_allowed">true</bool>
+    <bool name="config_cecRcProfileTvNone_default">true</bool>
+    <bool name="config_cecRcProfileTvOne_allowed">true</bool>
+    <bool name="config_cecRcProfileTvOne_default">false</bool>
+    <bool name="config_cecRcProfileTvTwo_allowed">true</bool>
+    <bool name="config_cecRcProfileTvTwo_default">false</bool>
+    <bool name="config_cecRcProfileTvThree_allowed">true</bool>
+    <bool name="config_cecRcProfileTvThree_default">false</bool>
+    <bool name="config_cecRcProfileTvFour_allowed">true</bool>
+    <bool name="config_cecRcProfileTvFour_default">false</bool>
+
+    <bool name="config_cecRcProfileSourceRootMenu_userConfigurable">true</bool>
+    <bool name="config_cecRcProfileSourceRootMenuHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceRootMenuHandled_default">true</bool>
+    <bool name="config_cecRcProfileSourceRootMenuNotHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceRootMenuNotHandled_default">false</bool>
+
+    <bool name="config_cecRcProfileSourceSetupMenu_userConfigurable">true</bool>
+    <bool name="config_cecRcProfileSourceSetupMenuHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceSetupMenuHandled_default">true</bool>
+    <bool name="config_cecRcProfileSourceSetupMenuNotHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceSetupMenuNotHandled_default">false</bool>
+
+    <bool name="config_cecRcProfileSourceContentsMenu_userConfigurable">true</bool>
+    <bool name="config_cecRcProfileSourceContentsMenuHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceContentsMenuHandled_default">false</bool>
+    <bool name="config_cecRcProfileSourceContentsMenuNotHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceContentsMenuNotHandled_default">true</bool>
+
+    <bool name="config_cecRcProfileSourceTopMenu_userConfigurable">true</bool>
+    <bool name="config_cecRcProfileSourceTopMenuHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceTopMenuHandled_default">false</bool>
+    <bool name="config_cecRcProfileSourceTopMenuNotHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceTopMenuNotHandled_default">true</bool>
+
+    <bool name="config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable">true</bool>
+    <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default">false</bool>
+    <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed">true</bool>
+    <bool name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default">true</bool>
 </resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 3a41d5f..7bc4663 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -247,4 +247,10 @@
 
   <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_IME_ENTER}. -->
   <item type="id" name="accessibilityActionImeEnter" />
+
+  <!-- View tag for remote views to store the index of the next child when adding nested remote views dynamically. -->
+  <item type="id" name="remote_views_next_child" />
+
+  <!-- View tag associating a view with its stable id for potential recycling. -->
+  <item type="id" name = "remote_views_stable_id" />
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e0a728c..40c80db 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3087,6 +3087,7 @@
     <public name="passwordsActivity"/>
     <public name="selectableAsDefault"/>
     <public name="isAccessibilityTool"/>
+    <public name="attributionTags"/>
   </public-group>
 
   <public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4af561b..387c065 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3691,6 +3691,8 @@
     <string name="ext_media_checking_notification_title">Checking <xliff:g id="name" example="SD card">%s</xliff:g>\u2026</string>
     <!-- Notification body when external media is being checked [CHAR LIMIT=NONE] -->
     <string name="ext_media_checking_notification_message">Reviewing current content</string>
+    <!-- TV specific notification body when external media is being checked [CHAR LIMIT=75] -->
+    <string name="ext_media_checking_notification_message" product="tv">Analyzing media storage</string>
 
     <!-- Notification body when new external media is detected [CHAR LIMIT=30] -->
     <string name="ext_media_new_notification_title">New <xliff:g id="name" example="SD card">%s</xliff:g></string>
@@ -3698,11 +3700,15 @@
     <string name="ext_media_new_notification_title" product="automotive"><xliff:g id="name" example="SD card">%s</xliff:g> isn\u2019t working</string>
     <!-- Notification body when new external media is detected [CHAR LIMIT=NONE] -->
     <string name="ext_media_new_notification_message">Tap to set up</string>
+    <!-- TV specific notification body when new external media is detected [CHAR LIMIT=75] -->
+    <string name="ext_media_new_notification_message" product="tv">Select to set up</string>
     <!-- Automotive specific notification body when new external media is detected. [CHAR LIMIT=NONE] -->
     <string name="ext_media_new_notification_message" product="automotive">You may need to reformat the device. Tap to eject.</string>
 
     <!-- Notification body when external media is ready for use [CHAR LIMIT=NONE] -->
     <string name="ext_media_ready_notification_message">For transferring photos and media</string>
+    <!-- TV specific notification body when external media is ready for use [CHAR LIMIT=75] -->
+    <string name="ext_media_ready_notification_message" product="tv">Browse media files</string>
 
     <!-- Notification title when external media is unmountable (corrupt) [CHAR LIMIT=30] -->
     <string name="ext_media_unmountable_notification_title">Issue with <xliff:g id="name" example="SD card">%s</xliff:g></string>
@@ -3721,8 +3727,8 @@
     <string name="ext_media_unsupported_notification_title" product="automotive"><xliff:g id="name" example="SD card">%s</xliff:g> isn\u2019t working</string>
     <!-- Notification body when external media is unsupported [CHAR LIMIT=NONE] -->
     <string name="ext_media_unsupported_notification_message">This device doesn\u2019t support this <xliff:g id="name" example="SD card">%s</xliff:g>. Tap to set up in a supported format.</string>
-    <!-- TV-specific notification body when external media is unsupported [CHAR LIMIT=NONE] -->
-    <string name="ext_media_unsupported_notification_message" product="tv">This device doesn\u2019t support this <xliff:g id="name" example="SD card">%s</xliff:g>. Select to set up in a supported format.</string>
+    <!-- TV-specific notification body when external media is unsupported [CHAR LIMIT=75] -->
+    <string name="ext_media_unsupported_notification_message" product="tv">Select to set up <xliff:g id="name" example="SD card">%s</xliff:g> in a supported format.</string>
     <!-- Automotive specific notification body when external media is unsupported [CHAR LIMIT=NONE] -->
     <string name="ext_media_unsupported_notification_message" product="automotive">You may need to reformat the device</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5f4063b..5a7b1fa 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -692,7 +692,6 @@
   <java-symbol type="string" name="not_checked" />
   <java-symbol type="array" name="config_ethernet_interfaces" />
   <java-symbol type="bool" name="config_vehicleInternalNetworkAlwaysRequested" />
-  <java-symbol type="array" name="config_wakeonlan_supported_interfaces" />
   <java-symbol type="string" name="config_forceVoiceInteractionServicePackage" />
   <java-symbol type="string" name="config_mms_user_agent" />
   <java-symbol type="string" name="config_mms_user_agent_profile_url" />
@@ -1972,11 +1971,9 @@
   <java-symbol type="integer" name="config_lowBatteryCloseWarningBump" />
   <java-symbol type="integer" name="config_lowBatteryWarningLevel" />
   <java-symbol type="integer" name="config_networkPolicyDefaultWarning" />
-  <java-symbol type="integer" name="config_networkTransitionTimeout" />
   <java-symbol type="integer" name="config_networkNotifySwitchType" />
   <java-symbol type="array" name="config_networkNotifySwitches" />
   <java-symbol type="integer" name="config_networkAvoidBadWifi" />
-  <java-symbol type="string" name="config_networkCaptivePortalServerUrl" />
   <java-symbol type="integer" name="config_networkWakeupPacketMark" />
   <java-symbol type="integer" name="config_networkWakeupPacketMask" />
   <java-symbol type="bool" name="config_apfDrop802_3Frames" />
@@ -4227,4 +4224,100 @@
   <java-symbol type="bool" name="config_voice_data_sms_auto_fallback" />
 
   <java-symbol type="bool" name="config_enableOneHandedKeyguard" />
+
+  <!-- CEC Configuration -->
+  <java-symbol type="bool" name="config_cecHdmiCecEnabled_userConfigurable" />
+  <java-symbol type="bool" name="config_cecHdmiCecControlEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecHdmiCecControlEnabled_default" />
+  <java-symbol type="bool" name="config_cecHdmiCecControlDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecHdmiCecControlDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecHdmiCecVersion_userConfigurable" />
+  <java-symbol type="bool" name="config_cecHdmiCecVersion14b_allowed" />
+  <java-symbol type="bool" name="config_cecHdmiCecVersion14b_default" />
+  <java-symbol type="bool" name="config_cecHdmiCecVersion20_allowed" />
+  <java-symbol type="bool" name="config_cecHdmiCecVersion20_default" />
+
+  <java-symbol type="bool" name="config_cecSendStandbyOnSleep_userConfigurable" />
+  <java-symbol type="bool" name="config_cecPowerControlModeTv_allowed" />
+  <java-symbol type="bool" name="config_cecPowerControlModeTv_default" />
+  <java-symbol type="bool" name="config_cecPowerControlModeBroadcast_allowed" />
+  <java-symbol type="bool" name="config_cecPowerControlModeBroadcast_default" />
+  <java-symbol type="bool" name="config_cecPowerControlModeNone_allowed" />
+  <java-symbol type="bool" name="config_cecPowerControlModeNone_default" />
+
+  <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLost_userConfigurable" />
+  <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostNone_allowed" />
+  <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostNone_default" />
+  <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed" />
+  <java-symbol type="bool" name="config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default" />
+
+  <java-symbol type="bool" name="config_cecSystemAudioModeMuting_userConfigurable" />
+  <java-symbol type="bool" name="config_cecSystemAudioModeMutingEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecSystemAudioModeMutingEnabled_default" />
+  <java-symbol type="bool" name="config_cecSystemAudioModeMutingDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecSystemAudioModeMutingDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecVolumeControlMode_userConfigurable" />
+  <java-symbol type="bool" name="config_cecVolumeControlModeEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecVolumeControlModeEnabled_default" />
+  <java-symbol type="bool" name="config_cecVolumeControlModeDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecVolumeControlModeDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlay_userConfigurable" />
+  <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlayEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlayEnabled_default" />
+  <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlayDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecTvWakeOnOneTouchPlayDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecTvSendStandbyOnSleep_userConfigurable" />
+  <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepEnabled_allowed" />
+  <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepEnabled_default" />
+  <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepDisabled_allowed" />
+  <java-symbol type="bool" name="config_cecTvSendStandbyOnSleepDisabled_default" />
+
+  <java-symbol type="bool" name="config_cecRcProfileTv_userConfigurable" />
+  <java-symbol type="bool" name="config_cecRcProfileTvNone_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileTvNone_default" />
+  <java-symbol type="bool" name="config_cecRcProfileTvOne_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileTvOne_default" />
+  <java-symbol type="bool" name="config_cecRcProfileTvTwo_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileTvTwo_default" />
+  <java-symbol type="bool" name="config_cecRcProfileTvThree_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileTvThree_default" />
+  <java-symbol type="bool" name="config_cecRcProfileTvFour_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileTvFour_default" />
+
+  <java-symbol type="bool" name="config_cecRcProfileSourceRootMenu_userConfigurable" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceRootMenuHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceRootMenuHandled_default" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceRootMenuNotHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceRootMenuNotHandled_default" />
+
+  <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenu_userConfigurable" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenuHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenuHandled_default" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenuNotHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceSetupMenuNotHandled_default" />
+
+  <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenu_userConfigurable" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenuHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenuHandled_default" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenuNotHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceContentsMenuNotHandled_default" />
+
+  <java-symbol type="bool" name="config_cecRcProfileSourceTopMenu_userConfigurable" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceTopMenuHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceTopMenuHandled_default" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceTopMenuNotHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceTopMenuNotHandled_default" />
+
+  <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed" />
+  <java-symbol type="bool" name="config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default" />
+
+  <java-symbol type="id" name="remote_views_next_child" />
+  <java-symbol type="id" name="remote_views_stable_id" />
 </resources>
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index f6e02bc..28f9ccc 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -41,7 +41,9 @@
 
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import android.content.res.TypedArray;
 import android.text.Selection;
@@ -356,4 +358,71 @@
                     .perform(clearText());
         }
     }
+
+    @Test
+    public void testCursorVisibility() {
+        final TextView textView = getActivity().findViewById(R.id.textview);
+        final String text = "abc";
+
+        assertTrue(textView.isCursorVisible());
+
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(replaceText(text));
+        final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+                new String[]{"ABC"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
+        setSuggestionSpan(suggestionSpan, text.indexOf('a'), text.indexOf('c') + 1);
+        showSuggestionsPopup();
+
+        assertSuggestionsPopupIsDisplayed();
+        assertSuggestionsPopupContainsItem("ABC");
+        assertFalse(textView.isCursorVisible());
+
+        // Delete an item.
+        clickSuggestionsPopupItem(
+                getActivity().getString(com.android.internal.R.string.delete));
+        assertSuggestionsPopupIsNotDisplayed();
+        assertTrue(textView.isCursorVisible());
+    }
+
+    @Test
+    public void testCursorVisibilityWhenImeConsumesInput() {
+        final TextView textView = getActivity().findViewById(R.id.textview);
+        final String text = "abc";
+
+        assertTrue(textView.isCursorVisible());
+
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(replaceText(text));
+        setImeConsumesInputWithExpect(textView, true /* imeConsumesInput */,
+                false /* expectedCursorVisibility */);
+        final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+                new String[]{"ABC"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
+        setSuggestionSpan(suggestionSpan, text.indexOf('a'), text.indexOf('c') + 1);
+        showSuggestionsPopup();
+
+        assertSuggestionsPopupIsDisplayed();
+        assertSuggestionsPopupContainsItem("ABC");
+        assertFalse(textView.isCursorVisible());
+
+        // Delete an item.
+        clickSuggestionsPopupItem(
+                getActivity().getString(com.android.internal.R.string.delete));
+        assertSuggestionsPopupIsNotDisplayed();
+        assertFalse(textView.isCursorVisible());
+
+        // Set IME not consumes input, cursor should be back to visible.
+        setImeConsumesInputWithExpect(textView, false /* imeConsumesInput */,
+                true /* expectedCursorVisibility */);
+    }
+
+    private void setImeConsumesInputWithExpect(
+            final TextView textView, boolean imeConsumesInput, boolean expectedCursorVisibility) {
+        textView.post(() -> textView.setImeConsumesInput(imeConsumesInput));
+        getInstrumentation().waitForIdleSync();
+        if (expectedCursorVisibility) {
+            assertTrue(textView.isCursorVisible());
+        } else {
+            assertFalse(textView.isCursorVisible());
+        }
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
index 7088890..10ff3a4 100644
--- a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java
@@ -19,18 +19,22 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.os.BatteryConsumer;
+import android.os.BatteryUsageStatsQuery;
 import android.os.Process;
 import android.os.UidBatteryConsumer;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.power.MeasuredEnergyStats;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -81,6 +85,10 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
+        final boolean[] supportedPowerBuckets =
+                new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
+        supportedPowerBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
+
         mStatsRule.getBatteryStats()
                 .setUserInfoProvider(mMockUserInfoProvider)
                 .setKernelCpuSpeedReaders(mMockKernelCpuSpeedReaders)
@@ -88,7 +96,8 @@
                 .setKernelCpuUidClusterTimeReader(mMockKernelCpuUidClusterTimeReader)
                 .setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader)
                 .setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader)
-                .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader);
+                .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader)
+                .initMeasuredEnergyStatsLocked(supportedPowerBuckets, 0);
     }
 
     @Test
@@ -103,28 +112,28 @@
 
         // User/System CPU time
         doAnswer(invocation -> {
-            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0);
+            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
             // User/system time in microseconds
             callback.onUidCpuTime(APP_UID1, new long[]{1111000, 2222000});
             callback.onUidCpuTime(APP_UID2, new long[]{3333000, 4444000});
             return null;
-        }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(any());
+        }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any());
 
         // Active CPU time
         doAnswer(invocation -> {
-            final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0);
+            final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1);
             callback.onUidCpuTime(APP_UID1, 1111L);
             callback.onUidCpuTime(APP_UID2, 3333L);
             return null;
-        }).when(mMockKerneCpuUidActiveTimeReader).readDelta(any());
+        }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any());
 
         // Per-cluster CPU time
         doAnswer(invocation -> {
-            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0);
+            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
             callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222});
             callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444});
             return null;
-        }).when(mMockKernelCpuUidClusterTimeReader).readDelta(any());
+        }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any());
 
         mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, null);
 
@@ -134,7 +143,8 @@
         CpuPowerCalculator calculator =
                 new CpuPowerCalculator(mStatsRule.getPowerProfile());
 
-        mStatsRule.apply(calculator);
+        mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(),
+                calculator);
 
         UidBatteryConsumer uidConsumer1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
         assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
@@ -150,4 +160,64 @@
                 .isWithin(PRECISION).of(2.672322);
         assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull();
     }
+
+    @Test
+    public void testMeasuredEnergyBasedModel() {
+        when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
+
+        when(mMockKernelCpuSpeedReaders[0].readDelta()).thenReturn(new long[]{1000, 2000});
+        when(mMockKernelCpuSpeedReaders[1].readDelta()).thenReturn(new long[]{3000, 4000});
+
+        when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false);
+
+        // User/System CPU time
+        doAnswer(invocation -> {
+            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
+            // User/system time in microseconds
+            callback.onUidCpuTime(APP_UID1, new long[]{1111000, 2222000});
+            callback.onUidCpuTime(APP_UID2, new long[]{3333000, 4444000});
+            return null;
+        }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any());
+
+        // Active CPU time
+        doAnswer(invocation -> {
+            final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1);
+            callback.onUidCpuTime(APP_UID1, 1111L);
+            callback.onUidCpuTime(APP_UID2, 3333L);
+            return null;
+        }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any());
+
+        // Per-cluster CPU time
+        doAnswer(invocation -> {
+            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
+            callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222});
+            callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444});
+            return null;
+        }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any());
+
+        final long[] clusterChargesUC = new long[]{13577531, 24688642};
+        mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, clusterChargesUC);
+
+        mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("foo").addCpuTimeLocked(4321, 1234);
+        mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("bar").addCpuTimeLocked(5432, 2345);
+
+        CpuPowerCalculator calculator =
+                new CpuPowerCalculator(mStatsRule.getPowerProfile());
+
+        mStatsRule.apply(calculator);
+
+        UidBatteryConsumer uidConsumer1 = mStatsRule.getUidBatteryConsumer(APP_UID1);
+        assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
+                .isEqualTo(3333);
+        assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
+                .isWithin(PRECISION).of(3.18877);
+        assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar");
+
+        UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2);
+        assertThat(uidConsumer2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU))
+                .isEqualTo(7777);
+        assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU))
+                .isWithin(PRECISION).of(7.44072);
+        assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull();
+    }
 }
diff --git a/core/tests/mockingcoretests/src/android/view/DisplayTests.java b/core/tests/mockingcoretests/src/android/view/DisplayTests.java
index 678f21f..a036db2 100644
--- a/core/tests/mockingcoretests/src/android/view/DisplayTests.java
+++ b/core/tests/mockingcoretests/src/android/view/DisplayTests.java
@@ -73,6 +73,11 @@
     private static Rect sAppBoundsPortrait = buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT);
     private static Rect sAppBoundsLandscape = buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH);
 
+    // Bounds of the device.
+    private static Rect sDeviceBoundsPortrait = new Rect(0, 0, LOGICAL_WIDTH, LOGICAL_HEIGHT);
+    private static Rect sDeviceBoundsLandscape = new Rect(0, 0, LOGICAL_HEIGHT, LOGICAL_WIDTH);
+
+
     private StaticMockitoSession mMockitoSession;
 
     private DisplayManagerGlobal mDisplayManagerGlobal;
@@ -278,29 +283,57 @@
     }
 
     @Test
-    public void testGetRealSize_resourcesPortraitSandboxed_matchesSandboxBounds() {
+    public void testGetRealSize_resourcesPortraitSandboxed_matchesAppSandboxBounds() {
         // GIVEN display is not rotated.
         setDisplayInfoPortrait(mDisplayInfo);
         // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsPortrait);
+        setMaxBoundsSandboxed(mApplicationContext.getResources(), sAppBoundsPortrait);
         final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
                 mApplicationContext.getResources());
         // THEN real size matches app bounds.
-        verifyRealSizeMatchesApp(display, sAppBoundsPortrait);
+        verifyRealSizeMatchesBounds(display, sAppBoundsPortrait);
     }
 
     @Test
-    public void testGetRealSize_resourcesLandscapeSandboxed_matchesSandboxBounds() {
+    public void testGetRealSize_resourcesPortraitSandboxed_matchesDisplayAreaSandboxBounds() {
+        // GIVEN display is not rotated.
+        setDisplayInfoPortrait(mDisplayInfo);
+        // GIVEN max bounds reflect DisplayArea size, which is the same size as the display.
+        setMaxBoundsSandboxed(mApplicationContext.getResources(), sDeviceBoundsPortrait);
+        // GIVEN app bounds do not stretch to include the full DisplayArea.
+        mApplicationContext.getResources().getConfiguration().windowConfiguration
+                .setAppBounds(buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT - 10));
+        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+                mApplicationContext.getResources());
+        // THEN real metrics matches max bounds for the DisplayArea.
+        verifyRealSizeMatchesBounds(display, sDeviceBoundsPortrait);
+    }
+
+    @Test
+    public void testGetRealSize_resourcesLandscapeSandboxed_matchesAppSandboxBounds() {
         // GIVEN display is rotated.
         setDisplayInfoLandscape(mDisplayInfo);
         // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsLandscape);
+        setMaxBoundsSandboxed(mApplicationContext.getResources(), sAppBoundsLandscape);
         final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
                 mApplicationContext.getResources());
         // THEN real size matches app bounds.
-        verifyRealSizeMatchesApp(display, sAppBoundsLandscape);
+        verifyRealSizeMatchesBounds(display, sAppBoundsLandscape);
+    }
+
+    @Test
+    public void testGetRealSize_resourcesLandscapeSandboxed_matchesDisplayAreaSandboxBounds() {
+        // GIVEN display is rotated.
+        setDisplayInfoLandscape(mDisplayInfo);
+        // GIVEN max bounds reflect DisplayArea size, which is the same size as the display.
+        setMaxBoundsSandboxed(mApplicationContext.getResources(), sDeviceBoundsLandscape);
+        // GIVEN app bounds do not stretch to include the full DisplayArea.
+        mApplicationContext.getResources().getConfiguration().windowConfiguration
+                .setAppBounds(buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH - 10));
+        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+                mApplicationContext.getResources());
+        // THEN real metrics matches max bounds for the DisplayArea.
+        verifyRealSizeMatchesBounds(display, sDeviceBoundsLandscape);
     }
 
     @Test
@@ -396,29 +429,57 @@
     }
 
     @Test
-    public void testGetRealMetrics_resourcesPortraitSandboxed_matchesSandboxBounds() {
+    public void testGetRealMetrics_resourcesPortraitSandboxed_matchesAppSandboxBounds() {
         // GIVEN display is not rotated.
         setDisplayInfoPortrait(mDisplayInfo);
         // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsPortrait);
+        setMaxBoundsSandboxed(mApplicationContext.getResources(), sAppBoundsPortrait);
         final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
                 mApplicationContext.getResources());
         // THEN real metrics matches app bounds.
-        verifyRealMetricsMatchesApp(display, sAppBoundsPortrait);
+        verifyRealMetricsMatchesBounds(display, sAppBoundsPortrait);
     }
 
     @Test
-    public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesSandboxBounds() {
+    public void testGetRealMetrics_resourcesPortraitSandboxed_matchesDisplayAreaSandboxBounds() {
+        // GIVEN display is not rotated.
+        setDisplayInfoPortrait(mDisplayInfo);
+        // GIVEN max bounds reflect DisplayArea size, which is the same size as the display.
+        setMaxBoundsSandboxed(mApplicationContext.getResources(), sDeviceBoundsPortrait);
+        // GIVEN app bounds do not stretch to include the full DisplayArea.
+        mApplicationContext.getResources().getConfiguration().windowConfiguration
+                .setAppBounds(buildAppBounds(LOGICAL_WIDTH, LOGICAL_HEIGHT - 10));
+        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+                mApplicationContext.getResources());
+        // THEN real metrics matches max bounds for the DisplayArea.
+        verifyRealMetricsMatchesBounds(display, sDeviceBoundsPortrait);
+    }
+
+    @Test
+    public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesAppSandboxBounds() {
         // GIVEN display is rotated.
         setDisplayInfoLandscape(mDisplayInfo);
         // GIVEN app is letterboxed.
-        setMaxBoundsSandboxedToMatchAppBounds(mApplicationContext.getResources(),
-                sAppBoundsLandscape);
+        setMaxBoundsSandboxed(mApplicationContext.getResources(), sAppBoundsLandscape);
         final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
                 mApplicationContext.getResources());
         // THEN real metrics matches app bounds.
-        verifyRealMetricsMatchesApp(display, sAppBoundsLandscape);
+        verifyRealMetricsMatchesBounds(display, sAppBoundsLandscape);
+    }
+
+    @Test
+    public void testGetRealMetrics_resourcesLandscapeSandboxed_matchesDisplayAreaSandboxBounds() {
+        // GIVEN display is rotated.
+        setDisplayInfoLandscape(mDisplayInfo);
+        // GIVEN max bounds reflect DisplayArea size, which is the same size as the display.
+        setMaxBoundsSandboxed(mApplicationContext.getResources(), sDeviceBoundsLandscape);
+        // GIVEN app bounds do not stretch to include the full DisplayArea.
+        mApplicationContext.getResources().getConfiguration().windowConfiguration
+                .setAppBounds(buildAppBounds(LOGICAL_HEIGHT, LOGICAL_WIDTH - 10));
+        final Display display = new Display(mDisplayManagerGlobal, DEFAULT_DISPLAY, mDisplayInfo,
+                mApplicationContext.getResources());
+        // THEN real metrics matches max bounds for the DisplayArea.
+        verifyRealMetricsMatchesBounds(display, sDeviceBoundsLandscape);
     }
 
     // Given rotated display dimensions, calculate the letterboxed app bounds.
@@ -450,8 +511,8 @@
      * Set max bounds to be sandboxed to the app bounds, indicating the app is in
      * size compat mode or letterbox.
      */
-    private static void setMaxBoundsSandboxedToMatchAppBounds(Resources resources, Rect appBounds) {
-        resources.getConfiguration().windowConfiguration.setMaxBounds(appBounds);
+    private static void setMaxBoundsSandboxed(Resources resources, Rect bounds) {
+        resources.getConfiguration().windowConfiguration.setMaxBounds(bounds);
     }
 
     /**
@@ -492,17 +553,17 @@
         assertThat(metrics.heightPixels).isEqualTo(LOGICAL_HEIGHT);
     }
 
-    private static void verifyRealSizeMatchesApp(Display display, Rect appBounds) {
+    private static void verifyRealSizeMatchesBounds(Display display, Rect bounds) {
         Point size = new Point();
         display.getRealSize(size);
-        assertThat(size).isEqualTo(new Point(appBounds.width(), appBounds.height()));
+        assertThat(size).isEqualTo(new Point(bounds.width(), bounds.height()));
     }
 
-    private static void verifyRealMetricsMatchesApp(Display display, Rect appBounds) {
+    private static void verifyRealMetricsMatchesBounds(Display display, Rect bounds) {
         DisplayMetrics metrics = new DisplayMetrics();
         display.getRealMetrics(metrics);
-        assertThat(metrics.widthPixels).isEqualTo(appBounds.width());
-        assertThat(metrics.heightPixels).isEqualTo(appBounds.height());
+        assertThat(metrics.widthPixels).isEqualTo(bounds.width());
+        assertThat(metrics.heightPixels).isEqualTo(bounds.height());
     }
 
     private static FixedRotationAdjustments setOverrideFixedRotationAdjustments(
diff --git a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
index c01bb75..e41805d 100644
--- a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
+++ b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
@@ -22,7 +22,6 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.os.PersistableBundle;
 import android.os.RemoteException;
@@ -32,6 +31,7 @@
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 import java.util.concurrent.Executor;
 
@@ -42,51 +42,23 @@
 @RunWith(AndroidJUnit4.class)
 public class RangingManagerTest {
 
-    private static final IUwbAdapter ADAPTER = mock(IUwbAdapter.class);
     private static final Executor EXECUTOR = UwbTestUtils.getExecutor();
     private static final PersistableBundle PARAMS = new PersistableBundle();
     private static final @RangingChangeReason int REASON = RangingChangeReason.UNKNOWN;
 
     @Test
     public void testOpenSession_OpenRangingInvoked() throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
+        RangingManager rangingManager = new RangingManager(adapter);
         RangingSession.Callback callback = mock(RangingSession.Callback.class);
         rangingManager.openSession(PARAMS, EXECUTOR, callback);
-        verify(ADAPTER, times(1)).openRanging(eq(rangingManager), eq(PARAMS));
-    }
-
-    @Test
-    public void testOpenSession_ErrorIfSameSessionHandleReturned() throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
-        RangingSession.Callback callback = mock(RangingSession.Callback.class);
-        SessionHandle handle = new SessionHandle(1);
-        when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
-
-        rangingManager.openSession(PARAMS, EXECUTOR, callback);
-
-        // Calling openSession will cause the same session handle to be returned. The onClosed
-        // callback should be invoked
-        RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
-        rangingManager.openSession(PARAMS, EXECUTOR, callback2);
-        verify(callback, times(0)).onClosed(anyInt(), any());
-        verify(callback2, times(1)).onClosed(anyInt(), any());
-    }
-
-    @Test
-    public void testOnRangingOpened_ValidSessionHandle() throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
-        RangingSession.Callback callback = mock(RangingSession.Callback.class);
-        SessionHandle handle = new SessionHandle(1);
-        when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
-
-        rangingManager.openSession(PARAMS, EXECUTOR, callback);
-        rangingManager.onRangingOpened(handle);
-        verify(callback, times(1)).onOpened(any());
+        verify(adapter, times(1)).openRanging(any(), eq(rangingManager), eq(PARAMS));
     }
 
     @Test
     public void testOnRangingOpened_InvalidSessionHandle() throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
+        RangingManager rangingManager = new RangingManager(adapter);
         RangingSession.Callback callback = mock(RangingSession.Callback.class);
 
         rangingManager.onRangingOpened(new SessionHandle(2));
@@ -95,18 +67,20 @@
 
     @Test
     public void testOnRangingOpened_MultipleSessionsRegistered() throws RemoteException {
-        SessionHandle sessionHandle1 = new SessionHandle(1);
-        SessionHandle sessionHandle2 = new SessionHandle(2);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
         RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
         RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
+        ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+                ArgumentCaptor.forClass(SessionHandle.class);
 
-        when(ADAPTER.openRanging(any(), any()))
-                .thenReturn(sessionHandle1)
-                .thenReturn(sessionHandle2);
-
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        RangingManager rangingManager = new RangingManager(adapter);
         rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
+
         rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+        verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
 
         rangingManager.onRangingOpened(sessionHandle1);
         verify(callback1, times(1)).onOpened(any());
@@ -119,12 +93,17 @@
 
     @Test
     public void testCorrectCallbackInvoked() throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
+        RangingManager rangingManager = new RangingManager(adapter);
         RangingSession.Callback callback = mock(RangingSession.Callback.class);
-        SessionHandle handle = new SessionHandle(1);
-        when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
+
+        ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+                ArgumentCaptor.forClass(SessionHandle.class);
 
         rangingManager.openSession(PARAMS, EXECUTOR, callback);
+        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle handle = sessionHandleCaptor.getValue();
+
         rangingManager.onRangingOpened(handle);
         verify(callback, times(1)).onOpened(any());
 
@@ -156,20 +135,23 @@
 
     @Test
     public void testOnRangingClosed_MultipleSessionsRegistered() throws RemoteException {
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
         // Verify that if multiple sessions are registered, only the session that is
         // requested to close receives the associated callbacks
-        SessionHandle sessionHandle1 = new SessionHandle(1);
-        SessionHandle sessionHandle2 = new SessionHandle(2);
         RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
         RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
 
-        when(ADAPTER.openRanging(any(), any()))
-                .thenReturn(sessionHandle1)
-                .thenReturn(sessionHandle2);
+        RangingManager rangingManager = new RangingManager(adapter);
+        ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+                ArgumentCaptor.forClass(SessionHandle.class);
 
-        RangingManager rangingManager = new RangingManager(ADAPTER);
         rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
+
         rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+        verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
 
         rangingManager.onRangingClosed(sessionHandle1, REASON, PARAMS);
         verify(callback1, times(1)).onClosed(anyInt(), any());
@@ -182,19 +164,22 @@
 
     @Test
     public void testOnRangingReport_MultipleSessionsRegistered() throws RemoteException {
-        SessionHandle sessionHandle1 = new SessionHandle(1);
-        SessionHandle sessionHandle2 = new SessionHandle(2);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
         RangingSession.Callback callback1 = mock(RangingSession.Callback.class);
         RangingSession.Callback callback2 = mock(RangingSession.Callback.class);
 
-        when(ADAPTER.openRanging(any(), any()))
-                .thenReturn(sessionHandle1)
-                .thenReturn(sessionHandle2);
+        ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+                ArgumentCaptor.forClass(SessionHandle.class);
 
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        RangingManager rangingManager = new RangingManager(adapter);
         rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
+
         rangingManager.onRangingStarted(sessionHandle1, PARAMS);
         rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+        verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
         rangingManager.onRangingStarted(sessionHandle2, PARAMS);
 
         rangingManager.onRangingResult(sessionHandle1, UwbTestUtils.getRangingReports(1));
@@ -232,17 +217,24 @@
 
     private void runReason(@RangingChangeReason int reasonIn,
             @RangingSession.Callback.Reason int reasonOut) throws RemoteException {
-        RangingManager rangingManager = new RangingManager(ADAPTER);
+        IUwbAdapter adapter = mock(IUwbAdapter.class);
+        RangingManager rangingManager = new RangingManager(adapter);
         RangingSession.Callback callback = mock(RangingSession.Callback.class);
-        SessionHandle handle = new SessionHandle(1);
-        when(ADAPTER.openRanging(any(), any())).thenReturn(handle);
+
+        ArgumentCaptor<SessionHandle> sessionHandleCaptor =
+                ArgumentCaptor.forClass(SessionHandle.class);
+
         rangingManager.openSession(PARAMS, EXECUTOR, callback);
+        verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        SessionHandle handle = sessionHandleCaptor.getValue();
 
         rangingManager.onRangingOpenFailed(handle, reasonIn, PARAMS);
         verify(callback, times(1)).onOpenFailed(eq(reasonOut), eq(PARAMS));
 
         // Open a new session
         rangingManager.openSession(PARAMS, EXECUTOR, callback);
+        verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
+        handle = sessionHandleCaptor.getValue();
         rangingManager.onRangingOpened(handle);
 
         rangingManager.onRangingStartFailed(handle, reasonIn, PARAMS);
diff --git a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
index 8e7f7c56..75c6924 100644
--- a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
+++ b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
@@ -16,34 +16,23 @@
 
 package android.uwb;
 
-import android.content.Context;
-import android.content.pm.PackageManager;
 import android.os.SystemClock;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.concurrent.Executor;
 
 public class UwbTestUtils {
     private UwbTestUtils() {}
 
-    public static boolean isUwbSupported(Context context) {
-        PackageManager packageManager = context.getPackageManager();
-        return packageManager.hasSystemFeature(PackageManager.FEATURE_UWB);
-    }
-
     public static AngleMeasurement getAngleMeasurement() {
-        return new AngleMeasurement.Builder()
-                .setRadians(getDoubleInRange(-Math.PI, Math.PI))
-                .setErrorRadians(getDoubleInRange(0, Math.PI))
-                .setConfidenceLevel(getDoubleInRange(0, 1))
-                .build();
+        return new AngleMeasurement(
+                getDoubleInRange(-Math.PI, Math.PI),
+                getDoubleInRange(0, Math.PI),
+                getDoubleInRange(0, 1));
     }
 
     public static AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() {
-        return new AngleOfArrivalMeasurement.Builder()
+        return new AngleOfArrivalMeasurement.Builder(getAngleMeasurement())
                 .setAltitude(getAngleMeasurement())
-                .setAzimuth(getAngleMeasurement())
                 .build();
     }
 
@@ -69,14 +58,6 @@
                 .build();
     }
 
-    public static List<RangingMeasurement> getRangingMeasurements(int num) {
-        List<RangingMeasurement> result = new ArrayList<>();
-        for (int i = 0; i < num; i++) {
-            result.add(getRangingMeasurement());
-        }
-        return result;
-    }
-
     public static RangingReport getRangingReports(int numMeasurements) {
         RangingReport.Builder builder = new RangingReport.Builder();
         for (int i = 0; i < numMeasurements; i++) {
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 77a38a9..b3a180d 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -154,6 +154,7 @@
     <assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="media" />
     <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="media" />
     <assign-permission name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" uid="media" />
+    <assign-permission name="android.permission.REGISTER_STATS_PULL_ATOM" uid="media" />
 
     <assign-permission name="android.permission.INTERNET" uid="media" />
 
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index c49fe85..a7b6636 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -488,6 +488,8 @@
         <permission name="android.permission.MANAGE_UI_TRANSLATION" />
         <!-- Permission required for CTS test - ClipboardManagerTest -->
         <permission name="android.permission.SET_CLIP_SOURCE" />
+        <!-- Permission required for CTS test - FontManagerTest -->
+        <permission name="android.permission.UPDATE_FONTS" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index c807882..2a6bbf3 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -25,6 +25,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.annotation.UiThread;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
@@ -169,6 +170,21 @@
             Collections.emptyMap();
 
     /**
+     * Returns the shared memory that used for creating Typefaces.
+     *
+     * @return A SharedMemory used for creating Typeface. Maybe null if the lazy initialization is
+     *         disabled or inside SystemServer or Zygote.
+     * @hide
+     */
+    @TestApi
+    public static @Nullable SharedMemory getSystemFontMapSharedMemory() {
+        if (ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
+            Objects.requireNonNull(sSystemFontMapSharedMemory);
+        }
+        return sSystemFontMapSharedMemory;
+    }
+
+    /**
      * @hide
      */
     @UnsupportedAppUsage
@@ -1196,8 +1212,13 @@
         }
     }
 
-    /** @hide */
-    public static SharedMemory serializeFontMap(Map<String, Typeface> fontMap)
+    /**
+     * Create a serialized system font mappings.
+     *
+     * @hide
+     */
+    @TestApi
+    public static @NonNull SharedMemory serializeFontMap(@NonNull Map<String, Typeface> fontMap)
             throws IOException, ErrnoException {
         long[] nativePtrs = new long[fontMap.size()];
         // The name table will not be large, so let's create a byte array in memory.
@@ -1229,9 +1250,14 @@
     }
 
     // buffer's byte order should be BIG_ENDIAN.
-    /** @hide */
-    @VisibleForTesting
-    public static Map<String, Typeface> deserializeFontMap(ByteBuffer buffer) throws IOException {
+    /**
+     * Deserialize the font mapping from the serialized byte buffer.
+     *
+     * @hide
+     */
+    @TestApi
+    public static @NonNull Map<String, Typeface> deserializeFontMap(@NonNull ByteBuffer buffer)
+            throws IOException {
         Map<String, Typeface> fontMap = new ArrayMap<>();
         int typefacesBytesCount = buffer.getInt();
         long[] nativePtrs = nativeReadTypefaces(buffer.slice());
diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java
index 2549277..657a32c 100644
--- a/graphics/java/android/graphics/drawable/RippleShader.java
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -61,53 +61,6 @@
             + "  return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);\n"
             + "}\n"
             + "\n"
-            + "float softRing(vec2 uv, vec2 xy, float radius, float blur) {\n"
-            + "  float thickness = 0.4;\n"
-            + "  float circle_outer = softCircle(uv, xy, radius + thickness * 0.5, blur);\n"
-            + "  float circle_inner = softCircle(uv, xy, radius - thickness * 0.5, blur);\n"
-            + "  return circle_outer - circle_inner;\n"
-            + "}\n"
-            + "\n"
-            + "struct Viewport {\n"
-            + "  float aspect;\n"
-            + "  vec2 uv;\n"
-            + "  vec2 resolution_pixels;\n"
-            + "};\n"
-            + "\n"
-            + "Viewport getViewport(vec2 frag_coord, vec2 resolution_pixels) {\n"
-            + "  Viewport v;\n"
-            + "  v.aspect = resolution_pixels.y / resolution_pixels.x;\n"
-            + "  v.uv = frag_coord / resolution_pixels;\n"
-            + "  v.uv.y = (1.0 - v.uv.y) * v.aspect;\n"
-            + "  v.resolution_pixels = resolution_pixels;\n"
-            + "  return v;\n"
-            + "}\n"
-            + "\n"
-            + "vec2 getTouch(vec2 touch_position_pixels, Viewport viewport) {\n"
-            + "  vec2 touch = touch_position_pixels / viewport.resolution_pixels;\n"
-            + "  touch.y *= viewport.aspect;\n"
-            + "  return touch;\n"
-            + "}\n"
-            + "\n"
-            + "struct Wave {\n"
-            + "  float ring;\n"
-            + "  float circle;\n"
-            + "};\n"
-            + "\n"
-            + "Wave getWave(Viewport viewport, vec2 touch, float progress) {\n"
-            + "  float fade = pow((clamp(progress, 0.8, 1.0)), 8.);\n"
-            + "  Wave w;\n"
-            + "  w.ring = max(softRing(viewport.uv, touch, progress, 0.45) - fade, 0.);\n"
-            + "  w.circle = softCircle(viewport.uv, touch, 2.0 * progress, 0.2) - progress;\n"
-            + "  return w;\n"
-            + "}\n"
-            + "\n"
-            + "vec4 getRipple(vec4 color, float loudness, float sparkle, Wave wave) {\n"
-            + "  float alpha = wave.ring * sparkle * loudness\n"
-            + "        + wave.circle * color.a;\n"
-            + "  return vec4(color.rgb, saturate(alpha));\n"
-            + "}\n"
-            + "\n"
             + "float getRingMask(vec2 frag, vec2 center, float r, float progress) {\n"
             + "      float dist = distance(frag, center);\n"
             + "      float expansion = r * .6;\n"
@@ -126,19 +79,15 @@
             + "    float fadeIn = subProgress(0., 0.175, in_progress);\n"
             + "    float fadeOutNoise = subProgress(0.375, 1., in_progress);\n"
             + "    float fadeOutRipple = subProgress(0.375, 0.75, in_progress);\n"
-            + "    Viewport vp = getViewport(p, in_resolution);\n"
-            + "    vec2 touch = getTouch(in_origin, vp);\n"
-            + "    Wave w = getWave(vp, touch, in_progress * 0.25);\n"
             + "    float ring = getRingMask(p, in_origin, in_maxRadius, fadeIn);\n"
             + "    float alpha = min(fadeIn, 1. - fadeOutNoise);\n"
             + "    float sparkle = sparkles(p, in_progress * 0.25 + in_secondsOffset)\n"
             + "        * ring * alpha;\n"
-            + "    vec4 r = getRipple(in_color, 1., sparkle, w);\n"
             + "    float fade = min(fadeIn, 1.-fadeOutRipple);\n"
-            + "    vec4 circle = vec4(in_color.rgb, softCircle(p, in_origin, in_maxRadius "
-            + "      * fadeIn, 0.2) * fade * in_color.a);\n"
+            + "    vec4 circle = in_color * (softCircle(p, in_origin, in_maxRadius "
+            + "      * fadeIn, 0.2) * fade);\n"
             + "    float mask = in_hasMask == 1. ? sample(in_shader).a > 0. ? 1. : 0. : 1.;\n"
-            + "    return mix(circle, vec4(1.), sparkle * mask);\n"
+            + "    return mix(circle, vec4(sparkle), sparkle) * mask;\n"
             + "}";
     private static final String SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN;
 
diff --git a/graphics/java/android/graphics/fonts/FontFileUtil.java b/graphics/java/android/graphics/fonts/FontFileUtil.java
index af49619..917eef2 100644
--- a/graphics/java/android/graphics/fonts/FontFileUtil.java
+++ b/graphics/java/android/graphics/fonts/FontFileUtil.java
@@ -183,6 +183,23 @@
         return nIsPostScriptType1Font(buffer, index);
     }
 
+    /**
+     * Analyze the file content and returns 1 if the font file is an OpenType collection file, 0 if
+     * the font file is a OpenType font file, -1 otherwise.
+     */
+    public static int isCollectionFont(@NonNull ByteBuffer buffer) {
+        ByteBuffer copied = buffer.slice();
+        copied.order(ByteOrder.BIG_ENDIAN);
+        int magicNumber = copied.getInt(0);
+        if (magicNumber == TTC_TAG) {
+            return 1;
+        } else if (magicNumber == SFNT_VERSION_1 || magicNumber == SFNT_VERSION_OTTO) {
+            return 0;
+        } else {
+            return -1;
+        }
+    }
+
     @FastNative
     private static native long nGetFontRevision(@NonNull ByteBuffer buffer,
             @IntRange(from = 0) int index);
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index 5501569..35b1c16 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -121,4 +121,22 @@
             return SYSTEM_ERROR;
         }
     }
+
+    /**
+     * Queries user state from Keystore 2.0.
+     *
+     * @param userId - Android user id of the user.
+     * @return UserState enum variant as integer if successful or an error
+     */
+    public static int getState(int userId) {
+        try {
+            return getService().getState(userId);
+        } catch (ServiceSpecificException e) {
+            Log.e(TAG, "getState failed", e);
+            return e.errorCode;
+        } catch (Exception e) {
+            Log.e(TAG, "Can not connect to keystore", e);
+            return SYSTEM_ERROR;
+        }
+    }
 }
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 93658e6..937f01c 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -43,6 +43,7 @@
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeystoreResponse;
 import android.security.keystore.UserNotAuthenticatedException;
+import android.security.maintenance.UserState;
 import android.system.keystore2.Domain;
 import android.util.Log;
 
@@ -196,6 +197,19 @@
     public State state(int userId) {
         final int ret;
         try {
+            if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) {
+                int userState = AndroidKeyStoreMaintenance.getState(userId);
+                switch (userState) {
+                    case UserState.UNINITIALIZED:
+                        return KeyStore.State.UNINITIALIZED;
+                    case UserState.LSKF_UNLOCKED:
+                        return KeyStore.State.UNLOCKED;
+                    case UserState.LSKF_LOCKED:
+                        return KeyStore.State.LOCKED;
+                    default:
+                        throw new AssertionError(KeyStore.VALUE_CORRUPTED);
+                }
+            }
             ret = mBinder.getState(userId);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index c0bc73d..d2b3cf6 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -18,4 +18,5 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.wm.shell">
     <uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
+    <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
 </manifest>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 2419865..c2f591b 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -51,4 +51,10 @@
 
     <!-- maximum animation duration for the icon when entering the starting window -->
     <integer name="max_starting_window_intro_icon_anim_duration">1000</integer>
+
+    <!-- Animation duration when exit starting window: icon going away -->
+    <integer name="starting_window_icon_exit_anim_duration">166</integer>
+
+    <!-- Animation duration when exit starting window: reveal app -->
+    <integer name="starting_window_app_reveal_anim_duration">333</integer>
 </resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 75bed37..3ced8d3 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -188,4 +188,13 @@
 
     <!-- The height of the brand image on staring surface. -->
     <dimen name="starting_surface_brand_image_height">80dp</dimen>
+
+    <!-- The length of the shift of main window when exit starting window. -->
+    <dimen name="starting_surface_exit_animation_window_shift_length">20dp</dimen>
+
+    <!-- The distance of the shift icon when normal exit starting window. -->
+    <dimen name="starting_surface_normal_exit_icon_distance">120dp</dimen>
+
+    <!-- The distance of the shift icon when early exit starting window. -->
+    <dimen name="starting_surface_early_exit_icon_distance">32dp</dimen>
 </resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index cb04bd7..fcb53cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -31,6 +31,7 @@
 import android.app.TaskInfo;
 import android.content.Context;
 import android.content.LocusId;
+import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
 import android.util.ArrayMap;
@@ -307,9 +308,10 @@
     }
 
     @Override
-    public void removeStartingWindow(int taskId) {
+    public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+            boolean playRevealAnimation) {
         if (mStartingSurface != null) {
-            mStartingSurface.removeStartingWindow(taskId);
+            mStartingSurface.removeStartingWindow(taskId, leash, frame, playRevealAnimation);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 46884fe..7d65a08 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -83,6 +83,7 @@
     private boolean mIsInitialized;
     private Listener mListener;
     private Executor mListenerExecutor;
+    private Rect mObscuredTouchRect;
 
     private final Rect mTmpRect = new Rect();
     private final Rect mTmpRootRect = new Rect();
@@ -161,6 +162,15 @@
     }
 
     /**
+     * Indicates a region of the view that is not touchable.
+     *
+     * @param obscuredRect the obscured region of the view.
+     */
+    public void setObscuredTouchRect(Rect obscuredRect) {
+        mObscuredTouchRect = obscuredRect;
+    }
+
+    /**
      * Call when view position or size has changed. Do not call when animating.
      */
     public void onLocationChanged() {
@@ -384,6 +394,10 @@
         mTmpRect.set(mTmpLocation[0], mTmpLocation[1],
                 mTmpLocation[0] + getWidth(), mTmpLocation[1] + getHeight());
         inoutInfo.touchableRegion.op(mTmpRect, Region.Op.DIFFERENCE);
+
+        if (mObscuredTouchRect != null) {
+            inoutInfo.touchableRegion.union(mObscuredTouchRect);
+        }
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 64a44ca..16ede73 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -41,7 +41,6 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.graphics.Region;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.util.Log;
@@ -1046,6 +1045,7 @@
         }
     };
 
+    // TODO: Create ManageMenuView and move setup / animations there
     private void setUpManageMenu() {
         if (mManageMenu != null) {
             removeView(mManageMenu);
@@ -2146,50 +2146,6 @@
     }
 
     /**
-     * This method is called by {@link android.app.ActivityView} because the BubbleStackView has a
-     * higher Z-index than the ActivityView (so that dragged-out bubbles are visible over the AV).
-     * ActivityView is asking BubbleStackView to subtract the stack's bounds from the provided
-     * touchable region, so that the ActivityView doesn't consume events meant for the stack. Due to
-     * the special nature of ActivityView, it does not respect the standard
-     * {@link #dispatchTouchEvent} and {@link #onInterceptTouchEvent} methods typically used for
-     * this purpose.
-     *
-     * BubbleStackView is MATCH_PARENT, so that bubbles can be positioned via their translation
-     * properties for performance reasons. This means that the default implementation of this method
-     * subtracts the entirety of the screen from the ActivityView's touchable region, resulting in
-     * it not receiving any touch events. This was previously addressed by returning false in the
-     * stack's {@link View#canReceivePointerEvents()} method, but this precluded the use of any
-     * touch handlers in the stack or its child views.
-     *
-     * To support touch handlers, we're overriding this method to leave the ActivityView's touchable
-     * region alone. The only touchable part of the stack that can ever overlap the AV is a
-     * dragged-out bubble that is animating back into the row of bubbles. It's not worth continually
-     * updating the touchable region to allow users to grab a bubble while it completes its ~50ms
-     * animation back to the bubble row.
-     *
-     * NOTE: Any future additions to the stack that obscure the ActivityView region will need their
-     * bounds subtracted here in order to receive touch events.
-     */
-    @Override
-    public void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
-        // If the notification shade is expanded, or the manage menu is open, or we are showing
-        // manage bubbles user education, we shouldn't let the ActivityView steal any touch events
-        // from any location.
-        if (!mIsExpanded
-                || mShowingManage
-                || (mManageEduView != null
-                    && mManageEduView.getVisibility() == VISIBLE)) {
-            touchableRegion.setEmpty();
-        }
-    }
-
-    /**
-     * If you're here because you're not receiving touch events on a view that is a descendant of
-     * BubbleStackView, and you think BSV is intercepting them - it's not! You need to subtract the
-     * bounds of the view in question in {@link #subtractObscuredTouchableRegion}. The ActivityView
-     * consumes all touch events within its bounds, even for views like the BubbleStackView that are
-     * above it. It ignores typical view touch handling methods like this one and
-     * dispatchTouchEvent.
      */
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
@@ -2539,6 +2495,11 @@
         }
 
         mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
+        if (mExpandedBubble.getExpandedView().getTaskView() != null) {
+            mExpandedBubble.getExpandedView().getTaskView().setObscuredTouchRect(mShowingManage
+                    ? new Rect(0, 0, getWidth(), getHeight())
+                    : null);
+        }
 
         final boolean isLtr =
                 getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_LTR;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java
index 7ce9014..57a9dd2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java
@@ -143,14 +143,14 @@
     @ImeAnimationFlags
     public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
             boolean imeShouldShow, boolean imeIsFloating, SurfaceControl.Transaction t) {
-        mHiddenTop = hiddenTop;
-        mShownTop = shownTop;
-        mTargetShown = imeShouldShow;
         if (!isDividerVisible()) {
             return 0;
         }
-        final boolean splitIsVisible = !getView().isHidden();
+        mHiddenTop = hiddenTop;
+        mShownTop = shownTop;
+        mTargetShown = imeShouldShow;
         mSecondaryHasFocus = getSecondaryHasFocus(displayId);
+        final boolean splitIsVisible = !getView().isHidden();
         final boolean targetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus
                 && !imeIsFloating && !getLayout().mDisplayLayout.isLandscape()
                 && !mSplits.mSplitScreenController.isMinimized();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
new file mode 100644
index 0000000..5bc2afd
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.startingsurface;
+
+import static android.view.View.GONE;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.graphics.BlendMode;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+import android.view.animation.Transformation;
+import android.view.animation.TranslateYAnimation;
+import android.window.SplashScreenView;
+
+import com.android.wm.shell.common.TransactionPool;
+
+/**
+ * Default animation for exiting the splash screen window.
+ * @hide
+ */
+public class SplashScreenExitAnimation implements Animator.AnimatorListener {
+    private static final boolean DEBUG_EXIT_ANIMATION = false;
+    private static final boolean DEBUG_EXIT_ANIMATION_BLEND = false;
+    private static final String TAG = StartingSurfaceDrawer.TAG;
+
+    private static final Interpolator ICON_EXIT_INTERPOLATOR = new PathInterpolator(1f, 0f, 1f, 1f);
+    private static final Interpolator APP_EXIT_INTERPOLATOR = new PathInterpolator(0f, 0f, 0f, 1f);
+
+    private static final int EXTRA_REVEAL_DELAY = 133;
+    private final Matrix mTmpTransform = new Matrix();
+    private final float[] mTmpFloat9 = new float[9];
+    private SurfaceControl mFirstWindowSurface;
+    private final Rect mFirstWindowFrame = new Rect();
+    private final SplashScreenView mSplashScreenView;
+    private final int mMainWindowShiftLength;
+    private final int mIconShiftLength;
+    private final int mAppDuration;
+    private final int mIconDuration;
+    private final TransactionPool mTransactionPool;
+
+    private ValueAnimator mMainAnimator;
+    private Animation mShiftUpAnimation;
+    private AnimationSet mIconAnimationSet;
+    private Runnable mFinishCallback;
+
+    SplashScreenExitAnimation(SplashScreenView view, SurfaceControl leash, Rect frame,
+            int appDuration, int iconDuration, int mainWindowShiftLength, int iconShiftLength,
+            TransactionPool pool, Runnable handleFinish) {
+        mSplashScreenView = view;
+        mFirstWindowSurface = leash;
+        if (frame != null) {
+            mFirstWindowFrame.set(frame);
+        }
+        mAppDuration = appDuration;
+        mIconDuration = iconDuration;
+        mMainWindowShiftLength = mainWindowShiftLength;
+        mIconShiftLength = iconShiftLength;
+        mFinishCallback = handleFinish;
+        mTransactionPool = pool;
+    }
+
+    void prepareAnimations() {
+        prepareRevealAnimation();
+        prepareShiftAnimation();
+    }
+
+    void startAnimations() {
+        if (mIconAnimationSet != null) {
+            mIconAnimationSet.start();
+        }
+        if (mMainAnimator != null) {
+            mMainAnimator.start();
+        }
+        if (mShiftUpAnimation != null) {
+            mShiftUpAnimation.start();
+        }
+    }
+
+    // reveal splash screen, shift up main window
+    private void prepareRevealAnimation() {
+        // splash screen
+        mMainAnimator = ValueAnimator.ofFloat(0f, 1f);
+        mMainAnimator.setDuration(mAppDuration);
+        mMainAnimator.setInterpolator(APP_EXIT_INTERPOLATOR);
+        mMainAnimator.addListener(this);
+
+        final int startDelay = mIconDuration + EXTRA_REVEAL_DELAY;
+        final float transparentRatio = 0.95f;
+        final int globalHeight = mSplashScreenView.getHeight();
+        final int verticalCircleCenter = 0;
+        final int finalVerticalLength = globalHeight - verticalCircleCenter;
+        final int halfWidth = mSplashScreenView.getWidth() / 2;
+        final int endRadius = (int) (0.5 + (1f / transparentRatio * (int)
+                Math.sqrt(finalVerticalLength * finalVerticalLength + halfWidth * halfWidth)));
+        final RadialVanishAnimation radialVanishAnimation = new RadialVanishAnimation(
+                mSplashScreenView, mMainAnimator);
+        radialVanishAnimation.setCircleCenter(halfWidth, verticalCircleCenter);
+        radialVanishAnimation.setRadius(0/* initRadius */, endRadius);
+        final int[] colors = {Color.TRANSPARENT, Color.TRANSPARENT, Color.WHITE};
+        final float[] stops = {0f, transparentRatio, 1f};
+        radialVanishAnimation.setRadialPaintParam(colors, stops);
+        radialVanishAnimation.setReady();
+        mMainAnimator.setStartDelay(startDelay);
+
+        if (mFirstWindowSurface != null) {
+            // shift up main window
+            View occludeHoleView = new View(mSplashScreenView.getContext());
+            if (DEBUG_EXIT_ANIMATION_BLEND) {
+                occludeHoleView.setBackgroundColor(Color.BLUE);
+            } else {
+                occludeHoleView.setBackgroundColor(mSplashScreenView.getInitBackgroundColor());
+            }
+            final ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
+                    WindowManager.LayoutParams.MATCH_PARENT, mMainWindowShiftLength);
+            mSplashScreenView.addView(occludeHoleView, params);
+
+            mShiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength);
+            mShiftUpAnimation.setDuration(mAppDuration);
+            mShiftUpAnimation.setInterpolator(APP_EXIT_INTERPOLATOR);
+            mShiftUpAnimation.setStartOffset(startDelay);
+
+            occludeHoleView.setAnimation(mShiftUpAnimation);
+        }
+    }
+
+    // shift down icon and branding view
+    private void prepareShiftAnimation() {
+        final View iconView = mSplashScreenView.getIconView();
+        if (iconView == null) {
+            return;
+        }
+        if (mIconShiftLength > 0) {
+            mIconAnimationSet = new AnimationSet(true /* shareInterpolator */);
+            if (DEBUG_EXIT_ANIMATION) {
+                Slog.v(TAG, "first exit animation, shift length: " + mIconShiftLength);
+            }
+            mIconAnimationSet.addAnimation(new TranslateYAnimation(0, mIconShiftLength));
+            mIconAnimationSet.addAnimation(new AlphaAnimation(1, 0));
+            mIconAnimationSet.setAnimationListener(new Animation.AnimationListener() {
+                @Override
+                public void onAnimationStart(Animation animation) {
+
+                }
+
+                @Override
+                public void onAnimationEnd(Animation animation) {
+                    if (DEBUG_EXIT_ANIMATION) {
+                        Slog.v(TAG, "first exit animation finished");
+                    }
+                    iconView.post(() -> iconView.setVisibility(GONE));
+                }
+
+                @Override
+                public void onAnimationRepeat(Animation animation) {
+                    // ignore
+                }
+            });
+            mIconAnimationSet.setDuration(mIconDuration);
+            mIconAnimationSet.setInterpolator(ICON_EXIT_INTERPOLATOR);
+            iconView.setAnimation(mIconAnimationSet);
+            final View brandingView = mSplashScreenView.getBrandingView();
+            if (brandingView != null) {
+                brandingView.setAnimation(mIconAnimationSet);
+            }
+        }
+    }
+
+    private static class RadialVanishAnimation extends View {
+        private SplashScreenView mView;
+        private int mInitRadius;
+        private int mFinishRadius;
+        private boolean mReady;
+
+        private final Point mCircleCenter = new Point();
+        private final Matrix mVanishMatrix = new Matrix();
+        private final Paint mVanishPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+        RadialVanishAnimation(SplashScreenView target, ValueAnimator animator) {
+            super(target.getContext());
+            mView = target;
+            animator.addUpdateListener((animation) -> {
+                if (mVanishPaint.getShader() == null) {
+                    return;
+                }
+                final float value = (float) animation.getAnimatedValue();
+                final float scale = (mFinishRadius - mInitRadius) * value + mInitRadius;
+                mVanishMatrix.setScale(scale, scale);
+                mVanishMatrix.postTranslate(mCircleCenter.x, mCircleCenter.y);
+                mVanishPaint.getShader().setLocalMatrix(mVanishMatrix);
+                mView.postInvalidate();
+            });
+            mView.addView(this);
+        }
+
+        void setRadius(int initRadius, int finishRadius) {
+            if (DEBUG_EXIT_ANIMATION) {
+                Slog.v(TAG, "RadialVanishAnimation setRadius init: " + initRadius
+                        + " final " + finishRadius);
+            }
+            mInitRadius = initRadius;
+            mFinishRadius = finishRadius;
+        }
+
+        void setCircleCenter(int x, int y) {
+            if (DEBUG_EXIT_ANIMATION) {
+                Slog.v(TAG, "RadialVanishAnimation setCircleCenter x: " + x + " y " + y);
+            }
+            mCircleCenter.set(x, y);
+        }
+
+        void setRadialPaintParam(int[] colors, float[] stops) {
+            // setup gradient shader
+            final RadialGradient rShader =
+                    new RadialGradient(0, 0, 1, colors, stops, Shader.TileMode.CLAMP);
+            mVanishPaint.setShader(rShader);
+            if (!DEBUG_EXIT_ANIMATION_BLEND) {
+                mVanishPaint.setBlendMode(BlendMode.MODULATE);
+            }
+        }
+
+        void setReady() {
+            mReady = true;
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            if (mReady) {
+                canvas.drawRect(0, 0, mView.getWidth(), mView.getHeight(), mVanishPaint);
+            }
+        }
+    }
+
+    private final class ShiftUpAnimation extends TranslateYAnimation {
+        ShiftUpAnimation(float fromYDelta, float toYDelta) {
+            super(fromYDelta, toYDelta);
+        }
+
+        @Override
+        protected void applyTransformation(float interpolatedTime, Transformation t) {
+            super.applyTransformation(interpolatedTime, t);
+
+            if (mFirstWindowSurface == null) {
+                return;
+            }
+            mTmpTransform.set(t.getMatrix());
+            final SurfaceControl.Transaction tx = mTransactionPool.acquire();
+            mTmpTransform.postTranslate(mFirstWindowFrame.left,
+                    mFirstWindowFrame.top + mMainWindowShiftLength);
+            tx.setMatrix(mFirstWindowSurface, mTmpTransform, mTmpFloat9);
+            // TODO set the vsyncId to ensure the transaction doesn't get applied too early.
+            //  Additionally, do you want to have this synchronized with your view animations?
+            //  If so, you'll need to use SyncRtSurfaceTransactionApplier
+            tx.apply();
+            mTransactionPool.release(tx);
+        }
+    }
+
+    private void reset() {
+        if (DEBUG_EXIT_ANIMATION) {
+            Slog.v(TAG, "vanish animation finished");
+        }
+        mSplashScreenView.post(() -> {
+            mSplashScreenView.setVisibility(GONE);
+            if (mFinishCallback != null) {
+                mFinishCallback.run();
+                mFinishCallback = null;
+            }
+        });
+        if (mFirstWindowSurface != null) {
+            final SurfaceControl.Transaction tx = mTransactionPool.acquire();
+            tx.setWindowCrop(mFirstWindowSurface, null);
+            tx.apply();
+            mFirstWindowSurface.release();
+            mFirstWindowSurface = null;
+        }
+    }
+
+    @Override
+    public void onAnimationStart(Animator animation) {
+        // ignore
+    }
+
+    @Override
+    public void onAnimationEnd(Animator animation) {
+        reset();
+    }
+
+    @Override
+    public void onAnimationCancel(Animator animation) {
+        reset();
+    }
+
+    @Override
+    public void onAnimationRepeat(Animator animation) {
+        // ignore
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 2973b50..3f9c271 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -31,12 +31,14 @@
 import android.graphics.drawable.LayerDrawable;
 import android.os.Build;
 import android.util.Slog;
+import android.view.SurfaceControl;
 import android.window.SplashScreenView;
 
 import com.android.internal.R;
 import com.android.internal.graphics.palette.Palette;
 import com.android.internal.graphics.palette.Quantizer;
 import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
+import com.android.wm.shell.common.TransactionPool;
 
 import java.util.List;
 
@@ -56,15 +58,25 @@
     // also 108*108 pixels, then do not enlarge this icon if only need to show foreground icon.
     private static final float ENLARGE_FOREGROUND_ICON_THRESHOLD = (72f * 72f) / (108f * 108f);
     private final Context mContext;
-    private final int mMaxIconAnimationDuration;
+    private final int mMaxAnimatableIconDuration;
 
     private int mIconSize;
     private int mBrandingImageWidth;
     private int mBrandingImageHeight;
+    private final int mAppRevealDuration;
+    private final int mIconExitDuration;
+    private int mMainWindowShiftLength;
+    private int mIconNormalExitDistance;
+    private int mIconEarlyExitDistance;
+    private final TransactionPool mTransactionPool;
 
-    SplashscreenContentDrawer(Context context, int maxIconAnimationDuration) {
+    SplashscreenContentDrawer(Context context, int maxAnimatableIconDuration,
+            int iconExitAnimDuration, int appRevealAnimDuration, TransactionPool pool) {
         mContext = context;
-        mMaxIconAnimationDuration = maxIconAnimationDuration;
+        mMaxAnimatableIconDuration = maxAnimatableIconDuration;
+        mAppRevealDuration = appRevealAnimDuration;
+        mIconExitDuration = iconExitAnimDuration;
+        mTransactionPool = pool;
     }
 
     private void updateDensity() {
@@ -74,6 +86,12 @@
                 com.android.wm.shell.R.dimen.starting_surface_brand_image_width);
         mBrandingImageHeight = mContext.getResources().getDimensionPixelSize(
                 com.android.wm.shell.R.dimen.starting_surface_brand_image_height);
+        mMainWindowShiftLength = mContext.getResources().getDimensionPixelSize(
+                com.android.wm.shell.R.dimen.starting_surface_exit_animation_window_shift_length);
+        mIconNormalExitDistance = mContext.getResources().getDimensionPixelSize(
+                com.android.wm.shell.R.dimen.starting_surface_normal_exit_icon_distance);
+        mIconEarlyExitDistance = mContext.getResources().getDimensionPixelSize(
+                com.android.wm.shell.R.dimen.starting_surface_early_exit_icon_distance);
     }
 
     private int getSystemBGColor() {
@@ -119,7 +137,7 @@
         if (attrs.mReplaceIcon != null) {
             iconDrawable = attrs.mReplaceIcon;
             animationDuration = Math.max(0,
-                    Math.min(attrs.mAnimationDuration, mMaxIconAnimationDuration));
+                    Math.min(attrs.mAnimationDuration, mMaxAnimatableIconDuration));
         } else {
             iconDrawable = iconRes != 0 ? context.getDrawable(iconRes)
                     : context.getPackageManager().getDefaultActivityIcon();
@@ -439,8 +457,8 @@
         }
 
         /**
-         * For ColorDrawable only.
-         * There will be only one color so don't spend too much resource for it.
+         * For ColorDrawable only. There will be only one color so don't spend too much resource for
+         * it.
          */
         private static class SingleColorTester implements ColorTester {
             private final ColorDrawable mColorDrawable;
@@ -472,9 +490,8 @@
         }
 
         /**
-         * For any other Drawable except ColorDrawable.
-         * This will use the Palette API to check the color information and use a quantizer to
-         * filter out transparent colors when needed.
+         * For any other Drawable except ColorDrawable. This will use the Palette API to check the
+         * color information and use a quantizer to filter out transparent colors when needed.
          */
         private static class ComplexDrawableTester implements ColorTester {
             private static final int MAX_BITMAP_SIZE = 40;
@@ -593,4 +610,17 @@
             }
         }
     }
+
+    /**
+     * Create and play the default exit animation for splash screen view.
+     */
+    void applyExitAnimation(SplashScreenView view, SurfaceControl leash,
+            Rect frame, boolean isEarlyExit, Runnable finishCallback) {
+        final SplashScreenExitAnimation animation = new SplashScreenExitAnimation(view, leash,
+                frame, mAppRevealDuration, mIconExitDuration, mMainWindowShiftLength,
+                isEarlyExit ? mIconEarlyExitDistance : mIconNormalExitDistance, mTransactionPool,
+                finishCallback);
+        animation.prepareAnimations();
+        animation.startAnimations();
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
index a594a9f..f258286 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java
@@ -16,7 +16,9 @@
 
 package com.android.wm.shell.startingsurface;
 
+import android.graphics.Rect;
 import android.os.IBinder;
+import android.view.SurfaceControl;
 import android.window.StartingWindowInfo;
 
 import java.util.function.BiConsumer;
@@ -31,7 +33,8 @@
     /**
      * Called when the content of a task is ready to show, starting window can be removed.
      */
-    void removeStartingWindow(int taskId);
+    void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+            boolean playRevealAnimation);
     /**
      * Called when the Task wants to copy the splash screen.
      * @param taskId
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 2d1d65b..14fbaac 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -29,14 +29,18 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
 import android.hardware.display.DisplayManager;
 import android.os.IBinder;
+import android.os.SystemClock;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.view.Choreographer;
 import android.view.Display;
+import android.view.SurfaceControl;
 import android.view.View;
-import android.view.Window;
 import android.view.WindowManager;
 import android.window.SplashScreenView;
 import android.window.SplashScreenView.SplashScreenViewParcelable;
@@ -46,6 +50,7 @@
 import com.android.internal.R;
 import com.android.internal.policy.PhoneWindow;
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.TransactionPool;
 
 import java.util.function.Consumer;
 
@@ -62,23 +67,23 @@
     private final DisplayManager mDisplayManager;
     private final ShellExecutor mSplashScreenExecutor;
     private final SplashscreenContentDrawer mSplashscreenContentDrawer;
-    protected Choreographer mChoreographer;
 
     // TODO(b/131727939) remove this when clearing ActivityRecord
     private static final int REMOVE_WHEN_TIMEOUT = 2000;
 
-    public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor) {
+    public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor,
+            TransactionPool pool) {
         mContext = context;
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
         mSplashScreenExecutor = splashScreenExecutor;
-        final int maxIconAnimDuration = context.getResources().getInteger(
+        final int maxAnimatableIconDuration = context.getResources().getInteger(
                 com.android.wm.shell.R.integer.max_starting_window_intro_icon_anim_duration);
-        mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, maxIconAnimDuration);
-        mSplashScreenExecutor.execute(this::initChoreographer);
-    }
-
-    protected void initChoreographer() {
-        mChoreographer = Choreographer.getInstance();
+        final int iconExitAnimDuration = context.getResources().getInteger(
+                com.android.wm.shell.R.integer.starting_window_icon_exit_anim_duration);
+        final int appRevealAnimDuration = context.getResources().getInteger(
+                com.android.wm.shell.R.integer.starting_window_app_reveal_anim_duration);
+        mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext,
+                maxAnimatableIconDuration, iconExitAnimDuration, appRevealAnimDuration, pool);
     }
 
     private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
@@ -195,6 +200,7 @@
         }
 
         final PhoneWindow win = new PhoneWindow(context);
+        win.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
         win.setIsStartingWindow(true);
 
         CharSequence label = context.getResources().getText(labelRes, null);
@@ -211,7 +217,7 @@
         // the keyguard is being hidden. This is okay because starting windows never show
         // secret information.
         // TODO(b/113840485): Occluded may not only happen on default display
-        if (displayId == DEFAULT_DISPLAY) {
+        if (displayId == DEFAULT_DISPLAY && windowInfo.isKeyguardOccluded) {
             windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
         }
 
@@ -247,6 +253,7 @@
         // Setting as trusted overlay to let touches pass through. This is safe because this
         // window is controlled by the system.
         params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+        params.format = PixelFormat.RGBA_8888;
 
         final Resources res = context.getResources();
         final boolean supportsScreen = res != null && (res.getCompatibilityInfo() != null
@@ -257,98 +264,25 @@
 
         params.setTitle("Splash Screen " + activityInfo.packageName);
 
-        // TODO(b/173975965) If the target activity doesn't request FLAG_HARDWARE_ACCELERATED, we
-        // cannot replace the content view after first view was drawn, sounds like an issue.
-        new AddSplashScreenViewRunnable(taskInfo.taskId, win, context, appToken, params, iconRes,
-                        splashscreenContentResId[0], enableHardAccelerated).run();
-    }
-
-    private class AddSplashScreenViewRunnable implements Runnable {
-        private final int mTaskId;
-        private final Window mWin;
-        private final IBinder mAppToken;
-        private final WindowManager.LayoutParams mLayoutParams;
-        private final Context mContext;
-        private final int mIconRes;
-        private final int mSplashscreenContentResId;
-        private final boolean mReplaceSplashScreenView;
-        private int mSequence;
-
-        AddSplashScreenViewRunnable(int taskId, Window window, Context context,
-                IBinder appToken, WindowManager.LayoutParams params, int iconRes,
-                int splashscreenContentResId, boolean replaceSplashScreenView) {
-            mTaskId = taskId;
-            mWin = window;
-            mAppToken = appToken;
-            mContext = context;
-            mLayoutParams = params;
-            mIconRes = iconRes;
-            mSplashscreenContentResId = splashscreenContentResId;
-            mReplaceSplashScreenView = replaceSplashScreenView;
-        }
-
-        private void createInitialView() {
-            View tempView = new View(mContext);
-            mWin.setContentView(tempView);
-            mSequence++;
-            final View view = mWin.getDecorView();
+        // TODO(b/173975965) tracking performance
+        final int taskId = taskInfo.taskId;
+        SplashScreenView sView = null;
+        try {
+            sView = mSplashscreenContentDrawer.makeSplashScreenContentView(context, iconRes,
+                            splashscreenContentResId[0]);
+            final View view = win.getDecorView();
             final WindowManager wm = mContext.getSystemService(WindowManager.class);
-            if (postAddWindow(mTaskId, mAppToken, view, wm, mLayoutParams)) {
-                mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, this, null);
+            if (postAddWindow(taskId, appToken, view, wm, params)) {
+                win.setContentView(sView);
+                sView.cacheRootWindow(win);
             }
-        }
-
-        private SplashScreenView replaceRealView() {
-            final SplashScreenView sView =
-                    mSplashscreenContentDrawer.makeSplashScreenContentView(mContext,
-                            mIconRes, mSplashscreenContentResId);
-            mWin.setContentView(sView);
-            sView.cacheRootWindow(mWin);
-            return sView;
-        }
-
-        private SplashScreenView initiateOnce() {
-            final SplashScreenView sView =
-                    mSplashscreenContentDrawer.makeSplashScreenContentView(mContext, mIconRes,
-                            mSplashscreenContentResId);
-            final View view = mWin.getDecorView();
-            final WindowManager wm = mContext.getSystemService(WindowManager.class);
-            if (postAddWindow(mTaskId, mAppToken, view, wm, mLayoutParams)) {
-                mWin.setContentView(sView);
-                sView.cacheRootWindow(mWin);
-            }
-            return sView;
-        }
-
-        @Override
-        public void run() {
-            SplashScreenView view = null;
-            boolean setRecord = false;
-            try {
-                if (mReplaceSplashScreenView) {
-                    // Tricky way to make animation start faster... create the real content after
-                    // first window drawn. The first empty window won't been see because wm will
-                    // still need to wait for transition ready.
-                    if (mSequence == 0) {
-                        createInitialView();
-                    } else if (mSequence == 1) {
-                        setRecord = true;
-                        view = replaceRealView();
-                    }
-                } else {
-                    setRecord = true;
-                    view = initiateOnce();
-                }
-            } catch (RuntimeException e) {
-                // don't crash if something else bad happens, for example a
-                // failure loading resources because we are loading from an app
-                // on external storage that has been unmounted.
-                Slog.w(TAG, " failed creating starting window", e);
-            } finally {
-                if (setRecord) {
-                    setSplashScreenRecord(mTaskId, view);
-                }
-            }
+        } catch (RuntimeException e) {
+            // don't crash if something else bad happens, for example a
+            // failure loading resources because we are loading from an app
+            // on external storage that has been unmounted.
+            Slog.w(TAG, " failed creating starting window", e);
+        } finally {
+            setSplashScreenRecord(taskId, sView);
         }
     }
 
@@ -359,8 +293,10 @@
             TaskSnapshot snapshot) {
         final int taskId = startingWindowInfo.taskInfo.taskId;
         final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken,
-                snapshot, mSplashScreenExecutor, () -> removeWindowSynced(taskId));
-        mSplashScreenExecutor.executeDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT);
+                snapshot, mSplashScreenExecutor,
+                () -> removeWindowNoAnimate(taskId));
+        mSplashScreenExecutor.executeDelayed(() -> removeWindowNoAnimate(taskId),
+                REMOVE_WHEN_TIMEOUT);
         final StartingWindowRecord tView =
                 new StartingWindowRecord(null/* decorView */, surface);
         mStartingWindowRecords.put(taskId, tView);
@@ -369,11 +305,12 @@
     /**
      * Called when the content of a task is ready to show, starting window can be removed.
      */
-    public void removeStartingWindow(int taskId) {
+    public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+            boolean playRevealAnimation) {
         if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) {
             Slog.d(TAG, "Task start finish, remove starting surface for task " + taskId);
         }
-        removeWindowSynced(taskId);
+        removeWindowSynced(taskId, leash, frame, playRevealAnimation);
     }
 
     /**
@@ -383,13 +320,6 @@
     public void copySplashScreenView(int taskId) {
         final StartingWindowRecord preView = mStartingWindowRecords.get(taskId);
         SplashScreenViewParcelable parcelable;
-        if (preView != null) {
-            if (preView.isWaitForContent()) {
-                mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT,
-                        () -> copySplashScreenView(taskId), null);
-                return;
-            }
-        }
         if (preView != null && preView.mContentView != null
                 && preView.mContentView.isCopyable()) {
             parcelable = new SplashScreenViewParcelable(preView.mContentView);
@@ -413,12 +343,6 @@
             Slog.w(TAG, appToken + " already running, starting window not displayed. "
                     + e.getMessage());
             shouldSaveView = false;
-        } catch (RuntimeException e) {
-            // don't crash if something else bad happens, for example a
-            // failure loading resources because we are loading from an app
-            // on external storage that has been unmounted.
-            Slog.w(TAG, appToken + " failed creating starting window", e);
-            shouldSaveView = false;
         } finally {
             if (view != null && view.getParent() == null) {
                 Slog.w(TAG, "view not successfully added to wm, removing view");
@@ -427,9 +351,9 @@
             }
         }
         if (shouldSaveView) {
-            removeWindowSynced(taskId);
-            mSplashScreenExecutor.executeDelayed(() -> removeWindowSynced(taskId),
-                    REMOVE_WHEN_TIMEOUT);
+            removeWindowNoAnimate(taskId);
+            mSplashScreenExecutor.executeDelayed(
+                    () -> removeWindowNoAnimate(taskId), REMOVE_WHEN_TIMEOUT);
             saveSplashScreenRecord(taskId, view);
         }
         return shouldSaveView;
@@ -449,24 +373,30 @@
         }
     }
 
-    protected void removeWindowSynced(int taskId) {
+    private void removeWindowNoAnimate(int taskId) {
+        removeWindowSynced(taskId, null, null, false);
+    }
+
+    protected void removeWindowSynced(int taskId, SurfaceControl leash, Rect frame,
+            boolean playRevealAnimation) {
         final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
         if (record != null) {
-            if (record.isWaitForContent()) {
-                if (DEBUG_SPLASH_SCREEN) {
-                    Slog.v(TAG, "splash screen window haven't been draw yet");
-                }
-                mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT,
-                        () -> removeWindowSynced(taskId), null);
-                return;
-            }
             if (record.mDecorView != null) {
                 if (DEBUG_SPLASH_SCREEN) {
                     Slog.v(TAG, "Removing splash screen window for task: " + taskId);
                 }
-                final WindowManager wm = record.mDecorView.getContext()
-                        .getSystemService(WindowManager.class);
-                wm.removeView(record.mDecorView);
+                if (record.mContentView != null) {
+                    final HandleExitFinish exitFinish = new HandleExitFinish(record.mDecorView);
+                    if (leash != null || playRevealAnimation) {
+                        mSplashscreenContentDrawer.applyExitAnimation(record.mContentView,
+                                leash, frame, record.isEarlyExit(), exitFinish);
+                        mSplashScreenExecutor.executeDelayed(exitFinish, REMOVE_WHEN_TIMEOUT);
+                    } else {
+                        // the SplashScreenView has been copied to client, skip default exit
+                        // animation
+                        exitFinish.run();
+                    }
+                }
             }
             if (record.mTaskSnapshotWindow != null) {
                 if (DEBUG_TASK_SNAPSHOT) {
@@ -478,6 +408,26 @@
         }
     }
 
+    private static class HandleExitFinish implements Runnable {
+        private View mDecorView;
+
+        HandleExitFinish(View decorView) {
+            mDecorView = decorView;
+        }
+
+        @Override
+        public void run() {
+            if (mDecorView == null) {
+                return;
+            }
+            final WindowManager wm = mDecorView.getContext().getSystemService(WindowManager.class);
+            if (wm != null) {
+                wm.removeView(mDecorView);
+            }
+            mDecorView = null;
+        }
+    }
+
     private void getWindowResFromContext(Context ctx, Consumer<TypedArray> consumer) {
         final TypedArray a = ctx.obtainStyledAttributes(R.styleable.Window);
         consumer.accept(a);
@@ -488,10 +438,12 @@
      * Record the view or surface for a starting window.
      */
     private static class StartingWindowRecord {
+        private static final long EARLY_START_MINIMUM_TIME_MS = 250;
         private final View mDecorView;
         private final TaskSnapshotWindow mTaskSnapshotWindow;
         private SplashScreenView mContentView;
         private boolean mSetSplashScreen;
+        private long mContentCreateTime;
 
         StartingWindowRecord(View decorView, TaskSnapshotWindow taskSnapshotWindow) {
             mDecorView = decorView;
@@ -503,11 +455,12 @@
                 return;
             }
             mContentView = splashScreenView;
+            mContentCreateTime = SystemClock.uptimeMillis();
             mSetSplashScreen = true;
         }
 
-        private boolean isWaitForContent() {
-            return mDecorView != null && !mSetSplashScreen;
+        boolean isEarlyExit() {
+            return SystemClock.uptimeMillis() - mContentCreateTime < EARLY_START_MINIMUM_TIME_MS;
         }
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
index 5eb7071..a694e52 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java
@@ -28,14 +28,17 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityTaskManager;
 import android.content.Context;
+import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
+import android.view.SurfaceControl;
 import android.window.StartingWindowInfo;
 import android.window.TaskOrganizer;
 import android.window.TaskSnapshot;
 
 import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.TransactionPool;
 
 import java.util.function.BiConsumer;
 
@@ -67,8 +70,16 @@
     private final StartingSurfaceImpl mImpl = new StartingSurfaceImpl();
     private final ShellExecutor mSplashScreenExecutor;
 
+    // For Car Launcher
     public StartingWindowController(Context context, ShellExecutor splashScreenExecutor) {
-        mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor);
+        mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor,
+                new TransactionPool());
+        mSplashScreenExecutor = splashScreenExecutor;
+    }
+
+    public StartingWindowController(Context context, ShellExecutor splashScreenExecutor,
+            TransactionPool pool) {
+        mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor, pool);
         mSplashScreenExecutor = splashScreenExecutor;
     }
 
@@ -112,7 +123,8 @@
                         + " allowTaskSnapshot " + allowTaskSnapshot
                         + " activityCreated " + activityCreated);
             }
-            if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
+            if ((newTask || !processRunning || (taskSwitch && !activityCreated))
+                    && windowInfo.taskInfo.topActivityType != ACTIVITY_TYPE_HOME) {
                 return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
             }
             if (taskSwitch && allowTaskSnapshot) {
@@ -198,8 +210,9 @@
     /**
      * Called when the content of a task is ready to show, starting window can be removed.
      */
-    void removeStartingWindow(int taskId) {
-        mStartingSurfaceDrawer.removeStartingWindow(taskId);
+    void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+            boolean playRevealAnimation) {
+        mStartingSurfaceDrawer.removeStartingWindow(taskId, leash, frame, playRevealAnimation);
     }
 
     private class StartingSurfaceImpl implements StartingSurface {
@@ -211,9 +224,11 @@
         }
 
         @Override
-        public void removeStartingWindow(int taskId) {
+        public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+                boolean playRevealAnimation) {
             mSplashScreenExecutor.execute(() ->
-                    StartingWindowController.this.removeStartingWindow(taskId));
+                    StartingWindowController.this.removeStartingWindow(taskId, leash, frame,
+                            playRevealAnimation));
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index f06d57c..ad4ccc0 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -11,6 +11,8 @@
         <option name="force-skip-system-props" value="true" />
         <!-- set WM tracing verbose level to all -->
         <option name="run-command" value="cmd window tracing level all" />
+        <!-- set WM tracing to frame (avoid incomplete states) -->
+        <option name="run-command" value="cmd window tracing frame" />
         <!-- restart launcher to activate TAPL -->
         <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
     </target_preparer>
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index 90e7137..63968f3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -18,6 +18,7 @@
 
 import android.os.SystemClock
 import android.platform.test.annotations.Presubmit
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
@@ -59,6 +60,14 @@
             }
         }
 
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @FlakyTest
+    @Test
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
     @Presubmit
     @Test
     fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index dc51b4f..63e9a78 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -56,6 +56,14 @@
             }
         }
 
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @FlakyTest
+    @Test
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
     @Presubmit
     @Test
     fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 5bb9b2f..234dda4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -61,6 +61,14 @@
             }
         }
 
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @FlakyTest
+    @Test
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
     @Presubmit
     @Test
     fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
index 91e080f..128560a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt
@@ -20,19 +20,23 @@
 import android.platform.test.annotations.Presubmit
 import android.system.helpers.ActivityHelper
 import android.util.Log
+import android.view.Surface
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.compatibility.common.util.SystemUtil
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.isRotated
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
 import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
 import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -134,17 +138,39 @@
 
     @Presubmit
     @Test
-    open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    open fun navBarLayerIsAlwaysVisible() {
+        testSpec.navBarLayerIsAlwaysVisible()
+    }
 
     @Presubmit
     @Test
-    open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    open fun statusBarLayerIsAlwaysVisible() {
+        testSpec.statusBarLayerIsAlwaysVisible()
+    }
 
     @Presubmit
     @Test
-    open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    open fun navBarWindowIsAlwaysVisible() {
+        testSpec.navBarWindowIsAlwaysVisible()
+    }
 
     @Presubmit
     @Test
-    open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    open fun statusBarWindowIsAlwaysVisible() {
+        testSpec.statusBarWindowIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun navBarLayerRotatesAndScales() {
+        testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0,
+            testSpec.config.endRotation)
+    }
+
+    @Presubmit
+    @Test
+    open fun statusBarLayerRotatesScales() {
+        testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
+            testSpec.config.endRotation)
+    }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index 5f003ba..d341bb1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -27,8 +27,6 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.wm.shell.flicker.appPairsDividerIsVisible
 import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
 import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
@@ -75,16 +73,6 @@
     @Test
     fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
 
-    @Presubmit
-    @Test
-    fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0,
-        testSpec.config.endRotation)
-
-    @Presubmit
-    @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
-        testSpec.config.endRotation)
-
     @FlakyTest(bugId = 172776659)
     @Test
     fun appPairsPrimaryBoundsIsVisible() =
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index d479208..3bf0296 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -27,16 +27,13 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.endRotation
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
 import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.wm.shell.flicker.appPairsDividerIsVisible
 import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible
 import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible
 import com.android.wm.shell.flicker.helpers.AppPairsHelper
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
-import org.junit.Assume
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -67,20 +64,10 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0,
-        testSpec.config.endRotation)
-
-    @Presubmit
-    @Test
     fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible()
 
     @Presubmit
     @Test
-    fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(
-        Surface.ROTATION_0, testSpec.config.endRotation)
-
-    @Presubmit
-    @Test
     override fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
 
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
index 75c33c6..0333227 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt
@@ -23,13 +23,7 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -48,7 +42,6 @@
     testSpec: FlickerTestParameter
 ) : PipTransition(testSpec) {
     private val testApp = FixedAppHelper(instrumentation)
-    private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
 
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = buildTransition(eachRun = true) {
@@ -84,14 +77,6 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
     fun showBothAppLayersThenHidePip() {
         testSpec.assertLayers {
             isVisible(testApp.defaultWindowName)
@@ -118,14 +103,6 @@
         }
     }
 
-    @Presubmit
-    @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
index 2f08db1b7..4847c98 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -24,14 +25,6 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.startRotation
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -57,15 +50,7 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun pipWindowBecomesVisible() {
+    fun pipAppWindowAlwaysVisible() {
         testSpec.assertWm {
             this.showsAppWindow(pipApp.defaultWindowName)
         }
@@ -73,34 +58,37 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerRotatesScales() =
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
     fun pipLayerBecomesVisible() {
         testSpec.assertLayers {
             this.isVisible(pipApp.launcherName)
         }
     }
 
-    @FlakyTest(bugId = 140855415)
+    @Postsubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    fun pipWindowBecomesVisible() {
+        testSpec.assertWm {
+            invoke("pipWindowIsNotVisible") { !it.wmState.hasPipWindow() }
+                .then()
+                .invoke("pipWindowIsVisible") { it.wmState.hasPipWindow() }
+        }
+    }
 
     @FlakyTest(bugId = 140855415)
     @Test
-    fun navBarLayerRotatesAndScales() =
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+    override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
 
     @FlakyTest(bugId = 140855415)
     @Test
-    fun noUncoveredRegions() =
-        testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @FlakyTest(bugId = 140855415)
+    @Test
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
+    @FlakyTest(bugId = 140855415)
+    @Test
+    override fun noUncoveredRegions() = super.noUncoveredRegions()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 9011f1a..ba88ee5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -18,16 +18,13 @@
 
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
 import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
 import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
@@ -83,6 +80,14 @@
             }
         }
 
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @FlakyTest
+    @Test
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
     @Presubmit
     @Test
     fun pipAppWindowIsAlwaysOnTop() {
@@ -109,14 +114,6 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
     fun pipAppLayerHidesTestApp() {
         testSpec.assertLayersStart {
             coversExactly(startingBounds, pipApp.defaultWindowName)
@@ -132,14 +129,6 @@
         }
     }
 
-    @Presubmit
-    @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
index 3e33176..eae7e97 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt
@@ -24,14 +24,7 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.Test
 import org.junit.runners.Parameterized
 
@@ -52,22 +45,6 @@
 
     @Presubmit
     @Test
-    open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
     open fun pipWindowBecomesInvisible() {
         testSpec.assertWm {
             this.showsAppWindow(PIP_WINDOW_TITLE)
@@ -86,21 +63,6 @@
         }
     }
 
-    @Presubmit
-    @Test
-    open fun statusBarLayerRotatesScales() =
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
-    open fun noUncoveredRegions() =
-        testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
-    open fun navBarLayerRotatesAndScales() =
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-
     @FlakyTest(bugId = 151179149)
     @Test
     open fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
index 97afc65..3309e10 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt
@@ -24,17 +24,11 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.helpers.launchSplitScreen
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
 import com.android.wm.shell.flicker.helpers.ImeAppHelper
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
 import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.wm.shell.flicker.removeAllTasksButHome
 import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
 import org.junit.FixMethodOrder
@@ -55,7 +49,6 @@
 class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
     private val imeApp = ImeAppHelper(instrumentation)
     private val testApp = FixedAppHelper(instrumentation)
-    private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
 
     override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
         get() = {
@@ -105,11 +98,11 @@
 
     @Postsubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
 
     @Postsubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
 
     @Postsubmit
     @Test
@@ -130,11 +123,11 @@
 
     @Postsubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+    override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible()
 
     @Postsubmit
     @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+    override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible()
 
     companion object {
         const val TEST_REPETITIONS = 2
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
index 4c95da2..d011419 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt
@@ -24,8 +24,6 @@
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.google.common.truth.Truth
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -59,11 +57,11 @@
 
     @Postsubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+    override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
 
     @Postsubmit
     @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+    override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
 
     @Postsubmit
     @Test
@@ -71,6 +69,14 @@
 
     @Postsubmit
     @Test
+    fun pipLayerInsideDisplay() {
+        testSpec.assertLayersStart {
+            coversAtMost(displayBounds, pipApp.defaultWindowName)
+        }
+    }
+
+    @Postsubmit
+    @Test
     fun pipWindowMovesUp() = testSpec.assertWmEnd {
         val initialState = this.trace?.first()?.wmState
             ?: error("Trace should not be empty")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
index df835d2..49a1055 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt
@@ -29,10 +29,6 @@
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.startRotation
 import com.android.wm.shell.flicker.helpers.FixedAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.noUncoveredRegions
 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
 import com.android.server.wm.flicker.statusBarLayerRotatesScales
@@ -77,34 +73,18 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
+    override fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
         testSpec.config.endRotation, allStates = false)
 
-    @Presubmit
-    @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
     @FlakyTest(bugId = 140855415)
     @Test
-    fun navBarLayerRotatesAndScales() =
+    override fun navBarLayerRotatesAndScales() =
         testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation,
             testSpec.config.endRotation)
 
     @FlakyTest(bugId = 140855415)
     @Test
-    fun statusBarLayerRotatesScales() =
+    override fun statusBarLayerRotatesScales() =
         testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation,
             testSpec.config.endRotation)
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
index 1bb1d28..945a20b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt
@@ -26,14 +26,7 @@
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -68,22 +61,6 @@
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
     fun appReplacesPipWindow() {
         testSpec.assertWm {
             this.showsAppWindow(PIP_WINDOW_TITLE)
@@ -94,11 +71,6 @@
 
     @Presubmit
     @Test
-    fun statusBarLayerRotatesScales() =
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
     fun appReplacesPipLayer() {
         testSpec.assertLayers {
             this.isVisible(PIP_WINDOW_TITLE)
@@ -107,15 +79,13 @@
         }
     }
 
-    @Presubmit
+    @FlakyTest
     @Test
-    fun noUncoveredRegions() =
-        testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
-    fun navBarLayerRotatesAndScales() =
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+    fun testAppCoversFullScreen() {
+        testSpec.assertLayersStart {
+            coversExactly(displayBounds, pipApp.defaultWindowName)
+        }
+    }
 
     @FlakyTest(bugId = 151179149)
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index b0a9afe..7dc7e7d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -18,27 +18,37 @@
 
 import android.app.Instrumentation
 import android.content.Intent
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.WindowUtils
 import com.android.server.wm.flicker.helpers.isRotated
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
 import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.wm.shell.flicker.helpers.PipAppHelper
 import com.android.wm.shell.flicker.removeAllTasksButHome
 import com.android.wm.shell.flicker.testapp.Components
+import org.junit.Test
 
 abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
     protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
     protected val isRotated = testSpec.config.startRotation.isRotated()
     protected val pipApp = PipAppHelper(instrumentation)
+    protected val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
     protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
     protected abstract val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
-
     // Helper class to process test actions by broadcast.
     protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) {
         private fun createIntentWithAction(broadcastAction: String): Intent {
@@ -148,4 +158,35 @@
             extraSpec(this, configuration)
         }
     }
+
+    @Presubmit
+    @Test
+    open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
+
+    @Presubmit
+    @Test
+    open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
+
+    @Presubmit
+    @Test
+    open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
+
+    @Presubmit
+    @Test
+    open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
+
+    @Presubmit
+    @Test
+    open fun navBarLayerRotatesAndScales() =
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+
+    @Presubmit
+    @Test
+    open fun statusBarLayerRotatesScales() =
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+
+    @Presubmit
+    @Test
+    open fun noUncoveredRegions() =
+        testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
index 7916ce5..67e1768 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -25,10 +26,6 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
 import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
 import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP
@@ -83,6 +80,14 @@
             }
         }
 
+    @FlakyTest
+    @Test
+    override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
+
+    @FlakyTest
+    @Test
+    override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
+
     @Presubmit
     @Test
     fun pipWindowInsideDisplay() {
@@ -101,20 +106,18 @@
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
     fun pipLayerInsideDisplay() {
         testSpec.assertLayersStart {
             coversAtMost(startingBounds, pipApp.defaultWindowName)
         }
     }
 
+    @Postsubmit
+    @Test
+    fun pipAlwaysVisible() = testSpec.assertWm {
+        this.showsAppWindow(pipApp.windowName)
+    }
+
     @Presubmit
     @Test
     fun pipAppLayerCoversFullScreen() {
@@ -123,14 +126,6 @@
         }
     }
 
-    @Presubmit
-    @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
index a531ef5..207db9e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package unittest.src.com.android.wm.shell.startingsurface;
+package com.android.wm.shell.startingsurface;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -33,11 +33,12 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.graphics.Rect;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.testing.TestableContext;
-import android.view.Choreographer;
+import android.view.SurfaceControl;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowMetrics;
@@ -49,7 +50,7 @@
 
 import com.android.wm.shell.common.HandlerExecutor;
 import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.startingsurface.StartingSurfaceDrawer;
+import com.android.wm.shell.common.TransactionPool;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -68,7 +69,7 @@
     @Mock
     private WindowManager mMockWindowManager;
     @Mock
-    private static Choreographer sFakeChoreographer;
+    private TransactionPool mTransactionPool;
 
     TestStartingSurfaceDrawer mStartingSurfaceDrawer;
 
@@ -76,13 +77,9 @@
         int mAddWindowForTask = 0;
         int mViewThemeResId;
 
-        TestStartingSurfaceDrawer(Context context, ShellExecutor executor) {
-            super(context, executor);
-        }
-
-        @Override
-        protected void initChoreographer() {
-            mChoreographer = sFakeChoreographer;
+        TestStartingSurfaceDrawer(Context context, ShellExecutor animExecutor,
+                TransactionPool pool) {
+            super(context, animExecutor, pool);
         }
 
         @Override
@@ -95,7 +92,8 @@
         }
 
         @Override
-        protected void removeWindowSynced(int taskId) {
+        protected void removeWindowSynced(int taskId, SurfaceControl leash, Rect frame,
+                boolean playRevealAnimation) {
             // listen for removeView
             if (mAddWindowForTask == taskId) {
                 mAddWindowForTask = 0;
@@ -123,7 +121,8 @@
         doNothing().when(mMockWindowManager).addView(any(), any());
 
         mStartingSurfaceDrawer = spy(new TestStartingSurfaceDrawer(context,
-                new HandlerExecutor(new Handler(Looper.getMainLooper()))));
+                new HandlerExecutor(new Handler(Looper.getMainLooper())),
+                mTransactionPool));
     }
 
     @Test
@@ -137,9 +136,9 @@
         verify(mStartingSurfaceDrawer).postAddWindow(eq(taskId), eq(mBinder), any(), any(), any());
         assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId);
 
-        mStartingSurfaceDrawer.removeStartingWindow(windowInfo.taskInfo.taskId);
+        mStartingSurfaceDrawer.removeStartingWindow(windowInfo.taskInfo.taskId, null, null, false);
         waitHandlerIdle(mainLoop);
-        verify(mStartingSurfaceDrawer).removeWindowSynced(eq(taskId));
+        verify(mStartingSurfaceDrawer).removeWindowSynced(eq(taskId), any(), any(), eq(false));
         assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, 0);
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
index 27e5f51..b908df2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package unittest.src.com.android.wm.shell.startingsurface;
+package com.android.wm.shell.startingsurface;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -48,7 +48,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.wm.shell.TestShellExecutor;
-import com.android.wm.shell.startingsurface.TaskSnapshotWindow;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index c0ef7be..7e45f95 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -226,8 +226,6 @@
 }
 
 void AssetManager2::DumpToLog() const {
-  base::ScopedLogSeverity _log(base::INFO);
-
   LOG(INFO) << base::StringPrintf("AssetManager2(this=%p)", this);
 
   std::string list;
@@ -1721,7 +1719,6 @@
 }
 
 void Theme::Dump() const {
-  base::ScopedLogSeverity _log(base::INFO);
   LOG(INFO) << base::StringPrintf("Theme(this=%p, AssetManager2=%p)", this, asset_manager_);
 
   for (int p = 0; p < packages_.size(); p++) {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9793300..800c580 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -576,6 +576,7 @@
 
     if (requireSwap) {
         if (mExpectSurfaceStats) {
+            reportMetricsWithPresentTime();
             std::lock_guard lock(mLast4FrameInfosMutex);
             std::pair<FrameInfo*, int64_t>& next = mLast4FrameInfos.next();
             next.first = mCurrentFrameInfo;
@@ -656,8 +657,6 @@
         }
     }
 
-    instance->reportMetricsWithPresentTime();
-
     if (frameInfo != nullptr) {
         if (gpuCompleteTime == -1) {
             gpuCompleteTime = frameInfo->get(FrameInfoIndex::SwapBuffersCompleted);
diff --git a/media/jni/soundpool/Sound.cpp b/media/jni/soundpool/Sound.cpp
index f8b4bdb..50e0d33 100644
--- a/media/jni/soundpool/Sound.cpp
+++ b/media/jni/soundpool/Sound.cpp
@@ -99,8 +99,8 @@
                                     __func__);
                             break;
                         }
-                        int sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize);
-                        ALOGV("%s: read %d", __func__, sampleSize);
+                        ssize_t sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize);
+                        ALOGV("%s: read %zd", __func__, sampleSize);
                         if (sampleSize < 0) {
                             sampleSize = 0;
                             sawInputEOS = true;
@@ -124,8 +124,8 @@
                 }
 
                 AMediaCodecBufferInfo info;
-                const int status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1);
-                ALOGV("%s: dequeueoutput returned: %d", __func__, status);
+                const ssize_t status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1);
+                ALOGV("%s: dequeueoutput returned: %zd", __func__, status);
                 if (status >= 0) {
                     if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
                         ALOGV("%s: output EOS", __func__);
@@ -167,10 +167,10 @@
                 } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
                     ALOGV("%s: no output buffer right now", __func__);
                 } else if (status <= AMEDIA_ERROR_BASE) {
-                    ALOGE("%s: decode error: %d", __func__, status);
+                    ALOGE("%s: decode error: %zd", __func__, status);
                     break;
                 } else {
-                    ALOGV("%s: unexpected info code: %d", __func__, status);
+                    ALOGV("%s: unexpected info code: %zd", __func__, status);
                 }
             }
 
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index abb0f12..95fe000 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -320,7 +320,8 @@
             // audio track while the new one is being started and avoids processing them with
             // wrong audio audio buffer size  (mAudioBufferSize)
             auto toggle = mToggle ^ 1;
-            void* userData = (void*)((uintptr_t)this | toggle);
+            // NOLINTNEXTLINE(performance-no-int-to-ptr)
+            void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle);
             audio_channel_mask_t soundChannelMask = sound->getChannelMask();
             // When sound contains a valid channel mask, use it as is.
             // Otherwise, use stream count to calculate channel mask.
@@ -391,6 +392,7 @@
 void Stream::staticCallback(int event, void* user, void* info)
 {
     const auto userAsInt = (uintptr_t)user;
+    // NOLINTNEXTLINE(performance-no-int-to-ptr)
     auto stream = reinterpret_cast<Stream*>(userAsInt & ~1);
     stream->callback(event, info, int(userAsInt & 1), 0 /* tries */);
 }
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
index 502ee00..8b84bf3 100644
--- a/media/jni/soundpool/StreamManager.cpp
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -330,7 +330,7 @@
 
     // streams on mProcessingStreams are undergoing processing by the StreamManager thread
     // and do not participate in normal stream migration.
-    return found;
+    return (ssize_t)found;
 }
 
 void StreamManager::addToRestartQueue_l(Stream *stream) {
diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
index 357cc63..a66d99f 100644
--- a/media/jni/soundpool/android_media_SoundPool.cpp
+++ b/media/jni/soundpool/android_media_SoundPool.cpp
@@ -34,7 +34,8 @@
     jclass      mSoundPoolClass;
 } fields;
 static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) {
-    return (SoundPool*)env->GetLongField(thiz, fields.mNativeContext);
+    // NOLINTNEXTLINE(performance-no-int-to-ptr)
+    return reinterpret_cast<SoundPool*>(env->GetLongField(thiz, fields.mNativeContext));
 }
 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
 struct audio_attributes_fields_t {
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index a8f1a4d..243e4ca 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -143,6 +143,7 @@
 
   public static class ConnectivityManager.NetworkCallback {
     ctor public ConnectivityManager.NetworkCallback();
+    ctor public ConnectivityManager.NetworkCallback(int);
     method public void onAvailable(@NonNull android.net.Network);
     method public void onBlockedStatusChanged(@NonNull android.net.Network, boolean);
     method public void onCapabilitiesChanged(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities);
@@ -150,6 +151,7 @@
     method public void onLosing(@NonNull android.net.Network, int);
     method public void onLost(@NonNull android.net.Network);
     method public void onUnavailable();
+    field public static final int FLAG_INCLUDE_LOCATION_INFO = 1; // 0x1
   }
 
   public static interface ConnectivityManager.OnNetworkActiveListener {
@@ -293,6 +295,7 @@
     method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
     method public int getOwnerUid();
     method public int getSignalStrength();
+    method @NonNull public java.util.Set<java.lang.Integer> getSubIds();
     method @Nullable public android.net.TransportInfo getTransportInfo();
     method public boolean hasCapability(int);
     method public boolean hasTransport(int);
@@ -399,6 +402,11 @@
     method public android.net.NetworkRequest.Builder removeTransportType(int);
     method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
     method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
+    method @NonNull public android.net.NetworkRequest.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
+  }
+
+  public class ParseException extends java.lang.RuntimeException {
+    field public String response;
   }
 
   public class ProxyInfo implements android.os.Parcelable {
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 6df57c1..4b33366 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -7,8 +7,9 @@
 
   public class ConnectivityManager {
     method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public java.util.List<android.net.NetworkStateSnapshot> getAllNetworkStateSnapshot();
+    method @NonNull public static android.util.Range<java.lang.Integer> getIpSecNetIdRange();
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_SETTINGS}) public void registerSystemDefaultNetworkCallback(@NonNull android.net.ConnectivityManager.NetworkCallback, @NonNull android.os.Handler);
-    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @Nullable android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void requestBackgroundNetwork(@NonNull android.net.NetworkRequest, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
   }
 
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index a732430..a98f14e 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -296,6 +296,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
+    method @NonNull public android.net.NetworkCapabilities.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
     method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
   }
 
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index bbf4559..7189be1 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -16,10 +16,10 @@
 package android.net;
 
 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-import static android.net.IpSecManager.INVALID_RESOURCE_ID;
 import static android.net.NetworkRequest.Type.BACKGROUND_REQUEST;
 import static android.net.NetworkRequest.Type.LISTEN;
 import static android.net.NetworkRequest.Type.REQUEST;
+import static android.net.NetworkRequest.Type.TRACK_BEST;
 import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
 import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT;
 import static android.net.QosCallback.QosCallbackRegistrationException;
@@ -43,6 +43,7 @@
 import android.net.TetheringManager.StartTetheringCallback;
 import android.net.TetheringManager.TetheringEventCallback;
 import android.net.TetheringManager.TetheringRequest;
+import android.net.wifi.WifiNetworkSuggestion;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
@@ -1314,7 +1315,7 @@
     }
 
     /**
-     * Returns an array of {@link android.net.NetworkCapabilities} objects, representing
+     * Returns an array of {@link NetworkCapabilities} objects, representing
      * the Networks that applications run by the given user will use by default.
      * @hide
      */
@@ -1394,11 +1395,19 @@
     }
 
     /**
-     * Get the {@link android.net.NetworkCapabilities} for the given {@link Network}.  This
+     * Get the {@link NetworkCapabilities} for the given {@link Network}.  This
      * will return {@code null} if the network is unknown.
      *
+     * This will remove any location sensitive data in {@link TransportInfo} embedded in
+     * {@link NetworkCapabilities#getTransportInfo()}. Some transport info instances like
+     * {@link android.net.wifi.WifiInfo} contain location sensitive information. Retrieving
+     * this location sensitive information (subject to app's location permissions) will be
+     * noted by system. To include any location sensitive data in {@link TransportInfo},
+     * use a {@link NetworkCallback} with
+     * {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} flag.
+     *
      * @param network The {@link Network} object identifying the network in question.
-     * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}.
+     * @return The {@link NetworkCapabilities} for the network, or {@code null}.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     @Nullable
@@ -1996,7 +2005,7 @@
             dup = createInvalidFd();
         }
         return new NattSocketKeepalive(mService, network, dup,
-                INVALID_RESOURCE_ID /* Unused */, source, destination, executor, callback);
+                -1 /* Unused */, source, destination, executor, callback);
     }
 
     /**
@@ -3244,6 +3253,54 @@
      */
     public static class NetworkCallback {
         /**
+         * No flags associated with this callback.
+         * @hide
+         */
+        public static final int FLAG_NONE = 0;
+        /**
+         * Use this flag to include any location sensitive data in {@link NetworkCapabilities} sent
+         * via {@link #onCapabilitiesChanged(Network, NetworkCapabilities)}.
+         * <p>
+         * These include:
+         * <li> Some transport info instances (retrieved via
+         * {@link NetworkCapabilities#getTransportInfo()}) like {@link android.net.wifi.WifiInfo}
+         * contain location sensitive information.
+         * <li> OwnerUid (retrieved via {@link NetworkCapabilities#getOwnerUid()} is location
+         * sensitive for wifi suggestor apps (i.e using {@link WifiNetworkSuggestion}).</li>
+         * </p>
+         * <p>
+         * Note:
+         * <li> Retrieving this location sensitive information (subject to app's location
+         * permissions) will be noted by system. </li>
+         * <li> Without this flag any {@link NetworkCapabilities} provided via the callback does
+         * not include location sensitive info.
+         * </p>
+         */
+        public static final int FLAG_INCLUDE_LOCATION_INFO = 1 << 0;
+
+        /** @hide */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef(flag = true, prefix = "FLAG_", value = {
+                FLAG_NONE,
+                FLAG_INCLUDE_LOCATION_INFO
+        })
+        public @interface Flag { }
+
+        /**
+         * All the valid flags for error checking.
+         */
+        private static final int VALID_FLAGS = FLAG_INCLUDE_LOCATION_INFO;
+
+        public NetworkCallback() {
+            this(FLAG_NONE);
+        }
+
+        public NetworkCallback(@Flag int flags) {
+            Preconditions.checkArgument((flags & VALID_FLAGS) == flags);
+            mFlags = flags;
+        }
+
+        /**
          * Called when the framework connects to a new network to evaluate whether it satisfies this
          * request. If evaluation succeeds, this callback may be followed by an {@link #onAvailable}
          * callback. There is no guarantee that this new network will satisfy any requests, or that
@@ -3380,7 +3437,7 @@
          * calling these methods while in a callback may return an outdated or even a null object.
          *
          * @param network The {@link Network} whose capabilities have changed.
-         * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this
+         * @param networkCapabilities The new {@link NetworkCapabilities} for this
          *                            network.
          */
         public void onCapabilitiesChanged(@NonNull Network network,
@@ -3449,6 +3506,7 @@
         public void onBlockedStatusChanged(@NonNull Network network, boolean blocked) {}
 
         private NetworkRequest networkRequest;
+        private final int mFlags;
     }
 
     /**
@@ -3638,14 +3696,15 @@
                 }
                 Messenger messenger = new Messenger(handler);
                 Binder binder = new Binder();
+                final int callbackFlags = callback.mFlags;
                 if (reqType == LISTEN) {
                     request = mService.listenForNetwork(
-                            need, messenger, binder, callingPackageName,
+                            need, messenger, binder, callbackFlags, callingPackageName,
                             getAttributionTag());
                 } else {
                     request = mService.requestNetwork(
                             need, reqType.ordinal(), messenger, timeoutMs, binder, legacyType,
-                            callingPackageName, getAttributionTag());
+                            callbackFlags, callingPackageName, getAttributionTag());
                 }
                 if (request != null) {
                     sCallbacks.put(request, callback);
@@ -3692,7 +3751,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+     * Request a network to satisfy a set of {@link NetworkCapabilities}.
      *
      * <p>This method will attempt to find the best network that matches the passed
      * {@link NetworkRequest}, and to bring up one that does if none currently satisfies the
@@ -3776,7 +3835,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+     * Request a network to satisfy a set of {@link NetworkCapabilities}.
      *
      * This method behaves identically to {@link #requestNetwork(NetworkRequest, NetworkCallback)}
      * but runs all the callbacks on the passed Handler.
@@ -3798,7 +3857,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
+     * Request a network to satisfy a set of {@link NetworkCapabilities}, limited
      * by a timeout.
      *
      * This function behaves identically to the non-timed-out version
@@ -3833,7 +3892,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
+     * Request a network to satisfy a set of {@link NetworkCapabilities}, limited
      * by a timeout.
      *
      * This method behaves identically to
@@ -3878,7 +3937,7 @@
 
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+     * Request a network to satisfy a set of {@link NetworkCapabilities}.
      *
      * This function behaves identically to the version that takes a NetworkCallback, but instead
      * of {@link NetworkCallback} a {@link PendingIntent} is used.  This means
@@ -4190,6 +4249,18 @@
     }
 
     /**
+     * @hide
+     */
+    // TODO: Make it public api.
+    @SuppressLint("ExecutorRegistration")
+    public void registerBestMatchingNetworkCallback(@NonNull NetworkRequest request,
+            @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
+        final NetworkCapabilities nc = request.networkCapabilities;
+        final CallbackHandler cbHandler = new CallbackHandler(handler);
+        sendRequestForNetwork(nc, networkCallback, 0, TRACK_BEST, TYPE_NONE, cbHandler);
+    }
+
+    /**
      * Requests bandwidth update for a given {@link Network} and returns whether the update request
      * is accepted by ConnectivityService. Once accepted, ConnectivityService will poll underlying
      * network connection for updated bandwidth information. The caller will be notified via
@@ -4898,7 +4969,7 @@
     }
 
     /**
-     * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, but
+     * Request a network to satisfy a set of {@link NetworkCapabilities}, but
      * does not cause any networks to retain the NET_CAPABILITY_FOREGROUND capability. This can
      * be used to request that the system provide a network without causing the network to be
      * in the foreground.
@@ -4979,10 +5050,10 @@
             NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
     })
     public void requestBackgroundNetwork(@NonNull NetworkRequest request,
-            @Nullable Handler handler, @NonNull NetworkCallback networkCallback) {
+            @NonNull Handler handler, @NonNull NetworkCallback networkCallback) {
         final NetworkCapabilities nc = request.networkCapabilities;
         sendRequestForNetwork(nc, networkCallback, 0, BACKGROUND_REQUEST,
-                TYPE_NONE, handler == null ? getDefaultHandler() : new CallbackHandler(handler));
+                TYPE_NONE, new CallbackHandler(handler));
     }
 
     /**
@@ -5040,4 +5111,21 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    // The first network ID of IPSec tunnel interface.
+    private static final int TUN_INTF_NETID_START = 0xFC00;
+    // The network ID range of IPSec tunnel interface.
+    private static final int TUN_INTF_NETID_RANGE = 0x0400;
+
+    /**
+     * Get the network ID range reserved for IPSec tunnel interfaces.
+     *
+     * @return A Range which indicates the network ID range of IPSec tunnel interface.
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    @NonNull
+    public static Range<Integer> getIpSecNetIdRange() {
+        return new Range(TUN_INTF_NETID_START, TUN_INTF_NETID_START + TUN_INTF_NETID_RANGE - 1);
+    }
 }
diff --git a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
index cd49258..f9393e3 100644
--- a/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
+++ b/packages/Connectivity/framework/src/android/net/IConnectivityManager.aidl
@@ -143,7 +143,7 @@
 
     NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, int reqType,
             in Messenger messenger, int timeoutSec, in IBinder binder, int legacy,
-            String callingPackageName, String callingAttributionTag);
+            int callbackFlags, String callingPackageName, String callingAttributionTag);
 
     NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
             in PendingIntent operation, String callingPackageName, String callingAttributionTag);
@@ -151,7 +151,7 @@
     void releasePendingNetworkRequest(in PendingIntent operation);
 
     NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
-            in Messenger messenger, in IBinder binder, String callingPackageName,
+            in Messenger messenger, in IBinder binder, int callbackFlags, String callingPackageName,
             String callingAttributionTag);
 
     void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index c82cd3b..058f3c9 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -25,6 +25,7 @@
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.net.ConnectivityManager.NetworkCallback;
+import android.net.wifi.WifiNetworkSuggestion;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -131,6 +132,7 @@
         mPrivateDnsBroken = false;
         mRequestorUid = Process.INVALID_UID;
         mRequestorPackageName = null;
+        mSubIds = new ArraySet<>();
     }
 
     /**
@@ -159,6 +161,7 @@
         mPrivateDnsBroken = nc.mPrivateDnsBroken;
         mRequestorUid = nc.mRequestorUid;
         mRequestorPackageName = nc.mRequestorPackageName;
+        mSubIds = new ArraySet<>(nc.mSubIds);
     }
 
     /**
@@ -1048,6 +1051,16 @@
      *
      * Instances of NetworkCapabilities sent to apps without the appropriate permissions will have
      * this field cleared out.
+     *
+     * <p>
+     * This field will only be populated for VPN and wifi network suggestor apps (i.e using
+     * {@link WifiNetworkSuggestion}), and only for the network they own.
+     * In the case of wifi network suggestors apps, this field is also location sensitive, so the
+     * app needs to hold {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission. If the
+     * app targets SDK version greater than or equal to {@link Build.VERSION_CODES#S}, then they
+     * also need to use {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} to get the info in their
+     * callback. The app will be blamed for location access if this field is included.
+     * </p>
      */
     public int getOwnerUid() {
         return mOwnerUid;
@@ -1655,6 +1668,7 @@
         combineSSIDs(nc);
         combineRequestor(nc);
         combineAdministratorUids(nc);
+        combineSubIds(nc);
     }
 
     /**
@@ -1674,8 +1688,9 @@
                 && satisfiedBySpecifier(nc)
                 && (onlyImmutable || satisfiedBySignalStrength(nc))
                 && (onlyImmutable || satisfiedByUids(nc))
-                && (onlyImmutable || satisfiedBySSID(nc)))
-                && (onlyImmutable || satisfiedByRequestor(nc));
+                && (onlyImmutable || satisfiedBySSID(nc))
+                && (onlyImmutable || satisfiedByRequestor(nc))
+                && (onlyImmutable || satisfiedBySubIds(nc)));
     }
 
     /**
@@ -1771,7 +1786,8 @@
                 && equalsOwnerUid(that)
                 && equalsPrivateDnsBroken(that)
                 && equalsRequestor(that)
-                && equalsAdministratorUids(that);
+                && equalsAdministratorUids(that)
+                && equalsSubIds(that);
     }
 
     @Override
@@ -1793,7 +1809,8 @@
                 + Objects.hashCode(mPrivateDnsBroken) * 47
                 + Objects.hashCode(mRequestorUid) * 53
                 + Objects.hashCode(mRequestorPackageName) * 59
-                + Arrays.hashCode(mAdministratorUids) * 61;
+                + Arrays.hashCode(mAdministratorUids) * 61
+                + Objects.hashCode(mSubIds) * 67;
     }
 
     @Override
@@ -1827,6 +1844,7 @@
         dest.writeInt(mOwnerUid);
         dest.writeInt(mRequestorUid);
         dest.writeString(mRequestorPackageName);
+        dest.writeIntArray(CollectionUtils.toIntArray(mSubIds));
     }
 
     public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1850,6 +1868,11 @@
                 netCap.mOwnerUid = in.readInt();
                 netCap.mRequestorUid = in.readInt();
                 netCap.mRequestorPackageName = in.readString();
+                netCap.mSubIds = new ArraySet<>();
+                final int[] subIdInts = Objects.requireNonNull(in.createIntArray());
+                for (int i = 0; i < subIdInts.length; i++) {
+                    netCap.mSubIds.add(subIdInts[i]);
+                }
                 return netCap;
             }
             @Override
@@ -1933,11 +1956,14 @@
             sb.append(" SSID: ").append(mSSID);
         }
 
-
         if (mPrivateDnsBroken) {
             sb.append(" PrivateDnsBroken");
         }
 
+        if (!mSubIds.isEmpty()) {
+            sb.append(" SubscriptionIds: ").append(mSubIds);
+        }
+
         sb.append("]");
         return sb.toString();
     }
@@ -2251,6 +2277,67 @@
     }
 
     /**
+     * Set of the subscription IDs that identifies the network or request, empty if none.
+     */
+    @NonNull
+    private ArraySet<Integer> mSubIds = new ArraySet<>();
+
+    /**
+     * Sets the subscription ID set that associated to this network or request.
+     *
+     * @hide
+     */
+    @NonNull
+    public NetworkCapabilities setSubIds(@NonNull Set<Integer> subIds) {
+        mSubIds = new ArraySet(Objects.requireNonNull(subIds));
+        return this;
+    }
+
+    /**
+     * Gets the subscription ID set that associated to this network or request.
+     * @return
+     */
+    @NonNull
+    public Set<Integer> getSubIds() {
+        return new ArraySet<>(mSubIds);
+    }
+
+    /**
+     * Tests if the subscription ID set of this network is the same as that of the passed one.
+     */
+    private boolean equalsSubIds(@NonNull NetworkCapabilities nc) {
+        return Objects.equals(mSubIds, nc.mSubIds);
+    }
+
+    /**
+     * Check if the subscription ID set requirements of this object are matched by the passed one.
+     * If specified in the request, the passed one need to have at least one subId and at least
+     * one of them needs to be in the request set.
+     */
+    private boolean satisfiedBySubIds(@NonNull NetworkCapabilities nc) {
+        if (mSubIds.isEmpty()) return true;
+        if (nc.mSubIds.isEmpty()) return false;
+        for (final Integer subId : nc.mSubIds) {
+            if (mSubIds.contains(subId)) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Combine subscription ID set of the capabilities.
+     *
+     * <p>This is only legal if the subscription Ids are equal.
+     *
+     * <p>If both subscription IDs are not equal, they belong to different subscription
+     * (or no subscription). In this case, it would not make sense to add them together.
+     */
+    private void combineSubIds(@NonNull NetworkCapabilities nc) {
+        if (!Objects.equals(mSubIds, nc.mSubIds)) {
+            throw new IllegalStateException("Can't combine two subscription ID sets");
+        }
+    }
+
+    /**
      * Builder class for NetworkCapabilities.
      *
      * This class is mainly for for {@link NetworkAgent} instances to use. Many fields in
@@ -2556,6 +2643,18 @@
         }
 
         /**
+         * Set the subscription ID set.
+         *
+         * @param subIds a set that represent the subscription IDs. Empty if clean up.
+         * @return this builder.
+         */
+        @NonNull
+        public Builder setSubIds(@NonNull final Set<Integer> subIds) {
+            mCaps.setSubIds(subIds);
+            return this;
+        }
+
+        /**
          * Builds the instance of the capabilities.
          *
          * @return the built instance of NetworkCapabilities.
diff --git a/packages/Connectivity/framework/src/android/net/NetworkInfo.java b/packages/Connectivity/framework/src/android/net/NetworkInfo.java
index d752901..bb23494 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkInfo.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkInfo.java
@@ -21,7 +21,6 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Annotation.NetworkType;
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -164,7 +163,7 @@
      * @param typeName a human-readable string for the network type, or an empty string or null.
      * @param subtypeName a human-readable string for the subtype, or an empty string or null.
      */
-    public NetworkInfo(int type, @NetworkType int subtype,
+    public NetworkInfo(int type, int subtype,
             @Nullable String typeName, @Nullable String subtypeName) {
         if (!ConnectivityManager.isNetworkTypeValid(type)
                 && type != ConnectivityManager.TYPE_NONE) {
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index 17a8ee1..3fd95ee 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -114,6 +114,10 @@
      *       for the network (if any) that satisfies the default Internet
      *       request.
      *
+     *     - TRACK_BEST, which causes the framework to send callbacks about
+     *       the single, highest scoring current network (if any) that matches
+     *       the specified NetworkCapabilities.
+     *
      *     - BACKGROUND_REQUEST, like REQUEST but does not cause any networks
      *       to retain the NET_CAPABILITY_FOREGROUND capability. A network with
      *       no foreground requests is in the background. A network that has
@@ -136,6 +140,7 @@
         REQUEST,
         BACKGROUND_REQUEST,
         TRACK_SYSTEM_DEFAULT,
+        TRACK_BEST,
     };
 
     /**
@@ -456,6 +461,21 @@
             }
             nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         }
+
+        /**
+         * Sets the optional subscription ID set.
+         * <p>
+         * This specify the subscription IDs requirement.
+         * A network will satisfy this request only if it matches one of the subIds in this set.
+         * An empty set matches all networks, including those without a subId.
+         *
+         * @param subIds A {@code Set} that represents subscription IDs.
+         */
+        @NonNull
+        public Builder setSubIds(@NonNull Set<Integer> subIds) {
+            mNetworkCapabilities.setSubIds(subIds);
+            return this;
+        }
     }
 
     // implement the Parcelable interface
diff --git a/packages/Connectivity/framework/src/android/net/NetworkState.java b/packages/Connectivity/framework/src/android/net/NetworkState.java
index d010265..9b69674 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkState.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkState.java
@@ -22,7 +22,7 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Slog;
+import android.util.Log;
 
 /**
  * Snapshot of network state.
@@ -83,7 +83,7 @@
         if (VALIDATE_ROAMING_STATE && networkInfo != null && networkCapabilities != null) {
             if (networkInfo.isRoaming() == networkCapabilities
                     .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)) {
-                Slog.wtf("NetworkState", "Roaming state disagreement between " + networkInfo
+                Log.wtf("NetworkState", "Roaming state disagreement between " + networkInfo
                         + " and " + networkCapabilities);
             }
         }
diff --git a/core/java/android/net/ParseException.java b/packages/Connectivity/framework/src/android/net/ParseException.java
similarity index 100%
rename from core/java/android/net/ParseException.java
rename to packages/Connectivity/framework/src/android/net/ParseException.java
diff --git a/packages/Connectivity/framework/src/android/net/QosSocketInfo.java b/packages/Connectivity/framework/src/android/net/QosSocketInfo.java
index d37c469..53d9669 100644
--- a/packages/Connectivity/framework/src/android/net/QosSocketInfo.java
+++ b/packages/Connectivity/framework/src/android/net/QosSocketInfo.java
@@ -92,7 +92,7 @@
         Objects.requireNonNull(socket, "socket cannot be null");
 
         mNetwork = Objects.requireNonNull(network, "network cannot be null");
-        mParcelFileDescriptor = ParcelFileDescriptor.dup(socket.getFileDescriptor$());
+        mParcelFileDescriptor = ParcelFileDescriptor.fromSocket(socket);
         mLocalSocketAddress =
                 new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort());
     }
@@ -114,10 +114,10 @@
         try {
             return new InetSocketAddress(InetAddress.getByAddress(address), port);
         } catch (final UnknownHostException e) {
-            /* The catch block was purposely left empty.  UnknownHostException will never be thrown
+            /* This can never happen. UnknownHostException will never be thrown
                since the address provided is numeric and non-null. */
+            throw new RuntimeException("UnknownHostException on numeric address", e);
         }
-        return new InetSocketAddress();
     }
 
     @Override
diff --git a/packages/Connectivity/service/Android.bp b/packages/Connectivity/service/Android.bp
index 856f3b8..f630cea 100644
--- a/packages/Connectivity/service/Android.bp
+++ b/packages/Connectivity/service/Android.bp
@@ -60,6 +60,7 @@
         "services.core",
         "services.net",
         "unsupportedappusage",
+        "ServiceConnectivityResources",
     ],
     static_libs: [
         "modules-utils-os",
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/Android.bp b/packages/Connectivity/service/ServiceConnectivityResources/Android.bp
new file mode 100644
index 0000000..f2446b7
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// APK to hold all the wifi overlayable resources.
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_app {
+    name: "ServiceConnectivityResources",
+    sdk_version: "system_current",
+    resource_dirs: [
+        "res",
+    ],
+    privileged: true,
+    export_package_resources: true,
+    apex_available: [
+        "com.android.tethering",
+    ],
+    // TODO: use a dedicated cert once generated
+    certificate: "platform",
+}
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/AndroidManifest.xml b/packages/Connectivity/service/ServiceConnectivityResources/AndroidManifest.xml
new file mode 100644
index 0000000..2c30302
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<!-- Manifest for connectivity resources APK -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.connectivity.resources"
+          coreApp="true"
+          android:versionCode="1"
+          android:versionName="S-initial">
+    <application
+        android:label="@string/connectivityResourcesAppLabel"
+        android:defaultToDeviceProtectedStorage="true"
+        android:directBootAware="true">
+        <!-- This is only used to identify this app by resolving the action.
+             The activity is never actually triggered. -->
+        <activity android:name="android.app.Activity" android:exported="true" android:enabled="true">
+            <intent-filter>
+                <action android:name="com.android.server.connectivity.intent.action.SERVICE_CONNECTIVITY_RESOURCES_APK" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
new file mode 100644
index 0000000..7d98c76
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!-- Configuration values for ConnectivityService
+     DO NOT EDIT THIS FILE for specific device configuration; instead, use a Runtime Resources
+     Overlay package following the overlayable.xml configuration in the same directory:
+     https://source.android.com/devices/architecture/rros -->
+<resources>
+
+    <!-- Configuration hook for the URL returned by ConnectivityManager#getCaptivePortalServerUrl.
+         If empty, the returned value is controlled by Settings.Global.CAPTIVE_PORTAL_HTTP_URL,
+         and if that value is empty, the framework will use a hard-coded default.
+         This is *NOT* a URL that will always be used by the system network validation to detect
+         captive portals: NetworkMonitor may use different strategies and will not necessarily use
+         this URL. NetworkMonitor behaviour should be configured with NetworkStack resource overlays
+         instead. -->
+    <!--suppress CheckTagEmptyBody -->
+    <string translatable="false" name="config_networkCaptivePortalServerUrl"></string>
+
+    <!-- The maximum duration (in milliseconds) we expect a network transition to take -->
+    <integer name="config_networkTransitionTimeout">60000</integer>
+
+    <!-- Configuration of network interfaces that support WakeOnLAN -->
+    <string-array translatable="false" name="config_wakeonlan_supported_interfaces">
+        <!--
+        <item>wlan0</item>
+        <item>eth0</item>
+        -->
+    </string-array>
+
+</resources>
\ No newline at end of file
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
new file mode 100644
index 0000000..00ec2df
--- /dev/null
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <overlayable name="ServiceConnectivityResourcesConfig">
+        <policy type="product|system|vendor">
+            <!-- Configuration values for ConnectivityService -->
+            <item type="string" name="config_networkCaptivePortalServerUrl"/>
+            <item type="integer" name="config_networkTransitionTimeout"/>
+            <item type="array" name="config_wakeonlan_supported_interfaces"/>
+
+
+        </policy>
+    </overlayable>
+</resources>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/strings.xml
similarity index 62%
copy from packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
copy to packages/Connectivity/service/ServiceConnectivityResources/res/values/strings.xml
index f32faa0..2c7b992 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/strings.xml
@@ -14,9 +14,9 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsAnimationViewFpmOther
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsAnimationViewFpmOther>
+<resources>
+    <!-- The System Connectivity Resources package is an internal system package that provides
+         configuration values for system networking that were pre-configured in the device. This
+         is the name of the package to display in the list of system apps. [CHAR LIMIT=40] -->
+    <string name="connectivityResourcesAppLabel">System Connectivity Resources</string>
+</resources>
\ No newline at end of file
diff --git a/packages/CtsShim/apk/arm/CtsShim.apk b/packages/CtsShim/apk/arm/CtsShim.apk
index 180dfb6..784a747 100644
--- a/packages/CtsShim/apk/arm/CtsShim.apk
+++ b/packages/CtsShim/apk/arm/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/arm/CtsShimPriv.apk b/packages/CtsShim/apk/arm/CtsShimPriv.apk
index d87ea7f..5b7bda4 100644
--- a/packages/CtsShim/apk/arm/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/arm/CtsShimPriv.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShim.apk b/packages/CtsShim/apk/x86/CtsShim.apk
index 180dfb6..784a747 100644
--- a/packages/CtsShim/apk/x86/CtsShim.apk
+++ b/packages/CtsShim/apk/x86/CtsShim.apk
Binary files differ
diff --git a/packages/CtsShim/apk/x86/CtsShimPriv.apk b/packages/CtsShim/apk/x86/CtsShimPriv.apk
index 3124651..780cb8a 100644
--- a/packages/CtsShim/apk/x86/CtsShimPriv.apk
+++ b/packages/CtsShim/apk/x86/CtsShimPriv.apk
Binary files differ
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index c1dca5d..16a946d 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -138,9 +138,6 @@
     private long mCurrentPartitionSize;
     private long mCurrentPartitionInstalledSize;
 
-    private boolean mJustCancelledByUser;
-    private boolean mKeepNotification;
-
     // This is for testing only now
     private boolean mEnableWhenCompleted;
 
@@ -174,11 +171,6 @@
         if (cache != null) {
             cache.flush();
         }
-
-        if (!mKeepNotification) {
-            // Cancel the persistent notification.
-            mNM.cancel(NOTIFICATION_ID);
-        }
     }
 
     @Override
@@ -231,9 +223,11 @@
             return;
         }
 
+        boolean removeNotification = false;
         switch (result) {
             case RESULT_CANCELLED:
                 postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
+                removeNotification = true;
                 break;
 
             case RESULT_ERROR_IO:
@@ -251,7 +245,7 @@
         }
 
         // if it's not successful, reset the task and stop self.
-        resetTaskAndStop();
+        resetTaskAndStop(removeNotification);
     }
 
     private void executeInstallCommand(Intent intent) {
@@ -302,12 +296,12 @@
             return;
         }
 
-        stopForeground(true);
-        mJustCancelledByUser = true;
-
         if (mInstallTask.cancel(false)) {
-            // Will stopSelf() in onResult()
+            // onResult() would call resetTaskAndStop() upon task completion.
             Log.d(TAG, "Cancel request filed successfully");
+            // Dismiss the notification as soon as possible as DynamicSystemManager.remove() may
+            // block.
+            stopForeground(STOP_FOREGROUND_REMOVE);
         } else {
             Log.e(TAG, "Trying to cancel installation while it's already completed.");
         }
@@ -322,8 +316,7 @@
         if (!isDynamicSystemInstalled() && (getStatus() != STATUS_READY)) {
             Log.e(TAG, "Trying to discard AOT while there is no complete installation");
             // Stop foreground state and dismiss stale notification.
-            stopForeground(STOP_FOREGROUND_REMOVE);
-            resetTaskAndStop();
+            resetTaskAndStop(true);
             return;
         }
 
@@ -331,8 +324,8 @@
                 getString(R.string.toast_dynsystem_discarded),
                 Toast.LENGTH_LONG).show();
 
-        resetTaskAndStop();
         postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
+        resetTaskAndStop(true);
 
         mDynSystem.remove();
     }
@@ -412,12 +405,13 @@
     }
 
     private void resetTaskAndStop() {
-        mInstallTask = null;
+        resetTaskAndStop(/* removeNotification= */ false);
+    }
 
-        new Handler().postDelayed(() -> {
-            stopForeground(STOP_FOREGROUND_DETACH);
-            stopSelf();
-        }, 50);
+    private void resetTaskAndStop(boolean removeNotification) {
+        mInstallTask = null;
+        stopForeground(removeNotification ? STOP_FOREGROUND_REMOVE : STOP_FOREGROUND_DETACH);
+        stopSelf();
     }
 
     private void prepareNotification() {
@@ -525,7 +519,7 @@
     private void postStatus(int status, int cause, Throwable detail) {
         String statusString;
         String causeString;
-        mKeepNotification = false;
+        boolean notifyOnNotificationBar = true;
 
         switch (status) {
             case STATUS_NOT_STARTED:
@@ -551,18 +545,16 @@
                 break;
             case CAUSE_INSTALL_CANCELLED:
                 causeString = "INSTALL_CANCELLED";
+                notifyOnNotificationBar = false;
                 break;
             case CAUSE_ERROR_IO:
                 causeString = "ERROR_IO";
-                mKeepNotification = true;
                 break;
             case CAUSE_ERROR_INVALID_URL:
                 causeString = "ERROR_INVALID_URL";
-                mKeepNotification = true;
                 break;
             case CAUSE_ERROR_EXCEPTION:
                 causeString = "ERROR_EXCEPTION";
-                mKeepNotification = true;
                 break;
             default:
                 causeString = "CAUSE_NOT_SPECIFIED";
@@ -571,16 +563,6 @@
 
         Log.d(TAG, "status=" + statusString + ", cause=" + causeString + ", detail=" + detail);
 
-        boolean notifyOnNotificationBar = true;
-
-        if (status == STATUS_NOT_STARTED
-                && cause == CAUSE_INSTALL_CANCELLED
-                && mJustCancelledByUser) {
-            // if task is cancelled by user, do not notify them
-            notifyOnNotificationBar = false;
-            mJustCancelledByUser = false;
-        }
-
         if (notifyOnNotificationBar) {
             mNM.notify(NOTIFICATION_ID, buildNotification(status, cause, detail));
         }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index a28a1e3..b4194fd 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -426,6 +426,9 @@
     <!-- Permission required for CTS test - ClipboardManagerTest -->
     <uses-permission android:name="android.permission.SET_CLIP_SOURCE" />
 
+    <!-- Permission required for CTS test - FontManagerTest -->
+    <uses-permission android:name="android.permission.UPDATE_FONTS" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index 4fc1973..0d348e2 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -44,7 +44,7 @@
     /**
      * Returns true if the FalsingManager thinks the last gesure was not a valid tap.
      *
-     * Accepts one parameter, robustCheck, that distinctly changes behavior. When set to false,
+     * The first parameter, robustCheck, distinctly changes behavior. When set to false,
      * this method simply looks at the last gesture and returns whether it is a tap or not, (as
      * opposed to a swipe or other non-tap gesture). When set to true, a more thorough analysis
      * is performed that can include historical interactions and other contextual cues to see
@@ -53,8 +53,11 @@
      * Set robustCheck to true if you want to validate a tap for launching an action, like opening
      * a notification. Set to false if you simply want to know if the last gesture looked like a
      * tap.
+     *
+     * The second parameter, falsePenalty, indicates how much this should affect future gesture
+     * classifications if this tap looks like a false.
      */
-    boolean isFalseTap(boolean robustCheck);
+    boolean isFalseTap(boolean robustCheck, double falsePenalty);
 
     /**
      * Returns true if the last two gestures do not look like a double tap.
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
index 53f7e44..ca13204 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
@@ -50,4 +50,8 @@
     public abstract void onStateChanged(State state);
 
     public abstract int getDetailY();
+
+    public View getLabelContainer() {
+        return null;
+    }
 }
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml b/packages/SystemUI/res/drawable/qs_tile_background.xml
similarity index 66%
copy from packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
copy to packages/SystemUI/res/drawable/qs_tile_background.xml
index f32faa0..265f575 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
+++ b/packages/SystemUI/res/drawable/qs_tile_background.xml
@@ -14,9 +14,10 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsAnimationViewFpmOther
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsAnimationViewFpmOther>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item android:id="@android:id/mask"
+        android:drawable="@drawable/qs_tile_background_shape" />
+    <item android:id="@id/background"
+        android:drawable="@drawable/qs_tile_background_shape"/>
+</ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml b/packages/SystemUI/res/drawable/qs_tile_background_shape.xml
similarity index 68%
copy from packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
copy to packages/SystemUI/res/drawable/qs_tile_background_shape.xml
index f32faa0..f6b6834 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
+++ b/packages/SystemUI/res/drawable/qs_tile_background_shape.xml
@@ -14,9 +14,8 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsAnimationViewFpmOther
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsAnimationViewFpmOther>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <corners android:radius="@dimen/qs_corner_radius" />
+    <solid android:color="#FFFFFF" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml b/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
deleted file mode 100644
index 644d1ada..0000000
--- a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<com.android.systemui.biometrics.UdfpsAnimationViewKeyguard
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/udfps_animation_view"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsAnimationViewKeyguard>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml b/packages/SystemUI/res/layout/udfps_bp_view.xml
similarity index 88%
rename from packages/SystemUI/res/layout/udfps_animation_view_bp.xml
rename to packages/SystemUI/res/layout/udfps_bp_view.xml
index 0cfbf2e..f1c55ef 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml
+++ b/packages/SystemUI/res/layout/udfps_bp_view.xml
@@ -14,9 +14,9 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsAnimationViewBp
+<com.android.systemui.biometrics.UdfpsBpView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/udfps_animation_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsAnimationViewBp>
+</com.android.systemui.biometrics.UdfpsBpView>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml b/packages/SystemUI/res/layout/udfps_enroll_view.xml
similarity index 79%
rename from packages/SystemUI/res/layout/udfps_animation_view_enroll.xml
rename to packages/SystemUI/res/layout/udfps_enroll_view.xml
index 9b5752d..4035305 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml
+++ b/packages/SystemUI/res/layout/udfps_enroll_view.xml
@@ -14,13 +14,13 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsAnimationViewEnroll
+<com.android.systemui.biometrics.UdfpsEnrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/udfps_animation_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <!-- Enrollment progress bar-->
+    <!-- Enrollment progress bar -->
     <com.android.systemui.biometrics.UdfpsProgressBar
         android:id="@+id/progress_bar"
         android:layout_width="match_parent"
@@ -31,4 +31,9 @@
         android:layout_gravity="center"
         android:visibility="gone"/>
 
-</com.android.systemui.biometrics.UdfpsAnimationViewEnroll>
+    <!-- Fingerprint -->
+    <ImageView
+        android:id="@+id/udfps_enroll_animation_fp_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+</com.android.systemui.biometrics.UdfpsEnrollView>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml b/packages/SystemUI/res/layout/udfps_fpm_other_view.xml
similarity index 74%
rename from packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
rename to packages/SystemUI/res/layout/udfps_fpm_other_view.xml
index f32faa0..6ecbb47 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
+++ b/packages/SystemUI/res/layout/udfps_fpm_other_view.xml
@@ -14,9 +14,15 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsAnimationViewFpmOther
+<com.android.systemui.biometrics.UdfpsFpmOtherView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/udfps_animation_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsAnimationViewFpmOther>
+
+    <!-- Fingerprint -->
+    <ImageView
+        android:id="@+id/udfps_fpm_other_fp_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+</com.android.systemui.biometrics.UdfpsFpmOtherView>
diff --git a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml b/packages/SystemUI/res/layout/udfps_keyguard_view.xml
similarity index 71%
copy from packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
copy to packages/SystemUI/res/layout/udfps_keyguard_view.xml
index f32faa0..0199ccb 100644
--- a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml
+++ b/packages/SystemUI/res/layout/udfps_keyguard_view.xml
@@ -14,9 +14,17 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.biometrics.UdfpsAnimationViewFpmOther
+<com.android.systemui.biometrics.UdfpsKeyguardView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/udfps_animation_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
-</com.android.systemui.biometrics.UdfpsAnimationViewFpmOther>
+
+    <!-- TODO: add background protection -->
+
+    <!-- Fingerprint -->
+    <ImageView
+        android:id="@+id/udfps_keyguard_animation_fp_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+</com.android.systemui.biometrics.UdfpsKeyguardView>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index e24c9e9..50b2f20 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -22,6 +22,11 @@
     android:layout_height="match_parent"
     systemui:sensorTouchAreaCoefficient="0.5">
 
+    <ViewStub
+        android:id="@+id/animation_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
     <com.android.systemui.biometrics.UdfpsSurfaceView
         android:id="@+id/hbm_view"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 885cd254..2062104 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -512,6 +512,7 @@
     <!-- The size of the gesture span needed to activate the "pull" notification expansion -->
     <dimen name="pull_span_min">25dp</dimen>
 
+    <dimen name="qs_corner_radius">14dp</dimen>
     <dimen name="qs_tile_height">96dp</dimen>
     <!--notification_side_paddings + notification_content_margin_start - (qs_quick_tile_size - qs_tile_background_size) / 2 -->
     <dimen name="qs_tile_layout_margin_side">18dp</dimen>
@@ -528,6 +529,8 @@
     <dimen name="qs_tile_margin_top">0dp</dimen>
     <dimen name="qs_tile_icon_background_stroke_width">-1dp</dimen>
     <dimen name="qs_tile_background_size">44dp</dimen>
+    <dimen name="qs_icon_size">20dp</dimen>
+    <dimen name="qs_label_container_margin">10dp</dimen>
     <dimen name="qs_quick_tile_size">48dp</dimen>
     <dimen name="qs_quick_tile_padding">12dp</dimen>
     <dimen name="qs_header_gear_translation">16dp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index ebb6e30..e9e9b24 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -289,15 +289,19 @@
     /**
      * Returns the visible width to height ratio. Returns 0f if snapshot data is not available.
      */
-    public float getVisibleThumbnailRatio() {
+    public float getVisibleThumbnailRatio(boolean clipInsets) {
         if (lastSnapshotData.taskSize == null || lastSnapshotData.contentInsets == null) {
             return 0f;
         }
 
-        float availableWidth = lastSnapshotData.taskSize.x - (lastSnapshotData.contentInsets.left
-                + lastSnapshotData.contentInsets.right);
-        float availableHeight = lastSnapshotData.taskSize.y - (lastSnapshotData.contentInsets.top
-                + lastSnapshotData.contentInsets.bottom);
+        float availableWidth = lastSnapshotData.taskSize.x;
+        float availableHeight = lastSnapshotData.taskSize.y;
+        if (clipInsets) {
+            availableWidth -=
+                    (lastSnapshotData.contentInsets.left + lastSnapshotData.contentInsets.right);
+            availableHeight -=
+                    (lastSnapshotData.contentInsets.top + lastSnapshotData.contentInsets.bottom);
+        }
         return availableWidth / availableHeight;
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index af7c5da..c2d52a7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -16,11 +16,11 @@
 
 package com.android.systemui.shared.system;
 
-import android.window.TaskSnapshot;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.IRecentsAnimationController;
+import android.window.TaskSnapshot;
 
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
@@ -141,9 +141,9 @@
     /**
      * @see IRecentsAnimationController#detachNavigationBarFromApp
      */
-    public void detachNavigationBarFromApp() {
+    public void detachNavigationBarFromApp(boolean moveHomeToTop) {
         try {
-            mAnimationController.detachNavigationBarFromApp();
+            mAnimationController.detachNavigationBarFromApp(moveHomeToTop);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to detach the navigation bar from app", e);
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index 1569fff..3f0e3eb 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -45,6 +45,7 @@
     private int mLockScreenColor;
 
     private boolean mIsDozing;
+    private float mDozeAmount;
     private Locale mLocale;
 
     private final NumberFormat mBurmeseNf = NumberFormat.getInstance(Locale.forLanguageTag("my"));
@@ -59,6 +60,7 @@
         super(view);
         mStatusBarStateController = statusBarStateController;
         mIsDozing = mStatusBarStateController.isDozing();
+        mDozeAmount = mStatusBarStateController.getDozeAmount();
         mBroadcastDispatcher = broadcastDispatcher;
 
         mBurmeseNumerals = mBurmeseNf.format(FORMAT_NUMBER);
@@ -82,6 +84,7 @@
                 new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
         mStatusBarStateController.addCallback(mStatusBarStateListener);
         mIsDozing = mStatusBarStateController.isDozing();
+        mDozeAmount = mStatusBarStateController.getDozeAmount();
         refreshTime();
         initColors();
     }
@@ -136,9 +139,15 @@
     private final StatusBarStateController.StateListener mStatusBarStateListener =
             new StatusBarStateController.StateListener() {
                 @Override
-                public void onDozingChanged(boolean isDozing) {
-                    mIsDozing = isDozing;
-                    mView.animateDoze(mIsDozing, true);
+                public void onDozeAmountChanged(float linear, float eased) {
+                    boolean noAnimation = (mDozeAmount == 0f && linear == 1f)
+                            || (mDozeAmount == 1f && linear == 0f);
+                    boolean isDozing = linear > mDozeAmount;
+                    mDozeAmount = linear;
+                    if (mIsDozing != isDozing) {
+                        mIsDozing = isDozing;
+                        mView.animateDoze(mIsDozing, !noAnimation);
+                    }
                 }
             };
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 5760565..a580663 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -35,12 +35,15 @@
 import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingClassifier;
+import com.android.systemui.classifier.FalsingCollector;
 
 public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKeyInputView>
         extends KeyguardInputViewController<T> {
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final LockPatternUtils mLockPatternUtils;
     private final LatencyTracker mLatencyTracker;
+    private final FalsingCollector mFalsingCollector;
     private CountDownTimer mCountdownTimer;
     protected KeyguardMessageAreaController mMessageAreaController;
     private boolean mDismissing;
@@ -70,11 +73,12 @@
             LockPatternUtils lockPatternUtils,
             KeyguardSecurityCallback keyguardSecurityCallback,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
-            LatencyTracker latencyTracker) {
+            LatencyTracker latencyTracker, FalsingCollector falsingCollector) {
         super(view, securityMode, keyguardSecurityCallback);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mLockPatternUtils = lockPatternUtils;
         mLatencyTracker = latencyTracker;
+        mFalsingCollector = falsingCollector;
         KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
         mMessageAreaController = messageAreaControllerFactory.create(kma);
     }
@@ -256,6 +260,7 @@
     }
 
     protected void onUserInput() {
+        mFalsingCollector.updateFalseConfidence(FalsingClassifier.Result.passed(0.6));
         getKeyguardSecurityCallback().userActivity();
         getKeyguardSecurityCallback().onUserInput();
         mMessageAreaController.setMessage("");
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
index 5e02e04..de64f07 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
@@ -26,16 +26,11 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.jank.InteractionJankMonitor;
-import com.android.systemui.Gefingerpoken;
-
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * A Base class for all Keyguard password/pattern/pin related inputs.
  */
 public abstract class KeyguardInputView extends LinearLayout {
-    private final List<Gefingerpoken> mMotionEventListener = new ArrayList<>();
 
     public KeyguardInputView(Context context) {
         super(context);
@@ -53,7 +48,6 @@
     abstract CharSequence getTitle();
 
     void animateForIme(float interpolatedFraction) {
-        return;
     }
 
     boolean disallowInterceptTouch(MotionEvent event) {
@@ -66,27 +60,6 @@
         return false;
     }
 
-    void addMotionEventListener(Gefingerpoken listener) {
-        mMotionEventListener.add(listener);
-    }
-
-    void removeMotionEventListener(Gefingerpoken listener) {
-        mMotionEventListener.remove(listener);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        return mMotionEventListener.stream().anyMatch(listener -> listener.onTouchEvent(event))
-                || super.onTouchEvent(event);
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent event) {
-        return mMotionEventListener.stream().anyMatch(
-                listener -> listener.onInterceptTouchEvent(event))
-                || super.onInterceptTouchEvent(event);
-    }
-
     protected AnimatorListenerAdapter getAnimationListener(int cuj) {
         return new AnimatorListenerAdapter() {
             private boolean mIsCancel;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 957882d..05f33a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -155,8 +155,8 @@
         private final InputMethodManager mInputMethodManager;
         private final DelayableExecutor mMainExecutor;
         private final Resources mResources;
-        private LiftToActivateListener mLiftToActivateListener;
-        private TelephonyManager mTelephonyManager;
+        private final LiftToActivateListener mLiftToActivateListener;
+        private final TelephonyManager mTelephonyManager;
         private final FalsingCollector mFalsingCollector;
         private final boolean mIsNewLayoutEnabled;
 
@@ -167,8 +167,7 @@
                 KeyguardMessageAreaController.Factory messageAreaControllerFactory,
                 InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor,
                 @Main Resources resources, LiftToActivateListener liftToActivateListener,
-                TelephonyManager telephonyManager,
-                FalsingCollector falsingCollector,
+                TelephonyManager telephonyManager, FalsingCollector falsingCollector,
                 FeatureFlags featureFlags) {
             mKeyguardUpdateMonitor = keyguardUpdateMonitor;
             mLockPatternUtils = lockPatternUtils;
@@ -189,12 +188,13 @@
             if (keyguardInputView instanceof KeyguardPatternView) {
                 return new KeyguardPatternViewController((KeyguardPatternView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
-                        keyguardSecurityCallback, mLatencyTracker, mMessageAreaControllerFactory);
+                        keyguardSecurityCallback, mLatencyTracker, mFalsingCollector,
+                        mMessageAreaControllerFactory);
             } else if (keyguardInputView instanceof KeyguardPasswordView) {
                 return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
-                        mInputMethodManager, mMainExecutor, mResources);
+                        mInputMethodManager, mMainExecutor, mResources, mFalsingCollector);
             } else if (keyguardInputView instanceof KeyguardPINView) {
                 return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
@@ -204,14 +204,14 @@
                 return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
-                        mLiftToActivateListener, mTelephonyManager,
-                        mFalsingCollector, mIsNewLayoutEnabled);
+                        mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
+                        mIsNewLayoutEnabled);
             } else if (keyguardInputView instanceof KeyguardSimPukView) {
                 return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
-                        mLiftToActivateListener, mTelephonyManager,
-                        mFalsingCollector, mIsNewLayoutEnabled);
+                        mLiftToActivateListener, mTelephonyManager, mFalsingCollector,
+                        mIsNewLayoutEnabled);
             }
 
             throw new RuntimeException("Unable to find controller for " + keyguardInputView);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 0f1c3c8..57b8cf0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -40,6 +40,7 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
@@ -112,9 +113,10 @@
             LatencyTracker latencyTracker,
             InputMethodManager inputMethodManager,
             @Main DelayableExecutor mainExecutor,
-            @Main Resources resources) {
+            @Main Resources resources,
+            FalsingCollector falsingCollector) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
-                messageAreaControllerFactory, latencyTracker);
+                messageAreaControllerFactory, latencyTracker, falsingCollector);
         mKeyguardSecurityCallback = keyguardSecurityCallback;
         mInputMethodManager = inputMethodManager;
         mMainExecutor = mainExecutor;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 2aaf748..4f48bb4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -23,6 +23,7 @@
 import android.os.AsyncTask;
 import android.os.CountDownTimer;
 import android.os.SystemClock;
+import android.view.MotionEvent;
 import android.view.View;
 
 import com.android.internal.util.LatencyTracker;
@@ -35,6 +36,8 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingClassifier;
+import com.android.systemui.classifier.FalsingCollector;
 
 import java.util.List;
 
@@ -50,6 +53,7 @@
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final LockPatternUtils mLockPatternUtils;
     private final LatencyTracker mLatencyTracker;
+    private final FalsingCollector mFalsingCollector;
     private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
 
     private KeyguardMessageAreaController mMessageAreaController;
@@ -102,6 +106,11 @@
 
             final int userId = KeyguardUpdateMonitor.getCurrentUser();
             if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
+                // Treat single-sized patterns as erroneous taps.
+                if (pattern.size() == 1) {
+                    mFalsingCollector.updateFalseConfidence(FalsingClassifier.Result.falsed(
+                            0.7, "empty pattern input"));
+                }
                 mLockPatternView.enableInput();
                 onPatternChecked(userId, false, 0, false /* not valid - too short */);
                 return;
@@ -179,11 +188,13 @@
             LockPatternUtils lockPatternUtils,
             KeyguardSecurityCallback keyguardSecurityCallback,
             LatencyTracker latencyTracker,
+            FalsingCollector falsingCollector,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
         super(view, securityMode, keyguardSecurityCallback);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mLockPatternUtils = lockPatternUtils;
         mLatencyTracker = latencyTracker;
+        mFalsingCollector = falsingCollector;
         mMessageAreaControllerFactory = messageAreaControllerFactory;
         KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
         mMessageAreaController = mMessageAreaControllerFactory.create(kma);
@@ -205,6 +216,12 @@
                 KeyguardUpdateMonitor.getCurrentUser()));
         // vibrate mode will be the same for the life of this screen
         mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
+        mLockPatternView.setOnTouchListener((v, event) -> {
+            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                mFalsingCollector.avoidGesture();
+            }
+            return false;
+        });
 
         EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
         if (button != null) {
@@ -224,6 +241,7 @@
     protected void onViewDetached() {
         super.onViewDetached();
         mLockPatternView.setOnPatternListener(null);
+        mLockPatternView.setOnTouchListener(null);
         EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
         if (button != null) {
             button.setCallback(null);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 4e06491..09fb8ef 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -164,6 +164,10 @@
         reloadColors();
     }
 
+    NumPadKey[] getButtons() {
+        return mButtons;
+    }
+
     /**
      * By default, the new layout will be enabled. When false, revert to the old style.
      */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index f247948..b156f81 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -25,7 +25,6 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingCollector;
 
@@ -50,19 +49,6 @@
         return false;
     };
 
-    private final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
-        @Override
-        public boolean onInterceptTouchEvent(MotionEvent ev) {
-            mFalsingCollector.avoidGesture();
-            return false;
-        }
-
-        @Override
-        public boolean onTouchEvent(MotionEvent ev) {
-            return false;
-        }
-    };
-
     protected KeyguardPinBasedInputViewController(T view,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             SecurityMode securityMode,
@@ -73,7 +59,7 @@
             LiftToActivateListener liftToActivateListener,
             FalsingCollector falsingCollector) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
-                messageAreaControllerFactory, latencyTracker);
+                messageAreaControllerFactory, latencyTracker, falsingCollector);
         mLiftToActivateListener = liftToActivateListener;
         mFalsingCollector = falsingCollector;
         mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
@@ -83,8 +69,14 @@
     protected void onViewAttached() {
         super.onViewAttached();
 
-        mView.addMotionEventListener(mGlobalTouchListener);
-
+        for (NumPadKey button: mView.getButtons()) {
+            button.setOnTouchListener((v, event) -> {
+                if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                    mFalsingCollector.avoidGesture();
+                }
+                return false;
+            });
+        }
         mPasswordEntry.setOnKeyListener(mOnKeyListener);
         mPasswordEntry.setUserActivityListener(this::onUserInput);
 
@@ -123,7 +115,10 @@
     @Override
     protected void onViewDetached() {
         super.onViewDetached();
-        mView.removeMotionEventListener(mGlobalTouchListener);
+
+        for (NumPadKey button: mView.getButtons()) {
+            button.setOnTouchListener(null);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index a2d7707..eaf8516 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -58,10 +58,12 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.Gefingerpoken;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 
+import java.util.ArrayList;
 import java.util.List;
 
 public class KeyguardSecurityContainer extends FrameLayout {
@@ -97,6 +99,7 @@
     private final ViewConfiguration mViewConfiguration;
     private final SpringAnimation mSpringAnimation;
     private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
+    private final List<Gefingerpoken> mMotionEventListeners = new ArrayList<>();
 
     private float mLastTouchY = -1;
     private int mActivePointerId = -1;
@@ -388,6 +391,10 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
+        boolean result =  mMotionEventListeners.stream().anyMatch(
+                listener -> listener.onInterceptTouchEvent(event))
+                || super.onInterceptTouchEvent(event);
+
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
                 int pointerIndex = event.getActionIndex();
@@ -418,12 +425,17 @@
                 mIsDragging = false;
                 break;
         }
-        return false;
+        return result;
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         final int action = event.getActionMasked();
+
+        boolean result =  mMotionEventListeners.stream()
+                .anyMatch(listener -> listener.onTouchEvent(event))
+                || super.onTouchEvent(event);
+
         switch (action) {
             case MotionEvent.ACTION_MOVE:
                 mVelocityTracker.addMovement(event);
@@ -469,6 +481,14 @@
         return true;
     }
 
+    void addMotionEventListener(Gefingerpoken listener) {
+        mMotionEventListeners.add(listener);
+    }
+
+    void removeMotionEventListener(Gefingerpoken listener) {
+        mMotionEventListeners.remove(listener);
+    }
+
     private void handleTap(MotionEvent event) {
         // If we're using a fullscreen security mode, skip
         if (!mOneHandedMode) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index fdab8db..7eac903 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -33,6 +33,7 @@
 import android.os.UserHandle;
 import android.util.Log;
 import android.util.Slog;
+import android.view.MotionEvent;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
@@ -46,6 +47,8 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.keyguard.dagger.KeyguardBouncerScope;
 import com.android.settingslib.utils.ThreadUtils;
+import com.android.systemui.Gefingerpoken;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -71,9 +74,44 @@
     private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
     private final SecurityCallback mSecurityCallback;
     private final ConfigurationController mConfigurationController;
+    private final KeyguardViewController mKeyguardViewController;
+    private final FalsingManager mFalsingManager;
 
     private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid;
 
+    private final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
+        private MotionEvent mTouchDown;
+        @Override
+        public boolean onInterceptTouchEvent(MotionEvent ev) {
+            return false;
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent ev) {
+            // Do just a bit of our own falsing. People should only be tapping on the input, not
+            // swiping.
+            if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                if (mTouchDown != null) {
+                    mTouchDown.recycle();
+                    mTouchDown = null;
+                }
+                mTouchDown = MotionEvent.obtain(ev);
+            } else if (mTouchDown != null) {
+                boolean tapResult = mFalsingManager.isFalseTap(true, 0.6);
+                if (tapResult
+                        || ev.getActionMasked() == MotionEvent.ACTION_UP
+                        || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
+                    if (tapResult) {
+                        mKeyguardViewController.reset(true);
+                    }
+                    mTouchDown.recycle();
+                    mTouchDown = null;
+                }
+            }
+            return false;
+        }
+    };
+
     private KeyguardSecurityCallback mKeyguardSecurityCallback = new KeyguardSecurityCallback() {
         public void userActivity() {
             if (mSecurityCallback != null) {
@@ -169,7 +207,9 @@
             KeyguardStateController keyguardStateController,
             SecurityCallback securityCallback,
             KeyguardSecurityViewFlipperController securityViewFlipperController,
-            ConfigurationController configurationController) {
+            ConfigurationController configurationController,
+            KeyguardViewController keyguardViewController,
+            FalsingManager falsingManager) {
         super(view);
         mLockPatternUtils = lockPatternUtils;
         mUpdateMonitor = keyguardUpdateMonitor;
@@ -182,6 +222,8 @@
         mAdminSecondaryLockScreenController = adminSecondaryLockScreenControllerFactory.create(
                 mKeyguardSecurityCallback);
         mConfigurationController = configurationController;
+        mKeyguardViewController = keyguardViewController;
+        mFalsingManager = falsingManager;
     }
 
     @Override
@@ -192,12 +234,14 @@
     @Override
     protected void onViewAttached() {
         mView.setSwipeListener(mSwipeListener);
+        mView.addMotionEventListener(mGlobalTouchListener);
         mConfigurationController.addCallback(mConfigurationListener);
     }
 
     @Override
     protected void onViewDetached() {
         mConfigurationController.removeCallback(mConfigurationListener);
+        mView.removeMotionEventListener(mGlobalTouchListener);
     }
 
     /** */
@@ -479,6 +523,8 @@
         private final KeyguardStateController mKeyguardStateController;
         private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
         private final ConfigurationController mConfigurationController;
+        private final KeyguardViewController mKeyguardViewController;
+        private final FalsingManager mFalsingManager;
 
         @Inject
         Factory(KeyguardSecurityContainer view,
@@ -491,7 +537,9 @@
                 UiEventLogger uiEventLogger,
                 KeyguardStateController keyguardStateController,
                 KeyguardSecurityViewFlipperController securityViewFlipperController,
-                ConfigurationController configurationController) {
+                ConfigurationController configurationController,
+                KeyguardViewController keyguardViewController,
+                FalsingManager falsingManager) {
             mView = view;
             mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
             mLockPatternUtils = lockPatternUtils;
@@ -502,6 +550,8 @@
             mKeyguardStateController = keyguardStateController;
             mSecurityViewFlipperController = securityViewFlipperController;
             mConfigurationController = configurationController;
+            mKeyguardViewController = keyguardViewController;
+            mFalsingManager = falsingManager;
         }
 
         public KeyguardSecurityContainerController create(
@@ -510,7 +560,7 @@
                     mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
                     mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
                     mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
-                    mConfigurationController);
+                    mConfigurationController, mKeyguardViewController, mFalsingManager);
         }
 
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index cdbbfe6..b2bf2e6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -78,8 +78,8 @@
             KeyguardSecurityCallback keyguardSecurityCallback,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
             LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
-            TelephonyManager telephonyManager,
-            FalsingCollector falsingCollector, boolean isNewLayoutEnabled) {
+            TelephonyManager telephonyManager, FalsingCollector falsingCollector,
+            boolean isNewLayoutEnabled) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, liftToActivateListener,
                 falsingCollector);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 8fff342..620db48 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -85,8 +85,8 @@
             KeyguardSecurityCallback keyguardSecurityCallback,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
             LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
-            TelephonyManager telephonyManager,
-            FalsingCollector falsingCollector, boolean isNewLayoutEnabled) {
+            TelephonyManager telephonyManager, FalsingCollector falsingCollector,
+            boolean isNewLayoutEnabled) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, liftToActivateListener,
                 falsingCollector);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index 43ecf67..2036150 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -16,57 +16,63 @@
 
 package com.android.systemui.biometrics;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.PointF;
 import android.graphics.RectF;
 import android.util.AttributeSet;
 import android.widget.FrameLayout;
 
-import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.statusbar.phone.StatusBar;
-
 /**
  * Base class for views containing UDFPS animations. Note that this is a FrameLayout so that we
- * can support multiple child views drawing on the same region around the sensor location.
+ * can support multiple child views drawing in the same region around the sensor location.
+ *
+ * - hides animation view when pausing auth
+ * - sends illumination events to fingerprint drawable
+ * - sends sensor rect updates to fingerprint drawable
+ * - optionally can override dozeTimeTick to adjust views for burn-in mitigation
  */
-public abstract class UdfpsAnimationView extends FrameLayout implements DozeReceiver,
-        StatusBar.ExpansionChangedListener {
+abstract class UdfpsAnimationView extends FrameLayout {
 
-    private static final String TAG = "UdfpsAnimationView";
-
-    @Nullable protected abstract UdfpsAnimation getUdfpsAnimation();
-
-    @NonNull private UdfpsView mParent;
-    @NonNull private RectF mSensorRect;
     private int mAlpha;
+    private boolean mPauseAuth;
 
     public UdfpsAnimationView(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
-        mSensorRect = new RectF();
-        setWillNotDraw(false);
     }
 
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
+    /**
+     * Fingerprint drawable
+     */
+    abstract UdfpsDrawable getDrawable();
 
-        if (getUdfpsAnimation() != null) {
-            final int alpha = mParent.shouldPauseAuth() ? mAlpha : 255;
-            getUdfpsAnimation().setAlpha(alpha);
-            getUdfpsAnimation().draw(canvas);
-        }
+    void onSensorRectUpdated(RectF bounds) {
+        getDrawable().onSensorRectUpdated(bounds);
     }
 
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
+    void onIlluminationStarting() {
+        getDrawable().setIlluminationShowing(true);
+        getDrawable().invalidateSelf();
+    }
 
-        if (getUdfpsAnimation() != null) {
-            getUdfpsAnimation().onDestroy();
+    void onIlluminationStopped() {
+        getDrawable().setIlluminationShowing(false);
+        getDrawable().invalidateSelf();
+    }
+
+    /**
+     * @return true if changed
+     */
+    boolean setPauseAuth(boolean pauseAuth) {
+        if (pauseAuth != mPauseAuth) {
+            mPauseAuth = pauseAuth;
+            updateAlpha();
+            return true;
         }
+        return false;
+    }
+
+    private void updateAlpha() {
+        getDrawable().setAlpha(mPauseAuth ? mAlpha : 255);
     }
 
     private int expansionToAlpha(float expansion) {
@@ -81,76 +87,15 @@
         return (int) ((1 - percent) * 255);
     }
 
-    void onIlluminationStarting() {
-        if (getUdfpsAnimation() == null) {
-            return;
-        }
-
-        getUdfpsAnimation().setIlluminationShowing(true);
-        postInvalidate();
-    }
-
-    void onIlluminationStopped() {
-        if (getUdfpsAnimation() == null) {
-            return;
-        }
-
-        getUdfpsAnimation().setIlluminationShowing(false);
-        postInvalidate();
-    }
-
-    void setParent(@NonNull UdfpsView parent) {
-        mParent = parent;
-    }
-
-    void onSensorRectUpdated(@NonNull RectF sensorRect) {
-        mSensorRect = sensorRect;
-        if (getUdfpsAnimation() != null) {
-            getUdfpsAnimation().onSensorRectUpdated(mSensorRect);
-        }
-    }
-
-    void updateColor() {
-        if (getUdfpsAnimation() != null) {
-            getUdfpsAnimation().updateColor();
-        }
-        postInvalidate();
-    }
-
-    @Override
-    public void dozeTimeTick() {
-        if (getUdfpsAnimation() instanceof DozeReceiver) {
-            ((DozeReceiver) getUdfpsAnimation()).dozeTimeTick();
-        }
-    }
-
-    @Override
     public void onExpansionChanged(float expansion, boolean expanded) {
         mAlpha = expansionToAlpha(expansion);
-        postInvalidate();
-    }
-
-    public int getPaddingX() {
-        if (getUdfpsAnimation() == null) {
-            return 0;
-        }
-        return getUdfpsAnimation().getPaddingX();
-    }
-
-    public int getPaddingY() {
-        if (getUdfpsAnimation() == null) {
-            return 0;
-        }
-        return getUdfpsAnimation().getPaddingY();
+        updateAlpha();
     }
 
     /**
-     * @return the amount of translation needed if the view currently requires the user to touch
-     *         somewhere other than the exact center of the sensor. For example, this can happen
-     *         during guided enrollment.
+     * @return true if handled
      */
-    @NonNull
-    PointF getTouchTranslation() {
-        return new PointF(0, 0);
+    boolean dozeTimeTick() {
+        return false;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
new file mode 100644
index 0000000..b6d80ba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import static com.android.systemui.statusbar.StatusBarState.FULLSCREEN_USER_SWITCHER;
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
+
+import android.annotation.NonNull;
+import android.graphics.PointF;
+import android.graphics.RectF;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.util.ViewController;
+
+/**
+ * Handles:
+ * 1. registering for listeners when its view is attached and unregistering on view detached
+ * 2. pausing udfps when fingerprintManager may still be running but we temporarily want to hide
+ * the affordance. this allows us to fade the view in and out nicely (see shouldPauseAuth)
+ * 3. sending events to its view including:
+ *      - illumination events
+ *      - sensor position changes
+ *      - doze time event
+ */
+abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView>
+        extends ViewController<T> {
+    @NonNull final StatusBarStateController mStatusBarStateController;
+    @NonNull final StatusBar mStatusBar;
+
+    private boolean mNotificationShadeExpanded;
+    private int mStatusBarState;
+
+    protected UdfpsAnimationViewController(
+            T view,
+            StatusBarStateController statusBarStateController,
+            StatusBar statusBar) {
+        super(view);
+        mStatusBarStateController = statusBarStateController;
+        mStatusBar = statusBar;
+    }
+
+    @Override
+    protected void onViewAttached() {
+        mStatusBarStateController.addCallback(mStateListener);
+        mStateListener.onStateChanged(mStatusBarStateController.getState());
+        mStatusBar.addExpansionChangedListener(mStatusBarExpansionChangedListener);
+    }
+
+    @Override
+    protected void onViewDetached() {
+        mStatusBarStateController.removeCallback(mStateListener);
+        mStatusBar.removeExpansionChangedListener(mStatusBarExpansionChangedListener);
+    }
+
+    /**
+     * Returns true if the fingerprint manager is running but we want to temporarily pause
+     * authentication.
+     */
+    boolean shouldPauseAuth() {
+        return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD)
+                || mStatusBarState == SHADE_LOCKED
+                || mStatusBarState == FULLSCREEN_USER_SWITCHER;
+    }
+
+    /**
+     * Send pause auth update to our view.
+     */
+    void updatePauseAuth() {
+        if (mView.setPauseAuth(shouldPauseAuth())) {
+            mView.postInvalidate();
+        }
+    }
+
+    /**
+     * Send sensor position change to our view. This rect contains paddingX and paddingY.
+     */
+    void onSensorRectUpdated(RectF sensorRect) {
+        mView.onSensorRectUpdated(sensorRect);
+    }
+
+    /**
+     * Send dozeTimeTick to view in case it wants to handle its burn-in offset.
+     */
+    void dozeTimeTick() {
+        if (mView.dozeTimeTick()) {
+            mView.postInvalidate();
+        }
+    }
+
+    /**
+     * @return the amount of translation needed if the view currently requires the user to touch
+     *         somewhere other than the exact center of the sensor. For example, this can happen
+     *         during guided enrollment.
+     */
+    PointF getTouchTranslation() {
+        return new PointF(0, 0);
+    }
+
+    /**
+     * X-Padding to add to left and right of the sensor rectangle area to increase the size of our
+     * window to draw within.
+     * @return
+     */
+    int getPaddingX() {
+        return 0;
+    }
+
+    /**
+     * Y-Padding to add to top and bottom of the sensor rectangle area to increase the size of our
+     * window to draw within.
+     */
+    int getPaddingY() {
+        return 0;
+    }
+
+    /**
+     * Udfps has started illuminating and the fingerprint manager is working on authenticating.
+     */
+    void onIlluminationStarting() {
+        mView.onIlluminationStarting();
+        mView.postInvalidate();
+    }
+
+    /**
+     * Udfps has stopped illuminating and the fingerprint manager is no longer attempting to
+     * authenticate.
+     */
+    void onIlluminationStopped() {
+        mView.onIlluminationStopped();
+        mView.postInvalidate();
+    }
+
+    private final StatusBar.ExpansionChangedListener mStatusBarExpansionChangedListener =
+            new StatusBar.ExpansionChangedListener() {
+                @Override
+                public void onExpansionChanged(float expansion, boolean expanded) {
+                    mNotificationShadeExpanded = expanded;
+                    mView.onExpansionChanged(expansion, expanded);
+                    updatePauseAuth();
+                }
+            };
+
+    private final StatusBarStateController.StateListener mStateListener =
+            new StatusBarStateController.StateListener() {
+                @Override
+                public void onStateChanged(int newState) {
+                    mStatusBarState = newState;
+                    updatePauseAuth();
+                }
+            };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java
deleted file mode 100644
index 543df33..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.graphics.PointF;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-
-import com.android.systemui.R;
-
-/**
- * Class that coordinates non-HBM animations during enrollment.
- */
-public class UdfpsAnimationViewEnroll extends UdfpsAnimationView
-        implements UdfpsEnrollHelper.Listener {
-
-    private static final String TAG = "UdfpsAnimationViewEnroll";
-
-    @NonNull private UdfpsAnimationEnroll mUdfpsAnimation;
-    @NonNull private UdfpsProgressBar mProgressBar;
-    @Nullable private UdfpsEnrollHelper mEnrollHelper;
-
-    @NonNull
-    @Override
-    protected UdfpsAnimation getUdfpsAnimation() {
-        return mUdfpsAnimation;
-    }
-
-    public UdfpsAnimationViewEnroll(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-        mUdfpsAnimation = new UdfpsAnimationEnroll(context);
-    }
-
-    public void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
-        mEnrollHelper = helper;
-        mUdfpsAnimation.setEnrollHelper(helper);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        mProgressBar = findViewById(R.id.progress_bar);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        if (mEnrollHelper == null) {
-            Log.e(TAG, "Enroll helper is null");
-            return;
-        }
-
-        if (mEnrollHelper.shouldShowProgressBar()) {
-            mProgressBar.setVisibility(View.VISIBLE);
-
-            // Only need enrollment updates if the progress bar is showing :)
-            mEnrollHelper.setListener(this);
-        }
-    }
-
-    @Override
-    public void onEnrollmentProgress(int remaining, int totalSteps) {
-        final int interpolatedProgress = mProgressBar.getMax()
-                * Math.max(0, totalSteps + 1 - remaining) / (totalSteps + 1);
-
-        mProgressBar.setProgress(interpolatedProgress, true);
-    }
-
-    @NonNull
-    @Override
-    PointF getTouchTranslation() {
-        if (!mEnrollHelper.isCenterEnrollmentComplete()) {
-            return new PointF(0, 0);
-        } else {
-            return mEnrollHelper.getNextGuidedEnrollmentPoint();
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java
deleted file mode 100644
index 3d2f5a0..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import android.content.Context;
-import android.util.AttributeSet;
-
-import androidx.annotation.Nullable;
-
-/**
- * Class that coordinates non-HBM animations during other usage of FingerprintManager (not
- * including Keyguard).
- */
-public class UdfpsAnimationViewFpmOther extends UdfpsAnimationView {
-
-    private final UdfpsAnimationFpmOther mAnimation;
-
-    public UdfpsAnimationViewFpmOther(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-        mAnimation = new UdfpsAnimationFpmOther(context);
-    }
-
-    @Nullable
-    @Override
-    protected UdfpsAnimation getUdfpsAnimation() {
-        return mAnimation;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java
deleted file mode 100644
index 7d0b3e5..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import android.content.Context;
-import android.util.AttributeSet;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-
-/**
- * Class that coordinates non-HBM animations during keyguard authentication.
- */
-public class UdfpsAnimationViewKeyguard extends UdfpsAnimationView {
-    @Nullable private UdfpsAnimationKeyguard mAnimation;
-
-    public UdfpsAnimationViewKeyguard(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    void setStatusBarStateController(@NonNull StatusBarStateController statusBarStateController) {
-        if (mAnimation == null) {
-            mAnimation = new UdfpsAnimationKeyguard(getContext(), statusBarStateController);
-            mAnimation.setAnimationView(this);
-        }
-    }
-
-    @Nullable
-    @Override
-    protected UdfpsAnimation getUdfpsAnimation() {
-        return mAnimation;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.java
similarity index 67%
rename from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.java
index 515b442..70be907 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.java
@@ -24,19 +24,22 @@
 /**
  * Class that coordinates non-HBM animations during BiometricPrompt.
  *
+ * Currently doesn't draw anything.
+ *
  * Note that {@link AuthBiometricUdfpsView} also shows UDFPS animations. At some point we should
- * de-dupe this if necessary. This will probably happen once the top-level TODO in UdfpsController
- * is completed (inflate operation-specific views, instead of inflating generic udfps_view and
- * adding operation-specific animations to it).
+ * de-dupe this if necessary.
  */
-public class UdfpsAnimationViewBp extends UdfpsAnimationView {
-    public UdfpsAnimationViewBp(Context context, @Nullable AttributeSet attrs) {
+public class UdfpsBpView extends UdfpsAnimationView {
+    private UdfpsFpDrawable mFingerprintDrawable;
+
+    public UdfpsBpView(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
+        // Drawable isn't ever added to the view, so we don't currently show anything
+        mFingerprintDrawable = new UdfpsFpDrawable(mContext);
     }
 
-    @Nullable
     @Override
-    protected UdfpsAnimation getUdfpsAnimation() {
-        return null;
+    UdfpsDrawable getDrawable() {
+        return mFingerprintDrawable;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
new file mode 100644
index 0000000..b712c65
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Class that coordinates non-HBM animations for biometric prompt.
+ */
+class UdfpsBpViewController extends UdfpsAnimationViewController<UdfpsBpView> {
+    protected UdfpsBpViewController(
+            UdfpsBpView view,
+            StatusBarStateController statusBarStateController,
+            StatusBar statusBar) {
+        super(view, statusBarStateController, statusBar);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 4b6a8f6..94aeb73 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -31,9 +31,9 @@
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.IUdfpsOverlayController;
-import android.os.SystemClock;
 import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -195,17 +195,6 @@
         }
     }
 
-    @VisibleForTesting final StatusBar.ExpansionChangedListener mStatusBarExpansionListener =
-            (expansion, expanded) -> mView.onExpansionChanged(expansion, expanded);
-
-    @VisibleForTesting final StatusBarStateController.StateListener mStatusBarStateListener =
-            new StatusBarStateController.StateListener() {
-                @Override
-                public void onStateChanged(int newState) {
-                    mView.onStateChanged(newState);
-                }
-            };
-
     private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) {
         final float vx = tracker.getXVelocity(pointerId);
         final float vy = tracker.getYVelocity(pointerId);
@@ -360,10 +349,9 @@
 
     @Override
     public void dozeTimeTick() {
-        if (mView == null) {
-            return;
+        if (mView != null) {
+            mView.dozeTimeTick();
         }
-        mView.dozeTimeTick();
     }
 
     /**
@@ -387,7 +375,8 @@
         }
     }
 
-    private WindowManager.LayoutParams computeLayoutParams(@Nullable UdfpsAnimationView animation) {
+    private WindowManager.LayoutParams computeLayoutParams(
+            @Nullable UdfpsAnimationViewController animation) {
         final int paddingX = animation != null ? animation.getPaddingX() : 0;
         final int paddingY = animation != null ? animation.getPaddingY() : 0;
 
@@ -438,20 +427,13 @@
         mFgExecutor.execute(() -> {
             if (mView == null) {
                 try {
-                    Log.v(TAG, "showUdfpsOverlay | adding window");
-                    // TODO: Eventually we should refactor the code to inflate an
-                    //  operation-specific view here, instead of inflating a generic udfps_view
-                    //  and adding operation-specific animations to it.
+                    Log.v(TAG, "showUdfpsOverlay | adding window reason=" + reason);
                     mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false);
                     mView.setSensorProperties(mSensorProps);
                     mView.setHbmCallback(this);
-
-                    final UdfpsAnimationView animation = getUdfpsAnimationViewForReason(reason);
-                    mView.setAnimationView(animation);
-
-                    mStatusBar.addExpansionChangedListener(mStatusBarExpansionListener);
-                    mStatusBarStateController.addCallback(mStatusBarStateListener);
-                    mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState());
+                    UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason);
+                    animation.init();
+                    mView.setAnimationViewController(animation);
 
                     mWindowManager.addView(mView, computeLayoutParams(animation));
                     mView.setOnTouchListener(mOnTouchListener);
@@ -464,40 +446,46 @@
         });
     }
 
-    @NonNull
-    private UdfpsAnimationView getUdfpsAnimationViewForReason(int reason) {
-        Log.d(TAG, "getUdfpsAnimationForReason: " + reason);
-
-        final LayoutInflater inflater = LayoutInflater.from(mContext);
-
+    private UdfpsAnimationViewController inflateUdfpsAnimation(int reason) {
         switch (reason) {
             case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR:
-            case IUdfpsOverlayController.REASON_ENROLL_ENROLLING: {
-                final UdfpsAnimationViewEnroll view = (UdfpsAnimationViewEnroll)
-                        inflater.inflate(R.layout.udfps_animation_view_enroll, null, false);
-                view.setEnrollHelper(mServerRequest.mEnrollHelper);
-                return view;
-            }
-
-            case IUdfpsOverlayController.REASON_AUTH_BP: {
-                final UdfpsAnimationViewBp view = (UdfpsAnimationViewBp)
-                        inflater.inflate(R.layout.udfps_animation_view_bp, null, false);
-                return view;
-            }
-
-            case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: {
-                final UdfpsAnimationViewKeyguard view = (UdfpsAnimationViewKeyguard)
-                        inflater.inflate(R.layout.udfps_animation_view_keyguard, null, false);
-                view.setStatusBarStateController(mStatusBarStateController);
-                return view;
-            }
-
-            case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: {
-                final UdfpsAnimationViewFpmOther view = (UdfpsAnimationViewFpmOther)
-                        inflater.inflate(R.layout.udfps_animation_view_fpm_other, null, false);
-                return view;
-            }
-
+            case IUdfpsOverlayController.REASON_ENROLL_ENROLLING:
+                UdfpsEnrollView enrollView = (UdfpsEnrollView) mInflater.inflate(
+                        R.layout.udfps_enroll_view, null);
+                mView.addView(enrollView);
+                return new UdfpsEnrollViewController(
+                        enrollView,
+                        mServerRequest.mEnrollHelper,
+                        mStatusBarStateController,
+                        mStatusBar
+                );
+            case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD:
+                UdfpsKeyguardView keyguardView = (UdfpsKeyguardView)
+                        mInflater.inflate(R.layout.udfps_keyguard_view, null);
+                mView.addView(keyguardView);
+                return new UdfpsKeyguardViewController(
+                        keyguardView,
+                        mStatusBarStateController,
+                        mStatusBar
+                );
+            case IUdfpsOverlayController.REASON_AUTH_BP:
+                // note: empty controller, currently shows no visual affordance
+                UdfpsBpView bpView = (UdfpsBpView) mInflater.inflate(R.layout.udfps_bp_view, null);
+                mView.addView(bpView);
+                return new UdfpsBpViewController(
+                        bpView,
+                        mStatusBarStateController,
+                        mStatusBar
+                );
+            case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER:
+                UdfpsFpmOtherView authOtherView = (UdfpsFpmOtherView)
+                        mInflater.inflate(R.layout.udfps_fpm_other_view, null);
+                mView.addView(authOtherView);
+                return new UdfpsFpmOtherViewController(
+                        authOtherView,
+                        mStatusBarStateController,
+                        mStatusBar
+                );
             default:
                 Log.d(TAG, "Animation for reason " + reason + " not supported yet");
                 return null;
@@ -510,11 +498,9 @@
                 Log.v(TAG, "hideUdfpsOverlay | removing window");
                 // Reset the controller back to its starting state.
                 onFingerUp();
-
-                mStatusBar.removeExpansionChangedListener(mStatusBarExpansionListener);
-                mStatusBarStateController.removeCallback(mStatusBarStateListener);
-
                 mWindowManager.removeView(mView);
+                mView.setOnTouchListener(null);
+                mView.setAnimationViewController(null);
                 mView = null;
             } else {
                 Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden");
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java
index a51b6fd1..13d31cb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java
@@ -17,10 +17,10 @@
 package com.android.systemui.biometrics;
 
 import android.content.Context;
+import android.graphics.ColorFilter;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
-import android.view.View;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -28,24 +28,24 @@
 import com.android.systemui.R;
 
 /**
- * Abstract base class for animations that should be drawn when the finger is not touching the
+ * Abstract base class for drawable displayed when the finger is not touching the
  * sensor area.
  */
-public abstract class UdfpsAnimation extends Drawable {
-    protected abstract void updateColor();
-    protected abstract void onDestroy();
-
+public abstract class UdfpsDrawable extends Drawable {
     @NonNull protected final Context mContext;
     @NonNull protected final Drawable mFingerprintDrawable;
-    @Nullable private View mView;
     private boolean mIlluminationShowing;
 
-    public UdfpsAnimation(@NonNull Context context) {
+    int mAlpha = 255; // 0 - 255
+    public UdfpsDrawable(@NonNull Context context) {
         mContext = context;
         mFingerprintDrawable = context.getResources().getDrawable(R.drawable.ic_fingerprint, null);
         mFingerprintDrawable.mutate();
     }
 
+    /**
+     * @param sensorRect the rect coordinates for the sensor area
+     */
     public void onSensorRectUpdated(@NonNull RectF sensorRect) {
         final int margin =  (int) sensorRect.height() / 8;
 
@@ -56,17 +56,17 @@
         updateFingerprintIconBounds(bounds);
     }
 
+    /**
+     * Bounds for the fingerprint icon
+     */
     protected void updateFingerprintIconBounds(@NonNull Rect bounds) {
         mFingerprintDrawable.setBounds(bounds);
     }
 
     @Override
     public void setAlpha(int alpha) {
-        mFingerprintDrawable.setAlpha(alpha);
-    }
-
-    public void setAnimationView(UdfpsAnimationView view) {
-        mView = view;
+        mAlpha = alpha;
+        mFingerprintDrawable.setAlpha(mAlpha);
     }
 
     boolean isIlluminationShowing() {
@@ -77,23 +77,12 @@
         mIlluminationShowing = showing;
     }
 
-    /**
-     * @return The amount of padding that's needed on each side of the sensor, in pixels.
-     */
-    public int getPaddingX() {
-        return 0;
+    @Override
+    public void setColorFilter(@Nullable ColorFilter colorFilter) {
     }
 
-    /**
-     * @return The amount of padding that's needed on each side of the sensor, in pixels.
-     */
-    public int getPaddingY() {
+    @Override
+    public int getOpacity() {
         return 0;
     }
-
-    protected void postInvalidateView() {
-        if (mView != null) {
-            mView.postInvalidate();
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index 015a598..d80e085 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -20,7 +20,6 @@
 import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -33,23 +32,23 @@
 import com.android.systemui.R;
 
 /**
- * UDFPS animations that should be shown when enrolling.
+ * UDFPS fingerprint drawable that is shown when enrolling
  */
-public class UdfpsAnimationEnroll extends UdfpsAnimation {
+public class UdfpsEnrollDrawable extends UdfpsDrawable {
     private static final String TAG = "UdfpsAnimationEnroll";
 
     private static final float SHADOW_RADIUS = 5.f;
-    private static final float PROGRESS_BAR_RADIUS = 140.f;
+    static final float PROGRESS_BAR_RADIUS = 140.f;
 
     @NonNull private final Drawable mMovingTargetFpIcon;
     @NonNull private final Paint mSensorPaint;
     @NonNull private final Paint mBlueFill;
-    @NonNull private final Paint mBlueStroke;;
+    @NonNull private final Paint mBlueStroke;
 
     @Nullable private RectF mSensorRect;
     @Nullable private UdfpsEnrollHelper mEnrollHelper;
 
-    UdfpsAnimationEnroll(@NonNull Context context) {
+    UdfpsEnrollDrawable(@NonNull Context context) {
         super(context);
 
         mSensorPaint = new Paint(0 /* flags */);
@@ -72,6 +71,8 @@
         mMovingTargetFpIcon = context.getResources().getDrawable(R.drawable.ic_fingerprint, null);
         mMovingTargetFpIcon.setTint(Color.WHITE);
         mMovingTargetFpIcon.mutate();
+
+        mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon));
     }
 
     void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) {
@@ -79,16 +80,6 @@
     }
 
     @Override
-    protected void updateColor() {
-        mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon));
-    }
-
-    @Override
-    protected void onDestroy() {
-
-    }
-
-    @Override
     public void onSensorRectUpdated(@NonNull RectF sensorRect) {
         super.onSensorRectUpdated(sensorRect);
         mSensorRect = sensorRect;
@@ -98,6 +89,7 @@
     protected void updateFingerprintIconBounds(@NonNull Rect bounds) {
         super.updateFingerprintIconBounds(bounds);
         mMovingTargetFpIcon.setBounds(bounds);
+        invalidateSelf();
     }
 
     @Override
@@ -117,7 +109,7 @@
 
         // Draw moving target
         if (mEnrollHelper.isCenterEnrollmentComplete()) {
-            mFingerprintDrawable.setAlpha(64);
+            mFingerprintDrawable.setAlpha(mAlpha == 255 ? 64 : mAlpha);
 
             canvas.save();
             final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint();
@@ -130,33 +122,16 @@
             mMovingTargetFpIcon.draw(canvas);
             canvas.restore();
         } else {
-            mFingerprintDrawable.setAlpha(255);
+            mFingerprintDrawable.setAlpha(mAlpha);
         }
     }
 
     @Override
-    public int getPaddingX() {
-        return (int) Math.ceil(PROGRESS_BAR_RADIUS);
-    }
-
-    @Override
-    public int getPaddingY() {
-        return (int) Math.ceil(PROGRESS_BAR_RADIUS);
-    }
-
-    @Override
     public void setAlpha(int alpha) {
         super.setAlpha(alpha);
         mSensorPaint.setAlpha(alpha);
-    }
-
-    @Override
-    public void setColorFilter(@Nullable ColorFilter colorFilter) {
-
-    }
-
-    @Override
-    public int getOpacity() {
-        return 0;
+        mBlueFill.setAlpha(alpha);
+        mBlueStroke.setAlpha(alpha);
+        invalidateSelf();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
index 667b7a7..98a703f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java
@@ -21,6 +21,9 @@
 import android.content.Context;
 import android.graphics.PointF;
 import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.Build;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.TypedValue;
 
 import java.util.ArrayList;
@@ -32,6 +35,10 @@
 public class UdfpsEnrollHelper {
     private static final String TAG = "UdfpsEnrollHelper";
 
+    private static final String SCALE_OVERRIDE =
+            "com.android.systemui.biometrics.UdfpsEnrollHelper.scale";
+    private static final float SCALE = 0.5f;
+
     // Enroll with two center touches before going to guided enrollment
     private static final int NUM_CENTER_TOUCHES = 2;
 
@@ -39,9 +46,10 @@
         void onEnrollmentProgress(int remaining, int totalSteps);
     }
 
+    @NonNull private final Context mContext;
     // IUdfpsOverlayController reason
     private final int mEnrollReason;
-    private final List<PointF> mGuidedEnrollmentPoints;
+    @NonNull private final List<PointF> mGuidedEnrollmentPoints;
 
     private int mTotalSteps = -1;
     private int mRemainingSteps = -1;
@@ -53,6 +61,7 @@
     @Nullable Listener mListener;
 
     public UdfpsEnrollHelper(@NonNull Context context, int reason) {
+        mContext = context;
         mEnrollReason = reason;
         mGuidedEnrollmentPoints = new ArrayList<>();
 
@@ -100,13 +109,13 @@
 
     }
 
-    void setListener(@NonNull Listener listener) {
+    void setListener(Listener listener) {
         mListener = listener;
 
         // Only notify during setListener if enrollment is already in progress, so the progress
         // bar can be updated. If enrollment has not started yet, the progress bar will be empty
         // anyway.
-        if (mTotalSteps != -1) {
+        if (mListener != null && mTotalSteps != -1) {
             mListener.onEnrollmentProgress(mRemainingSteps, mTotalSteps);
         }
     }
@@ -121,9 +130,15 @@
 
     @NonNull
     PointF getNextGuidedEnrollmentPoint() {
+        float scale = SCALE;
+        if (Build.IS_ENG || Build.IS_USERDEBUG) {
+            scale = Settings.Secure.getFloatForUser(mContext.getContentResolver(),
+                    SCALE_OVERRIDE, SCALE,
+                    UserHandle.USER_CURRENT);
+        }
         final int index = mLocationsEnrolled - NUM_CENTER_TOUCHES;
         final PointF originalPoint = mGuidedEnrollmentPoints
                 .get(index % mGuidedEnrollmentPoints.size());
-        return new PointF(originalPoint.x * 0.5f, originalPoint.y * 0.5f);
+        return new PointF(originalPoint.x * scale, originalPoint.y * scale);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
new file mode 100644
index 0000000..7985d95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+/**
+ * View corresponding with udfps_enroll_view.xml
+ */
+public class UdfpsEnrollView extends UdfpsAnimationView {
+    private final UdfpsEnrollDrawable mFingerprintDrawable;
+    private ImageView mFingerprintView;
+
+    public UdfpsEnrollView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        mFingerprintDrawable = new UdfpsEnrollDrawable(mContext);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        mFingerprintView = findViewById(R.id.udfps_enroll_animation_fp_view);
+        mFingerprintView.setImageDrawable(mFingerprintDrawable);
+    }
+
+    @Override
+    public UdfpsDrawable getDrawable() {
+        return mFingerprintDrawable;
+    }
+
+    void setEnrollHelper(UdfpsEnrollHelper enrollHelper) {
+        mFingerprintDrawable.setEnrollHelper(enrollHelper);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
new file mode 100644
index 0000000..da8d712
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.annotation.NonNull;
+import android.graphics.PointF;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Class that coordinates non-HBM animations during enrollment.
+ */
+public class UdfpsEnrollViewController extends UdfpsAnimationViewController<UdfpsEnrollView> {
+    @NonNull private final UdfpsProgressBar mProgressBar;
+    @NonNull private final UdfpsEnrollHelper mEnrollHelper;
+
+    protected UdfpsEnrollViewController(
+            UdfpsEnrollView view,
+            @NonNull UdfpsEnrollHelper enrollHelper,
+            StatusBarStateController statusBarStateController,
+            StatusBar statusBar) {
+        super(view, statusBarStateController, statusBar);
+        mEnrollHelper = enrollHelper;
+        mProgressBar = mView.findViewById(R.id.progress_bar);
+        mView.setEnrollHelper(mEnrollHelper);
+    }
+
+    @Override
+    protected void onViewAttached() {
+        super.onViewAttached();
+        if (mEnrollHelper.shouldShowProgressBar()) {
+            mProgressBar.setVisibility(View.VISIBLE);
+
+            // Only need enrollment updates if the progress bar is showing :)
+            mEnrollHelper.setListener(mEnrollHelperListener);
+        }
+    }
+
+    @Override
+    protected void onViewDetached() {
+        super.onViewDetached();
+        mEnrollHelper.setListener(null);
+    }
+
+    @NonNull
+    @Override
+    public PointF getTouchTranslation() {
+        if (!mEnrollHelper.isCenterEnrollmentComplete()) {
+            return new PointF(0, 0);
+        } else {
+            return mEnrollHelper.getNextGuidedEnrollmentPoint();
+        }
+    }
+
+    @Override
+    public int getPaddingX() {
+        return (int) Math.ceil(UdfpsEnrollDrawable.PROGRESS_BAR_RADIUS);
+    }
+
+    @Override
+    public int getPaddingY() {
+        return (int) Math.ceil(UdfpsEnrollDrawable.PROGRESS_BAR_RADIUS);
+    }
+
+    private final UdfpsEnrollHelper.Listener mEnrollHelperListener =
+            new UdfpsEnrollHelper.Listener() {
+        @Override
+        public void onEnrollmentProgress(int remaining, int totalSteps) {
+            final int interpolatedProgress = mProgressBar.getMax()
+                    * Math.max(0, totalSteps + 1 - remaining) / (totalSteps + 1);
+
+            mProgressBar.setProgress(interpolatedProgress, true);
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.java
similarity index 63%
rename from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.java
index ef7a340..09b6fab 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.java
@@ -18,32 +18,19 @@
 
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.ColorFilter;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 /**
- * UDFPS animations that should be shown when authenticating via FingerprintManager, excluding
- * keyguard.
+ * Draws udfps fingerprint if sensor isn't illuminating.
  */
-public class UdfpsAnimationFpmOther extends UdfpsAnimation {
+public class UdfpsFpDrawable extends UdfpsDrawable {
 
-    UdfpsAnimationFpmOther(@NonNull Context context) {
+    UdfpsFpDrawable(@NonNull Context context) {
         super(context);
     }
 
     @Override
-    protected void updateColor() {
-
-    }
-
-    @Override
-    protected void onDestroy() {
-
-    }
-
-    @Override
     public void draw(@NonNull Canvas canvas) {
         if (isIlluminationShowing()) {
             return;
@@ -51,14 +38,4 @@
 
         mFingerprintDrawable.draw(canvas);
     }
-
-    @Override
-    public void setColorFilter(@Nullable ColorFilter colorFilter) {
-
-    }
-
-    @Override
-    public int getOpacity() {
-        return 0;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.java
new file mode 100644
index 0000000..85f1606
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+/**
+ * View corresponding with udfps_fpm_other_view.xml
+ */
+public class UdfpsFpmOtherView extends UdfpsAnimationView {
+    private final UdfpsFpDrawable mFingerprintDrawable;
+    private ImageView mFingerprintView;
+
+    public UdfpsFpmOtherView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        mFingerprintDrawable = new UdfpsFpDrawable(context);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        mFingerprintView = findViewById(R.id.udfps_fpm_other_fp_view);
+        mFingerprintView.setImageDrawable(mFingerprintDrawable);
+    }
+
+    @Override
+    UdfpsDrawable getDrawable() {
+        return mFingerprintDrawable;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
new file mode 100644
index 0000000..587501b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Class that coordinates non-HBM animations for non keyguard, enrollment or biometric prompt
+ * states.
+ *
+ * Currently only shows the fp drawable.
+ */
+class UdfpsFpmOtherViewController extends UdfpsAnimationViewController<UdfpsFpmOtherView> {
+    protected UdfpsFpmOtherViewController(
+            UdfpsFpmOtherView view,
+            StatusBarStateController statusBarStateController,
+            StatusBar statusBar) {
+        super(view, statusBarStateController, statusBar);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java
similarity index 63%
rename from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java
index 5f268cf..b0c5da0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java
@@ -21,28 +21,25 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.ColorFilter;
 import android.util.MathUtils;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import com.android.internal.graphics.ColorUtils;
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
 
 /**
  * UDFPS animations that should be shown when authenticating on keyguard.
  */
-public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiver,
-        StatusBarStateController.StateListener {
+public class UdfpsKeyguardDrawable extends UdfpsDrawable implements DozeReceiver {
 
     private static final String TAG = "UdfpsAnimationKeyguard";
+    private final int mLockScreenColor;
+    private final int mAmbientDisplayColor;
 
     @NonNull private final Context mContext;
-    @NonNull private final StatusBarStateController mStatusBarStateController;
     private final int mMaxBurnInOffsetX;
     private final int mMaxBurnInOffsetY;
 
@@ -51,18 +48,19 @@
     private float mBurnInOffsetX;
     private float mBurnInOffsetY;
 
-    UdfpsAnimationKeyguard(@NonNull Context context,
-            @NonNull StatusBarStateController statusBarStateController) {
+    UdfpsKeyguardDrawable(@NonNull Context context) {
         super(context);
         mContext = context;
-        mStatusBarStateController = statusBarStateController;
 
+        // TODO: move burn-in to view
         mMaxBurnInOffsetX = context.getResources()
                 .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
         mMaxBurnInOffsetY = context.getResources()
                 .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
 
-        statusBarStateController.addCallback(this);
+        mLockScreenColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor);
+        mAmbientDisplayColor = Color.WHITE;
+        updateAodPositionAndColor();
     }
 
     private void updateAodPositionAndColor() {
@@ -74,8 +72,10 @@
                 getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
                         - mMaxBurnInOffsetY,
                 mInterpolatedDarkAmount);
-        updateColor();
-        postInvalidateView();
+
+        mFingerprintDrawable.setTint(ColorUtils.blendARGB(mLockScreenColor,
+                mAmbientDisplayColor, mInterpolatedDarkAmount));
+        invalidateSelf();
     }
 
     @Override
@@ -84,44 +84,15 @@
     }
 
     @Override
-    public void onDozeAmountChanged(float linear, float eased) {
-        mInterpolatedDarkAmount = eased;
-        updateAodPositionAndColor();
-    }
-
-    @Override
     public void draw(@NonNull Canvas canvas) {
         if (isIlluminationShowing()) {
             return;
         }
-
-        canvas.save();
-        canvas.translate(mBurnInOffsetX, mBurnInOffsetY);
         mFingerprintDrawable.draw(canvas);
-        canvas.restore();
     }
 
-    @Override
-    public void setColorFilter(@Nullable ColorFilter colorFilter) {
-
-    }
-
-    @Override
-    public int getOpacity() {
-        return 0;
-    }
-
-    @Override
-    protected void updateColor() {
-        final int lockScreenIconColor = Utils.getColorAttrDefaultColor(mContext,
-                R.attr.wallpaperTextColor);
-        final int ambientDisplayIconColor = Color.WHITE;
-        mFingerprintDrawable.setTint(ColorUtils.blendARGB(lockScreenIconColor,
-                ambientDisplayIconColor, mInterpolatedDarkAmount));
-    }
-
-    @Override
-    protected void onDestroy() {
-        mStatusBarStateController.removeCallback(this);
+    void onDozeAmountChanged(float linear, float eased) {
+        mInterpolatedDarkAmount = eased;
+        updateAodPositionAndColor();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
new file mode 100644
index 0000000..6a93560
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+
+/**
+ * View corresponding with udfps_keyguard_view.xml
+ */
+public class UdfpsKeyguardView extends UdfpsAnimationView {
+    private final UdfpsKeyguardDrawable mFingerprintDrawable;
+    private ImageView mFingerprintView;
+
+    public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        mFingerprintDrawable = new UdfpsKeyguardDrawable(mContext);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        mFingerprintView = findViewById(R.id.udfps_keyguard_animation_fp_view);
+        mFingerprintView.setImageDrawable(mFingerprintDrawable);
+    }
+
+    @Override
+    public UdfpsDrawable getDrawable() {
+        return mFingerprintDrawable;
+    }
+
+    @Override
+    public boolean dozeTimeTick() {
+        // TODO: burnin
+        mFingerprintDrawable.dozeTimeTick();
+        return true;
+    }
+
+    void onDozeAmountChanged(float linear, float eased) {
+        mFingerprintDrawable.onDozeAmountChanged(linear, eased);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
new file mode 100644
index 0000000..14bb3fee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Class that coordinates non-HBM animations during keyguard authentication.
+ */
+public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> {
+    private boolean mForceShow;
+
+    protected UdfpsKeyguardViewController(
+            UdfpsKeyguardView view,
+            StatusBarStateController statusBarStateController,
+            StatusBar statusBar) {
+        super(view, statusBarStateController, statusBar);
+    }
+
+    @Override
+    protected void onViewAttached() {
+        super.onViewAttached();
+        mStatusBarStateController.addCallback(mStateListener);
+        final float dozeAmount = mStatusBarStateController.getDozeAmount();
+        mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
+    }
+
+    @Override
+    protected void onViewDetached() {
+        super.onViewDetached();
+        mStatusBarStateController.removeCallback(mStateListener);
+    }
+
+    /**
+     * Overrides non-force show logic in shouldPauseAuth to still auth.
+     */
+    private void forceShow(boolean forceShow) {
+        if (mForceShow == forceShow) {
+            return;
+        }
+
+        mForceShow = forceShow;
+        updatePauseAuth();
+        // TODO: animate show/hide background protection
+    }
+
+    /**
+     * Returns true if the fingerprint manager is running but we want to temporarily pause
+     * authentication. On the keyguard, we may want to show udfps when the shade
+     * is expanded, so this can be overridden with the forceShow method.
+     */
+    public boolean shouldPauseAuth() {
+        if (mForceShow) {
+            return false;
+        }
+        return super.shouldPauseAuth();
+    }
+
+    private final StatusBarStateController.StateListener mStateListener =
+            new StatusBarStateController.StateListener() {
+        @Override
+        public void onDozeAmountChanged(float linear, float eased) {
+            mView.onDozeAmountChanged(linear, eased);
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index a52bddc..42d0d84 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -16,10 +16,6 @@
 
 package com.android.systemui.biometrics;
 
-import static com.android.systemui.statusbar.StatusBarState.FULLSCREEN_USER_SWITCHER;
-import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -39,15 +35,12 @@
 
 import com.android.systemui.R;
 import com.android.systemui.doze.DozeReceiver;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.StatusBar;
 
 /**
  * A view containing 1) A SurfaceView for HBM, and 2) A normal drawable view for all other
  * animations.
  */
-public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator,
-        StatusBarStateController.StateListener, StatusBar.ExpansionChangedListener {
+public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator {
     private static final String TAG = "UdfpsView";
 
     private static final int DEBUG_TEXT_SIZE_PX = 32;
@@ -56,7 +49,7 @@
     @NonNull private final Paint mDebugTextPaint;
 
     @NonNull private UdfpsSurfaceView mHbmSurfaceView;
-    @Nullable private UdfpsAnimationView mAnimationView;
+    @Nullable private UdfpsAnimationViewController mAnimationViewController;
 
     // Used to obtain the sensor location.
     @NonNull private FingerprintSensorPropertiesInternal mSensorProps;
@@ -64,8 +57,6 @@
     private final float mSensorTouchAreaCoefficient;
     @Nullable private String mDebugMessage;
     private boolean mIlluminationRequested;
-    private int mStatusBarState;
-    private boolean mNotificationShadeExpanded;
 
     public UdfpsView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -108,15 +99,6 @@
         mSensorProps = properties;
     }
 
-    void setAnimationView(@NonNull UdfpsAnimationView animation) {
-        mAnimationView = animation;
-        animation.setParent(this);
-
-        // TODO: Consider using a ViewStub placeholder to maintain positioning and inflating it
-        //  after the animation type has been decided.
-        addView(animation, 0);
-    }
-
     @Override
     public void setHbmCallback(@Nullable HbmCallback callback) {
         mHbmSurfaceView.setHbmCallback(callback);
@@ -124,45 +106,38 @@
 
     @Override
     public void dozeTimeTick() {
-        if (mAnimationView == null) {
-            return;
-        }
-        mAnimationView.dozeTimeTick();
-    }
-
-    @Override
-    public void onStateChanged(int newState) {
-        mStatusBarState = newState;
-    }
-
-    @Override
-    public void onExpansionChanged(float expansion, boolean expanded) {
-        mNotificationShadeExpanded = expanded;
-
-        if (mAnimationView != null) {
-            mAnimationView.onExpansionChanged(expansion, expanded);
+        if (mAnimationViewController != null) {
+            mAnimationViewController.dozeTimeTick();
         }
     }
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-        mSensorRect.set(0 + mAnimationView.getPaddingX(),
-                0 + mAnimationView.getPaddingY(),
-                2 * mSensorProps.sensorRadius + mAnimationView.getPaddingX(),
-                2 * mSensorProps.sensorRadius + mAnimationView.getPaddingY());
+        int paddingX = mAnimationViewController == null ? 0
+                : mAnimationViewController.getPaddingX();
+        int paddingY = mAnimationViewController == null ? 0
+                : mAnimationViewController.getPaddingY();
+        mSensorRect.set(
+                paddingX,
+                paddingY,
+                2 * mSensorProps.sensorRadius + paddingX,
+                2 * mSensorProps.sensorRadius + paddingY);
 
         mHbmSurfaceView.onSensorRectUpdated(new RectF(mSensorRect));
-        mAnimationView.onSensorRectUpdated(new RectF(mSensorRect));
+        if (mAnimationViewController != null) {
+            mAnimationViewController.onSensorRectUpdated(new RectF(mSensorRect));
+        }
+    }
+
+    void setAnimationViewController(UdfpsAnimationViewController animationViewController) {
+        mAnimationViewController = animationViewController;
     }
 
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         Log.v(TAG, "onAttachedToWindow");
-
-        // Retrieve the colors each time, since it depends on day/night mode
-        mAnimationView.updateColor();
     }
 
     @Override
@@ -188,7 +163,9 @@
 
     boolean isWithinSensorArea(float x, float y) {
         // The X and Y coordinates of the sensor's center.
-        final PointF translation = mAnimationView.getTouchTranslation();
+        final PointF translation = mAnimationViewController == null
+                ? new PointF(0, 0)
+                : mAnimationViewController.getTouchTranslation();
         final float cx = mSensorRect.centerX() + translation.x;
         final float cy = mSensorRect.centerY() + translation.y;
         // Radii along the X and Y axes.
@@ -199,18 +176,7 @@
                 && x < (cx + rx * mSensorTouchAreaCoefficient)
                 && y > (cy - ry * mSensorTouchAreaCoefficient)
                 && y < (cy + ry * mSensorTouchAreaCoefficient)
-                && !shouldPauseAuth();
-    }
-
-    /**
-     * States where UDFPS should temporarily not be authenticating. Instead of completely stopping
-     * authentication which would cause the UDFPS icons to abruptly disappear, do it here by not
-     * sending onFingerDown and smoothly animating away.
-     */
-    boolean shouldPauseAuth() {
-        return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD)
-                || mStatusBarState == SHADE_LOCKED
-                || mStatusBarState == FULLSCREEN_USER_SWITCHER;
+                && !mAnimationViewController.shouldPauseAuth();
     }
 
     boolean isIlluminationRequested() {
@@ -223,7 +189,9 @@
     @Override
     public void startIllumination(@Nullable Runnable onIlluminatedRunnable) {
         mIlluminationRequested = true;
-        mAnimationView.onIlluminationStarting();
+        if (mAnimationViewController != null) {
+            mAnimationViewController.onIlluminationStarting();
+        }
         mHbmSurfaceView.setVisibility(View.VISIBLE);
         mHbmSurfaceView.startIllumination(onIlluminatedRunnable);
     }
@@ -231,7 +199,9 @@
     @Override
     public void stopIllumination() {
         mIlluminationRequested = false;
-        mAnimationView.onIlluminationStopped();
+        if (mAnimationViewController != null) {
+            mAnimationViewController.onIlluminationStopped();
+        }
         mHbmSurfaceView.setVisibility(View.INVISIBLE);
         mHbmSurfaceView.stopIllumination();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 6572ca9..efb7992 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -18,7 +18,6 @@
 
 import static com.android.systemui.classifier.FalsingManagerProxy.FALSING_SUCCESS;
 import static com.android.systemui.classifier.FalsingModule.BRIGHT_LINE_GESTURE_CLASSIFERS;
-import static com.android.systemui.classifier.FalsingModule.DOUBLE_TAP_TIMEOUT_MS;
 
 import android.net.Uri;
 import android.os.Build;
@@ -29,11 +28,9 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.classifier.FalsingDataProvider.SessionListener;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.TestHarness;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.sensors.ThresholdSensor;
 
 import java.io.FileDescriptor;
@@ -63,14 +60,13 @@
 
     private static final int RECENT_INFO_LOG_SIZE = 40;
     private static final int RECENT_SWIPE_LOG_SIZE = 20;
+    private static final double TAP_CONFIDENCE_THRESHOLD = 0.7;
 
     private final FalsingDataProvider mDataProvider;
     private final DockManager mDockManager;
     private final SingleTapClassifier mSingleTapClassifier;
     private final DoubleTapClassifier mDoubleTapClassifier;
     private final HistoryTracker mHistoryTracker;
-    private final DelayableExecutor mDelayableExecutor;
-    private final long mDoubleTapTimeMs;
     private final boolean mTestHarness;
     private final MetricsLogger mMetricsLogger;
     private int mIsFalseTouchCalls;
@@ -98,19 +94,7 @@
                 @Override
         public void onGestureComplete(long completionTimeMs) {
             if (mPriorResults != null) {
-                // Single taps that may become double taps don't get added right away.
-                if (mClassifyAsSingleTap) {
-                    Collection<FalsingClassifier.Result> singleTapResults = mPriorResults;
-                    mSingleTapHistoryCanceller = mDelayableExecutor.executeDelayed(
-                            () -> {
-                                mSingleTapHistoryCanceller = null;
-                                mHistoryTracker.addResults(singleTapResults, completionTimeMs);
-                            },
-                            mDoubleTapTimeMs);
-                    mClassifyAsSingleTap = false;  // Don't treat things as single taps by default.
-                } else {
-                    mHistoryTracker.addResults(mPriorResults, completionTimeMs);
-                }
+                mHistoryTracker.addResults(mPriorResults, completionTimeMs);
                 mPriorResults = null;
             } else {
                 // Gestures that were not classified get treated as a false.
@@ -123,17 +107,13 @@
     };
 
     private Collection<FalsingClassifier.Result> mPriorResults;
-    private boolean mClassifyAsSingleTap;
-    private Runnable mSingleTapHistoryCanceller;
 
     @Inject
     public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider,
             DockManager dockManager, MetricsLogger metricsLogger,
             @Named(BRIGHT_LINE_GESTURE_CLASSIFERS) Set<FalsingClassifier> classifiers,
             SingleTapClassifier singleTapClassifier, DoubleTapClassifier doubleTapClassifier,
-            HistoryTracker historyTracker, @Main DelayableExecutor delayableExecutor,
-            @Named(DOUBLE_TAP_TIMEOUT_MS) long doubleTapTimeMs,
-            @TestHarness boolean testHarness) {
+            HistoryTracker historyTracker, @TestHarness boolean testHarness) {
         mDataProvider = falsingDataProvider;
         mDockManager = dockManager;
         mMetricsLogger = metricsLogger;
@@ -141,8 +121,6 @@
         mSingleTapClassifier = singleTapClassifier;
         mDoubleTapClassifier = doubleTapClassifier;
         mHistoryTracker = historyTracker;
-        mDelayableExecutor = delayableExecutor;
-        mDoubleTapTimeMs = doubleTapTimeMs;
         mTestHarness = testHarness;
 
         mDataProvider.addSessionListener(mSessionListener);
@@ -158,7 +136,6 @@
     public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
         boolean result;
 
-        mClassifyAsSingleTap = false;
         mDataProvider.setInteractionType(interactionType);
 
         if (!mTestHarness && !mDataProvider.isJustUnlockedWithFace() && !mDockManager.isDocked()) {
@@ -166,7 +143,7 @@
                     mClassifiers.stream().map(falsingClassifier -> {
                         FalsingClassifier.Result classifierResult =
                                 falsingClassifier.classifyGesture(
-                                        mHistoryTracker.falsePenalty(),
+                                        mHistoryTracker.falseBelief(),
                                         mHistoryTracker.falseConfidence());
                         if (classifierResult.isFalse()) {
                             logInfo(String.format(
@@ -217,9 +194,7 @@
     }
 
     @Override
-    public boolean isFalseTap(boolean robustCheck) {
-        mClassifyAsSingleTap = true;
-
+    public boolean isFalseTap(boolean robustCheck, double falsePenalty) {
         FalsingClassifier.Result singleTapResult =
                 mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents());
         mPriorResults = Collections.singleton(singleTapResult);
@@ -233,14 +208,24 @@
             return true;
         }
 
-        // TODO(b/172655679): More heuristics to come. For now, allow touches through if face-authed
         if (robustCheck) {
-            boolean result = !mDataProvider.isJustUnlockedWithFace();
-            mPriorResults = Collections.singleton(
-                    result ? FalsingClassifier.Result.falsed(0.1, "no face detected")
-                            : FalsingClassifier.Result.passed(1));
-
-            return result;
+            if (mDataProvider.isJustUnlockedWithFace()) {
+                // Immediately pass if a face is detected.
+                mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(1));
+                return false;
+            } else if (!isFalseDoubleTap()) {
+                // We must check double tapping before other heuristics. This is because
+                // the double tap will fail if there's only been one tap. We don't want that
+                // failure to be recorded in mPriorResults.
+                return false;
+            } else if (mHistoryTracker.falseBelief() > TAP_CONFIDENCE_THRESHOLD) {
+                mPriorResults = Collections.singleton(
+                        FalsingClassifier.Result.falsed(0, "bad history"));
+                return true;
+            } else {
+                mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(0.1));
+                return false;
+            }
         }
 
         return false;
@@ -248,7 +233,6 @@
 
     @Override
     public boolean isFalseDoubleTap() {
-        mClassifyAsSingleTap = false;
         FalsingClassifier.Result result = mDoubleTapClassifier.classifyGesture();
         mPriorResults = Collections.singleton(result);
         if (result.isFalse()) {
@@ -258,12 +242,6 @@
             if (reason != null) {
                 logInfo(reason);
             }
-        } else {
-            // A valid double tap prevents an invalid single tap from going into history.
-            if (mSingleTapHistoryCanceller != null) {
-                mSingleTapHistoryCanceller.run();
-                mSingleTapHistoryCanceller = null;
-            }
         }
         return result.isFalse();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
index bbb9371..ffcdb93 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
@@ -62,7 +62,7 @@
                 VERTICAL_ANGLE_RANGE);
     }
 
-    Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
         float angle = getAngle();
 
         if (angle == Float.MAX_VALUE) {  // Unknown angle
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
index 4cb5aa2..0f121c1 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
@@ -147,7 +147,7 @@
     }
 
     @Override
-    Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
         return !getPassedFlingThreshold()
                 ? Result.falsed(0.5, getReason()) : Result.passed(0.5);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
index f7fe14a..baa54a6 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java
@@ -46,14 +46,14 @@
     }
 
     @Override
-    Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
         List<MotionEvent> secondTapEvents = getRecentMotionEvents();
         List<MotionEvent> firstTapEvents = getPriorMotionEvents();
 
         StringBuilder reason = new StringBuilder();
 
         if (firstTapEvents == null) {
-            return Result.falsed(1, "Only one gesture recorded");
+            return Result.falsed(0, "Only one gesture recorded");
         }
 
         return !isDoubleTap(firstTapEvents, secondTapEvents, reason)
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java
index f40045b..1af5f7c 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java
@@ -124,7 +124,7 @@
      * See also {@link #classifyGesture(double, double)}.
      */
     Result classifyGesture() {
-        return calculateFalsingResult(0, 0);
+        return calculateFalsingResult(0.5, 0);
     }
 
     /**
@@ -135,16 +135,16 @@
      *
      * See also {@link #classifyGesture()}.
      */
-    Result classifyGesture(double historyPenalty, double historyConfidence) {
-        return calculateFalsingResult(historyPenalty, historyConfidence);
+    Result classifyGesture(double historyBelief, double historyConfidence) {
+        return calculateFalsingResult(historyBelief, historyConfidence);
     }
 
     /**
      * Calculate a result based on available data.
      *
-     * When passed a historyConfidence of 0, the history penalty should be wholly ignored.
+     * When passed a historyConfidence of 0, the history belief should be wholly ignored.
      */
-    abstract Result calculateFalsingResult(double historyPenalty, double historyConfidence);
+    abstract Result calculateFalsingResult(double historyBelief, double historyConfidence);
 
     /** */
     public static void logDebug(String msg) {
@@ -164,7 +164,7 @@
     /**
      * A Falsing result that encapsulates the boolean result along with confidence and a reason.
      */
-    static class Result {
+    public static class Result {
         private final boolean mFalsed;
         private final double mConfidence;
         private final String mReason;
@@ -193,14 +193,14 @@
         /**
          * Construct a "falsed" result indicating that a gesture should be treated as accidental.
          */
-        static Result falsed(double confidence, String reason) {
+        public static Result falsed(double confidence, String reason) {
             return new Result(true, confidence, reason);
         }
 
         /**
          * Construct a "passed" result indicating that a gesture should be allowed.
          */
-        static Result passed(double confidence) {
+        public static Result passed(double confidence) {
             return new Result(false, confidence, null);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
index b0bbab3..bb03720 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
@@ -120,5 +120,8 @@
 
     /** */
     void cleanup();
+
+    /** */
+    void updateFalseConfidence(FalsingClassifier.Result result);
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
index 12a0604..939b45a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
@@ -153,4 +153,8 @@
     @Override
     public void cleanup() {
     }
+
+    @Override
+    public void updateFalseConfidence(FalsingClassifier.Result result) {
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index e08b43b..e090006 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -29,6 +29,9 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.util.sensors.ProximitySensor;
 import com.android.systemui.util.sensors.ThresholdSensor;
+import com.android.systemui.util.time.SystemClock;
+
+import java.util.Collections;
 
 import javax.inject.Inject;
 
@@ -42,8 +45,10 @@
     private final FalsingDataProvider mFalsingDataProvider;
     private final FalsingManager mFalsingManager;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final HistoryTracker mHistoryTracker;
     private final ProximitySensor mProximitySensor;
     private final StatusBarStateController mStatusBarStateController;
+    private final SystemClock mSystemClock;
 
     private int mState;
     private boolean mShowingAod;
@@ -80,13 +85,16 @@
 
     @Inject
     FalsingCollectorImpl(FalsingDataProvider falsingDataProvider, FalsingManager falsingManager,
-            KeyguardUpdateMonitor keyguardUpdateMonitor,
-            ProximitySensor proximitySensor, StatusBarStateController statusBarStateController) {
+            KeyguardUpdateMonitor keyguardUpdateMonitor, HistoryTracker historyTracker,
+            ProximitySensor proximitySensor, StatusBarStateController statusBarStateController,
+            SystemClock systemClock) {
         mFalsingDataProvider = falsingDataProvider;
         mFalsingManager = falsingManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mHistoryTracker = historyTracker;
         mProximitySensor = proximitySensor;
         mStatusBarStateController = statusBarStateController;
+        mSystemClock = systemClock;
 
 
         mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
@@ -282,6 +290,11 @@
         mStatusBarStateController.removeCallback(mStatusBarStateListener);
     }
 
+    @Override
+    public void updateFalseConfidence(FalsingClassifier.Result result) {
+        mHistoryTracker.addResults(Collections.singleton(result), mSystemClock.uptimeMillis());
+    }
+
     private void updateInteractionType(@Classifier.InteractionType int type) {
         logDebug("InteractionType: " + type);
         mFalsingDataProvider.setInteractionType(type);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
index d4d8d06..aac27cb 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -76,7 +76,7 @@
     }
 
     @Override
-    public boolean isFalseTap(boolean robustCheck) {
+    public boolean isFalseTap(boolean robustCheck, double falsePenalty) {
         return robustCheck ? mIsFalseRobustTap : mIsFalseTap;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index cbec057..e9bb48c 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -131,8 +131,8 @@
     }
 
     @Override
-    public boolean isFalseTap(boolean robustCheck) {
-        return mInternalFalsingManager.isFalseTap(robustCheck);
+    public boolean isFalseTap(boolean robustCheck, double falsePenalty) {
+        return mInternalFalsingManager.isFalseTap(robustCheck, falsePenalty);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java b/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java
index 8bd94a1..be48ec4 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/HistoryTracker.java
@@ -36,12 +36,17 @@
  */
 @SysUISingleton
 public class HistoryTracker {
-    private static final double HISTORY_DECAY = 0.8f;
+    private static final long HISTORY_MAX_AGE_MS = 10000;
+    // A score is decayed discretely every DECAY_INTERVAL_MS.
     private static final long DECAY_INTERVAL_MS = 100;
-    // We expire items once their decay factor is below 0.001.
-    private static final double MINIMUM_SCORE = 0.001;
-    private static final long TOTAL_DECAY_TIME_MS =
-            DECAY_INTERVAL_MS * (long) (Math.log(MINIMUM_SCORE) / Math.log(HISTORY_DECAY));
+    // We expire items once their decay factor is below 0.1.
+    private static final double MINIMUM_SCORE = 0.1;
+    // This magic number is the factor a score is reduced by every DECAY_INTERVAL_MS.
+    // Once a score is HISTORY_MAX_AGE_MS ms old, it will be reduced by being multiplied by
+    // MINIMUM_SCORE. The math below ensures that.
+    private static final double HISTORY_DECAY =
+            Math.pow(10, Math.log10(MINIMUM_SCORE) / HISTORY_MAX_AGE_MS * DECAY_INTERVAL_MS);
+
     private final SystemClock mSystemClock;
 
     DelayQueue<CombinedResult> mResults = new DelayQueue<>();
@@ -54,29 +59,36 @@
     /**
      * Returns how much the HistoryClassifier thinks the past events indicate pocket dialing.
      *
-     * A result of 0 means that all prior gestures succeeded or there is no data to
-     * calculate a score with. Use {@link #falseConfidence()} to differentiate between the
-     * two cases.
+     * A result close to 0.5 means that prior data is inconclusive (inconsistent, lacking
+     * confidence, or simply lacking in quantity).
      *
-     * A result of 1 means that all prior gestures were very obviously false. The current gesture
-     * might be valid, but it should have a high-bar to be classified as such.
+     * A result close to 0 means that prior gestures indicate a success.
+     *
+     * A result close to 1 means that prior gestures were very obviously false.
+     *
+     * The current gesture might be different than what is reported by this method, but there should
+     * be a high-bar to be classified differently.
      *
      * See also {@link #falseConfidence()}.
      */
-    double falsePenalty() {
+    double falseBelief() {
         //noinspection StatementWithEmptyBody
         while (mResults.poll() != null) {
             // Empty out the expired results.
         }
 
         if (mResults.isEmpty()) {
-            return 0;
+            return 0.5;
         }
 
         long nowMs = mSystemClock.uptimeMillis();
+        // Get our Bayes on.
         return mResults.stream()
                 .map(result -> result.getDecayedScore(nowMs))
-                .reduce(0.0, Double::sum) / mResults.size();
+                .reduce(0.5,
+                        (prior, measurement) ->
+                                (prior * measurement)
+                                        / (prior * measurement + (1 - prior) * (1 - measurement)));
     }
 
     /**
@@ -91,7 +103,7 @@
      * A result of 1 means that there are ample, fresh data to act upon that is all consistent
      * with each other.
      *
-     * See als {@link #falsePenalty()}.
+     * See als {@link #falseBelief()}.
      */
     double falseConfidence() {
         //noinspection StatementWithEmptyBody
@@ -126,6 +138,15 @@
 
         finalScore /= results.size();
 
+        // Never add a 0 or  1, else Bayes breaks down (a 0 and a 1 together results in NaN). In
+        // other words,  you shouldn't need Bayes if you have 100% confidence one way or another.
+        // Instead, make the number ever so slightly smaller so that our math never breaks.
+        if (finalScore == 1) {
+            finalScore = 0.99999;
+        } else if (finalScore == 0) {
+            finalScore = 0.00001;
+        }
+
         //noinspection StatementWithEmptyBody
         while (mResults.poll() != null) {
             // Empty out the expired results.
@@ -147,15 +168,17 @@
         private final double mScore;
 
         CombinedResult(long uptimeMillis, double score) {
-            mExpiryMs = uptimeMillis + TOTAL_DECAY_TIME_MS;
+            mExpiryMs = uptimeMillis + HISTORY_MAX_AGE_MS;
             mScore = score;
         }
 
         double getDecayedScore(long nowMs) {
             long remainingTimeMs = mExpiryMs - nowMs;
-            long decayedTimeMs = TOTAL_DECAY_TIME_MS - remainingTimeMs;
+            long decayedTimeMs = HISTORY_MAX_AGE_MS - remainingTimeMs;
             double timeIntervals = (double) decayedTimeMs / DECAY_INTERVAL_MS;
-            return mScore * Math.pow(HISTORY_DECAY, timeIntervals);
+
+            // Score should decay towards 0.5.
+            return (mScore - 0.5) * Math.pow(HISTORY_DECAY, timeIntervals) + 0.5;
         }
 
         double getScore() {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
index cd399fe..77d2d42 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java
@@ -56,7 +56,7 @@
     }
 
     @Override
-    Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
         int interactionType = getInteractionType();
         int allowedPointerCount =
                 (interactionType == QUICK_SETTINGS || interactionType == NOTIFICATION_DRAG_DOWN)
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
index 9ee8598..6e97857 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
@@ -112,7 +112,7 @@
     }
 
     @Override
-    Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
         if (getInteractionType() == QUICK_SETTINGS) {
             return Result.passed(0);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
index f2622ec..4dd20cc 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java
@@ -39,12 +39,15 @@
     }
 
     @Override
-    Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
         return isTap(getRecentMotionEvents());
     }
 
     /** Given a list of {@link android.view.MotionEvent}'s, returns true if the look like a tap. */
     public Result isTap(List<MotionEvent> motionEvents) {
+        if (motionEvents.isEmpty()) {
+            return Result.falsed(0, "no motion events");
+        }
         float downX = motionEvents.get(0).getX();
         float downY = motionEvents.get(0).getY();
 
@@ -59,7 +62,7 @@
             } else if (Math.abs(event.getY() - downY) >= mTouchSlop) {
                 reason = "dY too big for a tap: "
                         + Math.abs(event.getY() - downY)
-                        + "vs "
+                        + " vs "
                         + mTouchSlop;
                 return Result.falsed(0.5, reason);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
index d470d62..4e032ea 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
@@ -38,7 +38,7 @@
     }
 
     @Override
-    Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
         boolean vertical = isVertical();
         boolean up = isUp();
         boolean right = isRight();
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
index 2bfb218..2058257 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
@@ -84,7 +84,7 @@
     }
 
     @Override
-    Result calculateFalsingResult(double historyPenalty, double historyConfidence) {
+    Result calculateFalsingResult(double historyBelief, double historyConfidence) {
         List<MotionEvent> motionEvents = getRecentMotionEvents();
         // Rotate horizontal gestures to be horizontal between their first and last point.
         // Rotate vertical gestures to be vertical between their first and last point.
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 4491cc1..0bfd065 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -212,6 +212,14 @@
         createNavigationBar(display, null /* savedState */, null /* result */);
     }
 
+    @Override
+    public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+        final NavigationBarView navigationBarView = getNavigationBarView(displayId);
+        if (navigationBarView != null) {
+            navigationBarView.setNavigationBarLumaSamplingEnabled(enable);
+        }
+    }
+
     /**
      * Recreates the navigation bar for the given display.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 35d5ca9..148c665 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -1397,4 +1397,12 @@
     private final Consumer<Rect> mPipListener = bounds -> post(() -> {
         mEdgeBackGestureHandler.setPipStashExclusionBounds(bounds);
     });
+
+    void setNavigationBarLumaSamplingEnabled(boolean enable) {
+        if (enable) {
+            mRegionSamplingHelper.start(mSamplingBounds);
+        } else {
+            mRegionSamplingHelper.stop();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index fe76668..b8823e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.qs;
 
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
 import android.util.Log;
 import android.view.View;
 import android.view.View.OnAttachStateChangeListener;
@@ -35,6 +37,7 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
@@ -77,6 +80,9 @@
     // This animates fading of SecurityFooter and media divider
     private TouchAnimator mAllPagesDelayedAnimator;
     private TouchAnimator mBrightnessAnimator;
+    private HeightExpansionAnimator mQQSTileHeightAnimator;
+    private HeightExpansionAnimator mOtherTilesExpandAnimator;
+
     private boolean mNeedsAnimatorUpdate = false;
 
     private boolean mOnKeyguard;
@@ -161,7 +167,7 @@
     @Override
     public void onViewAttachedToWindow(View v) {
         mTunerService.addTunable(this, ALLOW_FANCY_ANIMATION,
-                MOVE_FULL_ROWS, QuickQSPanel.NUM_QUICK_TILES);
+                MOVE_FULL_ROWS);
     }
 
     @Override
@@ -179,9 +185,6 @@
             }
         } else if (MOVE_FULL_ROWS.equals(key)) {
             mFullRows = TunerService.parseIntegerSwitch(newValue, true);
-        } else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) {
-            mNumQuickTiles = QuickQSPanel.parseNumTiles(newValue);
-            clearAnimationState();
         }
         updateAnimators();
     }
@@ -209,6 +212,10 @@
         clearAnimationState();
         mAllViews.clear();
         mQuickQsViews.clear();
+        mQQSTileHeightAnimator = null;
+        mOtherTilesExpandAnimator = null;
+
+        mNumQuickTiles = mQuickQsPanel.getNumQuickTiles();
 
         QSTileLayout tileLayout = mQsPanelController.getTileLayout();
         mAllViews.add((View) tileLayout);
@@ -218,6 +225,9 @@
                 + mQs.getHeader().getPaddingBottom();
         firstPageBuilder.addFloat(tileLayout, "translationY", heightDiff, 0);
 
+        boolean qsSideLabelsEnabled = mFeatureFlags.isQSLabelsEnabled();
+        int qqsTileHeight = 0;
+
         for (QSTile tile : tiles) {
             QSTileView tileView = mQsPanelController.getTileView(tile);
             if (tileView == null) {
@@ -237,22 +247,47 @@
                 getRelativePosition(loc1, quickTileView.getIcon().getIconView(), view);
                 getRelativePosition(loc2, tileIcon, view);
                 final int xDiff = loc2[0] - loc1[0];
-                final int yDiff = loc2[1] - loc1[1];
-
+                int yDiff = loc2[1] - loc1[1];
 
                 if (count < tileLayout.getNumVisibleTiles()) {
+                    getRelativePosition(loc1, quickTileView, view);
+                    getRelativePosition(loc2, tileView, view);
+                    int yOffset = qsSideLabelsEnabled ? loc2[1] - loc1[1] : 0;
                     // Move the quick tile right from its location to the new one.
-                    translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
-                    translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
-
-                    // Counteract the parent translation on the tile. So we have a static base to
-                    // animate the label position off from.
-                    //firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
+                    View v = qsSideLabelsEnabled ? quickTileView.getIcon() : quickTileView;
+                    translationXBuilder.addFloat(v, "translationX", 0, xDiff);
+                    translationYBuilder.addFloat(v, "translationY", 0, yDiff - yOffset);
+                    mAllViews.add(v);
 
                     // Move the real tile from the quick tile position to its final
                     // location.
-                    translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
-                    translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0);
+                    v = qsSideLabelsEnabled ? tileIcon : tileView;
+                    translationXBuilder.addFloat(v, "translationX", -xDiff, 0);
+                    translationYBuilder.addFloat(v, "translationY", -yDiff + yOffset, 0);
+
+                    if (qsSideLabelsEnabled) {
+                        translationYBuilder.addFloat(quickTileView, "translationY", 0, yOffset);
+                        translationYBuilder.addFloat(tileView, "translationY", -yOffset, 0);
+
+                        if (mQQSTileHeightAnimator == null) {
+                            mQQSTileHeightAnimator = new HeightExpansionAnimator(this,
+                                    quickTileView.getHeight(), tileView.getHeight());
+                            qqsTileHeight = quickTileView.getHeight();
+                        }
+
+                        mQQSTileHeightAnimator.addView(quickTileView);
+                        View qqsLabelContainer = quickTileView.getLabelContainer();
+                        View qsLabelContainer = tileView.getLabelContainer();
+
+                        getRelativePosition(loc1, qqsLabelContainer, view);
+                        getRelativePosition(loc2, qsLabelContainer, view);
+                        yDiff = loc2[1] - loc1[1] - yOffset;
+
+                        translationYBuilder.addFloat(qqsLabelContainer, "translationY", 0, yDiff);
+                        translationYBuilder.addFloat(qsLabelContainer, "translationY", -yDiff, 0);
+                        mAllViews.add(qqsLabelContainer);
+                        mAllViews.add(qsLabelContainer);
+                    }
 
                 } else { // These tiles disappear when expanding
                     firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0);
@@ -265,7 +300,7 @@
                             translationX);
                 }
 
-                if (mFeatureFlags.isQSLabelsEnabled()) {
+                if (qsSideLabelsEnabled) {
                     mQuickQsViews.add(tileView);
                 } else {
                     mQuickQsViews.add(tileView.getIconWithBackground());
@@ -278,9 +313,29 @@
 
                 mAllViews.add(tileIcon);
             } else {
-                firstPageBuilder.addFloat(tileView, "alpha", 0, 1);
-                firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
+                if (!qsSideLabelsEnabled) {
+                    firstPageBuilder.addFloat(tileView, "alpha", 0, 1);
+                    firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
+                } else {
+                    // Pretend there's a corresponding QQS tile (for the position) that we are
+                    // expanding from.
+                    SideLabelTileLayout qqsLayout =
+                            (SideLabelTileLayout) mQuickQsPanel.getTileLayout();
+                    getRelativePosition(loc1, qqsLayout, view);
+                    getRelativePosition(loc2, tileView, view);
+                    int diff = loc2[1] - (loc1[1] + qqsLayout.getPhantomTopPosition(count));
+                    translationYBuilder.addFloat(tileView, "translationY", -diff, 0);
+                    if (mOtherTilesExpandAnimator == null) {
+                        mOtherTilesExpandAnimator =
+                                new HeightExpansionAnimator(
+                                        this, qqsTileHeight, tileView.getHeight());
+                    }
+                    mOtherTilesExpandAnimator.addView(tileView);
+                    tileView.setClipChildren(true);
+                    tileView.setClipToPadding(true);
+                }
             }
+
             mAllViews.add(tileView);
             count++;
         }
@@ -303,7 +358,7 @@
                     .build();
             // Fade in the tiles/labels as we reach the final position.
             Builder builder = new Builder()
-                    .setStartDelay(EXPANDED_TILE_DELAY)
+                    .setStartDelay(qsSideLabelsEnabled ? 0 : EXPANDED_TILE_DELAY)
                     .addFloat(tileLayout, "alpha", 0, 1);
             mFirstPageDelayedAnimator = builder.build();
 
@@ -331,6 +386,12 @@
             translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator());
             mTranslationXAnimator = translationXBuilder.build();
             mTranslationYAnimator = translationYBuilder.build();
+            if (mQQSTileHeightAnimator != null) {
+                mQQSTileHeightAnimator.setInterpolator(interpolatorBuilder.getYInterpolator());
+            }
+            if (mOtherTilesExpandAnimator != null) {
+                mOtherTilesExpandAnimator.setInterpolator(interpolatorBuilder.getYInterpolator());
+            }
         }
         mNonfirstPageAnimator = new TouchAnimator.Builder()
                 .addFloat(mQuickQsPanel, "alpha", 1, 0)
@@ -404,6 +465,12 @@
             if (mBrightnessAnimator != null) {
                 mBrightnessAnimator.setPosition(position);
             }
+            if (mQQSTileHeightAnimator != null) {
+                mQQSTileHeightAnimator.setPosition(position);
+            }
+            if (mOtherTilesExpandAnimator != null) {
+                mOtherTilesExpandAnimator.setPosition(position);
+            }
         } else {
             mNonfirstPageAnimator.setPosition(position);
             mNonfirstPageDelayedAnimator.setPosition(position);
@@ -446,6 +513,16 @@
             v.setAlpha(1);
             v.setTranslationX(0);
             v.setTranslationY(0);
+            if (v instanceof SideLabelTileLayout) {
+                ((SideLabelTileLayout) v).setClipChildren(false);
+                ((SideLabelTileLayout) v).setClipToPadding(false);
+            }
+        }
+        if (mQQSTileHeightAnimator != null) {
+            mQQSTileHeightAnimator.resetViewsHeights();
+        }
+        if (mOtherTilesExpandAnimator != null) {
+            mOtherTilesExpandAnimator.resetViewsHeights();
         }
         final int N2 = mQuickQsViews.size();
         for (int i = 0; i < N2; i++) {
@@ -483,4 +560,61 @@
         updateAnimators();
         setCurrentPosition();
     };
+
+    static class HeightExpansionAnimator {
+        private final List<View> mViews = new ArrayList<>();
+        private final ValueAnimator mAnimator;
+        private final TouchAnimator.Listener mListener;
+
+        private final ValueAnimator.AnimatorUpdateListener mUpdateListener =
+                new ValueAnimator.AnimatorUpdateListener() {
+            float mLastT = -1;
+            @Override
+            public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                float t = valueAnimator.getAnimatedFraction();
+                if (t == 0f) {
+                    mListener.onAnimationAtStart();
+                } else if (t == 1f) {
+                    mListener.onAnimationAtEnd();
+                } else if (mLastT <= 0 || mLastT == 1) {
+                    mListener.onAnimationStarted();
+                }
+                mLastT = t;
+                final int viewCount = mViews.size();
+                int height = (Integer) valueAnimator.getAnimatedValue();
+                for (int i = 0; i < viewCount; i++) {
+                    View v = mViews.get(i);
+                    v.setBottom(v.getTop() + height);
+                }
+            }
+        };
+
+        HeightExpansionAnimator(TouchAnimator.Listener listener, int startHeight, int endHeight) {
+            mListener = listener;
+            mAnimator = ValueAnimator.ofInt(startHeight, endHeight);
+            mAnimator.setRepeatCount(ValueAnimator.INFINITE);
+            mAnimator.setRepeatMode(ValueAnimator.REVERSE);
+            mAnimator.addUpdateListener(mUpdateListener);
+        }
+
+        void addView(View v) {
+            mViews.add(v);
+        }
+
+        void setInterpolator(TimeInterpolator interpolator) {
+            mAnimator.setInterpolator(interpolator);
+        }
+
+        void setPosition(float position) {
+            mAnimator.setCurrentFraction(position);
+        }
+
+        void resetViewsHeights() {
+            final int viewsCount = mViews.size();
+            for (int i = 0; i < viewsCount; i++) {
+                View v = mViews.get(i);
+                v.setBottom(v.getTop() + v.getMeasuredHeight());
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index ed308ae..7c9f0b0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -430,6 +430,7 @@
         mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
         mQSPanelController.setRevealExpansion(expansion);
         mQSPanelController.getTileLayout().setExpansion(expansion);
+        mQuickQSPanelController.getTileLayout().setExpansion(expansion);
         mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff);
         if (fullyCollapsed) {
             mQSPanelScrollView.setScrollY(0);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index ff9b912..c794a21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -315,7 +315,7 @@
         int tileBg = getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size);
         mFooterMarginStartHorizontal = getResources().getDimensionPixelSize(
                 R.dimen.qs_footer_horizontal_margin);
-        mVisualTilePadding = (int) ((tileSize - tileBg) / 2.0f);
+        mVisualTilePadding = mSideLabels ? 0 : (int) ((tileSize - tileBg) / 2.0f);
         updatePadding();
 
         updatePageIndicator();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index f51d7ef..8f99a9e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -348,6 +348,7 @@
     }
 
     static class QQSSideLabelTileLayout extends SideLabelTileLayout {
+
         QQSSideLabelTileLayout(Context context) {
             super(context, null);
             setClipChildren(false);
@@ -361,6 +362,7 @@
 
         @Override
         public boolean updateResources() {
+            mCellHeightResId = R.dimen.qs_quick_tile_size;
             boolean b = super.updateResources();
             mMaxAllowedRows = 2;
             return b;
@@ -379,5 +381,38 @@
             updateMaxRows(10000, mRecords.size());
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         }
+
+        @Override
+        public void setListening(boolean listening, UiEventLogger uiEventLogger) {
+            boolean startedListening = !mListening && listening;
+            super.setListening(listening, uiEventLogger);
+            if (startedListening) {
+                // getNumVisibleTiles() <= mRecords.size()
+                for (int i = 0; i < getNumVisibleTiles(); i++) {
+                    QSTile tile = mRecords.get(i).tile;
+                    uiEventLogger.logWithInstanceId(QSEvent.QQS_TILE_VISIBLE, 0,
+                            tile.getMetricsSpec(), tile.getInstanceId());
+                }
+            }
+        }
+
+        @Override
+        public void setExpansion(float expansion) {
+            if (expansion > 0f && expansion < 1f) {
+                return;
+            }
+            boolean selected = expansion == 0f;
+            // Expansion == 0f is when QQS is fully showing (as opposed to 1f, which is QS). At this
+            // point we want them to be selected so the tiles will marquee (but not at other points
+            // of expansion.
+            // We set it as not important while we change this, so setting each tile as selected
+            // will not cause them to announce themselves until the user has actually selected the
+            // item.
+            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+            for (int i = 0; i < getChildCount(); i++) {
+                getChildAt(i).setSelected(selected);
+            }
+            setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 671f8f7..fee56b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -99,7 +99,7 @@
                 break;
             }
         }
-        super.setTiles(tiles, !mQSLabelFlag);
+        super.setTiles(tiles, /* collapsedView */ true);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
index 52f111e7..0ebadfd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
@@ -33,4 +33,15 @@
     override fun isFull(): Boolean {
         return mRecords.size >= maxTiles()
     }
+
+    /**
+     * Return the position from the top of the layout of the tile with this index.
+     *
+     * This will return a position even for indices that go beyond [maxTiles], continuing the rows
+     * beyond that.
+     */
+    fun getPhantomTopPosition(index: Int): Int {
+        val row = index / mColumns
+        return getRowTop(row)
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index c1ce4a5..9e0aa5a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -25,6 +25,7 @@
 
     protected int mColumns;
     protected int mCellWidth;
+    protected int mCellHeightResId = R.dimen.qs_tile_height;
     protected int mCellHeight;
     protected int mMaxCellHeight;
     protected int mCellMarginHorizontal;
@@ -118,7 +119,7 @@
         final Resources res = mContext.getResources();
         mResourceColumns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
         updateColumns();
-        mMaxCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
+        mMaxCellHeight = mContext.getResources().getDimensionPixelSize(mCellHeightResId);
         mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
         mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
         mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
@@ -235,7 +236,7 @@
         layoutTileRecords(mRecords.size());
     }
 
-    private int getRowTop(int row) {
+    protected int getRowTop(int row) {
         return row * (mCellHeight + mCellMarginVertical) + mCellMarginTop;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
index f8c0dd4..7977b49 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt
@@ -14,7 +14,7 @@
 class CustomizeTileViewHorizontal(
     context: Context,
     icon: QSIconView
-) : QSTileViewHorizontal(context, icon),
+) : QSTileViewHorizontal(context, icon, collapsed = false),
     TileAdapter.CustomizeView {
 
     private var showAppLabel = false
@@ -27,6 +27,7 @@
 
     override fun handleStateChanged(state: QSTile.State) {
         super.handleStateChanged(state)
+        mShowRippleEffect = false
         mSecondLine.visibility = if (showAppLabel) View.VISIBLE else View.GONE
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 56d06eb..1699a34 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -51,6 +51,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.State;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -95,6 +96,7 @@
             QSHost host,
             Looper backgroundLooper,
             Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -102,8 +104,8 @@
             String action,
             Context userContext
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mWindowManager = WindowManagerGlobal.getWindowManagerService();
         mComponent = ComponentName.unflattenFromString(action);
         mTile = new Tile();
@@ -450,6 +452,7 @@
         final Lazy<QSHost> mQSHostLazy;
         final Looper mBackgroundLooper;
         final Handler mMainHandler;
+        private final FalsingManager mFalsingManager;
         final MetricsLogger mMetricsLogger;
         final StatusBarStateController mStatusBarStateController;
         final ActivityStarter mActivityStarter;
@@ -463,6 +466,7 @@
                 Lazy<QSHost> hostLazy,
                 @Background Looper backgroundLooper,
                 @Main Handler mainHandler,
+                FalsingManager falsingManager,
                 MetricsLogger metricsLogger,
                 StatusBarStateController statusBarStateController,
                 ActivityStarter activityStarter,
@@ -471,6 +475,7 @@
             mQSHostLazy = hostLazy;
             mBackgroundLooper = backgroundLooper;
             mMainHandler = mainHandler;
+            mFalsingManager = falsingManager;
             mMetricsLogger = metricsLogger;
             mStatusBarStateController = statusBarStateController;
             mActivityStarter = activityStarter;
@@ -496,6 +501,7 @@
                     mQSHostLazy.get(),
                     mBackgroundLooper,
                     mMainHandler,
+                    mFalsingManager,
                     mMetricsLogger,
                     mStatusBarStateController,
                     mActivityStarter,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 9e5fe73..29b9e64 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -248,10 +248,10 @@
     public QSTileView createTileView(QSTile tile, boolean collapsedView) {
         Context context = new ContextThemeWrapper(mQsHostLazy.get().getContext(), R.style.qs_theme);
         QSIconView icon = tile.createTileView(context);
-        if (collapsedView) {
+        if (mSideLabels) {
+            return new QSTileViewHorizontal(context, icon, collapsedView);
+        } else if (collapsedView) {
             return new QSTileBaseView(context, icon, collapsedView);
-        } else if (mSideLabels) {
-            return new QSTileViewHorizontal(context, icon);
         } else {
             return new com.android.systemui.qs.tileimpl.QSTileView(context, icon);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 33ca7d6..a45b1319 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -58,13 +58,13 @@
     private static final int ICON_MASK_ID = com.android.internal.R.string.config_icon_mask;
     protected final Handler mHandler = new H();
     private final int[] mLocInScreen = new int[2];
-    private final FrameLayout mIconFrame;
+    protected final FrameLayout mIconFrame;
     protected QSIconView mIcon;
     protected RippleDrawable mRipple;
     protected Drawable mTileBackground;
     private String mAccessibilityClass;
     private boolean mTileState;
-    private boolean mCollapsedView;
+    protected boolean mCollapsedView;
     protected boolean mShowRippleEffect = true;
     private float mStrokeWidthActive;
     private float mStrokeWidthInactive;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 5a81676..f9d1def 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -58,6 +58,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.Prefs;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
@@ -103,6 +104,7 @@
     private final StatusBarStateController mStatusBarStateController;
     protected final ActivityStarter mActivityStarter;
     private final UiEventLogger mUiEventLogger;
+    private final FalsingManager mFalsingManager;
     private final QSLogger mQSLogger;
     private volatile int mReadyState;
 
@@ -159,6 +161,7 @@
             QSHost host,
             Looper backgroundLooper,
             Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -171,6 +174,7 @@
 
         mUiHandler = mainHandler;
         mHandler = new H(backgroundLooper);
+        mFalsingManager = falsingManager;
         mQSLogger = qsLogger;
         mMetricsLogger = metricsLogger;
         mStatusBarStateController = statusBarStateController;
@@ -608,7 +612,9 @@
                                 mContext, mEnforcedAdmin);
                         mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
                     } else {
-                        handleClick();
+                        if (!mFalsingManager.isFalseTap(true, 0.1)) {
+                            handleClick();
+                        }
                     }
                 } else if (msg.what == SECONDARY_CLICK) {
                     name = "handleSecondaryClick";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index b59326a..c7ed89b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -26,6 +26,8 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 import com.android.settingslib.Utils;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
@@ -116,7 +118,8 @@
         }
     }
 
-    private boolean shouldLabelBeSingleLine() {
+    protected boolean shouldLabelBeSingleLine() {
+        if (mCollapsedView) return true;
         if (mLabel.getLineCount() > mMaxLabelLines) {
             return true;
         } else if (!TextUtils.isEmpty(mSecondLine.getText())
@@ -138,14 +141,14 @@
             } else {
                 labelColor = mColorLabelUnavailable;
             }
-            mLabel.setTextColor(labelColor);
+            changeLabelColor(labelColor);
             mState = state.state;
             mLabel.setText(state.label);
         }
         if (!Objects.equals(mSecondLine.getText(), state.secondaryLabel)) {
             mSecondLine.setText(state.secondaryLabel);
-            mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) ? View.GONE
-                    : View.VISIBLE);
+            mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) || mCollapsedView
+                    ? View.GONE : View.VISIBLE);
         }
         boolean dualTarget = mDualTargetAllowed && state.dualTarget;
         handleExpand(dualTarget);
@@ -160,6 +163,10 @@
         mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
     }
 
+    protected void changeLabelColor(ColorStateList color) {
+        mLabel.setTextColor(color);
+    }
+
     protected void handleExpand(boolean dualTarget) {
         mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
         mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE);
@@ -178,4 +185,10 @@
     public TextView getAppLabel() {
         return mSecondLine;
     }
+
+    @Nullable
+    @Override
+    public View getLabelContainer() {
+        return mLabelContainer;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
index 328c2c3..32285cf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -21,8 +21,7 @@
 import android.content.res.ColorStateList
 import android.graphics.Color
 import android.graphics.drawable.Drawable
-import android.graphics.drawable.ShapeDrawable
-import android.graphics.drawable.shapes.RoundRectShape
+import android.graphics.drawable.RippleDrawable
 import android.service.quicksettings.Tile.STATE_ACTIVE
 import android.view.Gravity
 import android.widget.LinearLayout
@@ -32,25 +31,31 @@
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState
 
-// Placeholder
-private const val CORNER_RADIUS = 40f
-private val RADII = (1..8).map { CORNER_RADIUS }.toFloatArray()
-
 open class QSTileViewHorizontal(
     context: Context,
-    icon: QSIconView
-) : QSTileView(context, icon, false) {
+    icon: QSIconView,
+    collapsed: Boolean
+) : QSTileView(context, icon, collapsed) {
 
-    protected var backgroundDrawable: ShapeDrawable? = null
+    protected var colorBackgroundDrawable: Drawable? = null
     private var paintColor = Color.WHITE
     private var paintAnimator: ValueAnimator? = null
+    private var labelAnimator: ValueAnimator? = null
 
     init {
         orientation = HORIZONTAL
+        gravity = Gravity.CENTER_VERTICAL or Gravity.START
         mDualTargetAllowed = false
+        val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding)
+        setPadding(padding, paddingTop, padding, paddingBottom)
+
         mBg.setImageDrawable(null)
+        mIconFrame.removeAllViews()
+        removeView(mIconFrame)
+        val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
+        addView(mIcon, 0, LayoutParams(iconSize, iconSize))
+
         mColorLabelActive = ColorStateList.valueOf(getColorForState(getContext(), STATE_ACTIVE))
-        mMaxLabelLines = 3
     }
 
     override fun createLabel() {
@@ -61,65 +66,112 @@
                 removeRule(RelativeLayout.ALIGN_PARENT_TOP)
             }
         }
+        mLabelContainer.setPadding(0, 0, 0, 0)
+        (mLabelContainer.layoutParams as MarginLayoutParams).apply {
+            marginStart = context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
+        }
         mLabel.gravity = Gravity.START
         mLabel.textDirection = TEXT_DIRECTION_LOCALE
         mSecondLine.gravity = Gravity.START
         mSecondLine.textDirection = TEXT_DIRECTION_LOCALE
-        val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding)
-        mLabelContainer.setPaddingRelative(0, padding, padding, padding)
+
         (mLabelContainer.layoutParams as LayoutParams).gravity =
             Gravity.CENTER_VERTICAL or Gravity.START
+        if (mCollapsedView) {
+            mSecondLine.visibility = GONE
+        }
+    }
+
+    override fun shouldLabelBeSingleLine(): Boolean {
+        return true
     }
 
     override fun updateRippleSize() {
     }
 
     override fun newTileBackground(): Drawable? {
-        backgroundDrawable = ShapeDrawable(RoundRectShape(RADII, null, null))
-        return backgroundDrawable
+        val ripple = mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
+        colorBackgroundDrawable = ripple.findDrawableByLayerId(R.id.background)
+        return ripple
     }
 
     override fun setClickable(clickable: Boolean) {
         super.setClickable(clickable)
-        background = mTileBackground
+        background = if (clickable && mShowRippleEffect) {
+            mTileBackground
+        } else {
+            colorBackgroundDrawable
+        }
     }
 
     override fun handleStateChanged(state: QSTile.State) {
         super.handleStateChanged(state)
-        mSecondLine.setTextColor(mLabel.textColors)
         mLabelContainer.background = null
 
         val allowAnimations = animationsEnabled() && paintColor != Color.WHITE
         val newColor = getCircleColor(state.state)
         if (allowAnimations) {
-            animateToNewState(newColor)
+            animateBackground(newColor)
         } else {
             if (newColor != paintColor) {
-                clearAnimator()
-                backgroundDrawable?.setTintList(ColorStateList.valueOf(newColor))
+                clearBackgroundAnimator()
+                colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(newColor))?.also {
+                    paintColor = newColor
+                }
                 paintColor = newColor
             }
         }
     }
 
-    private fun animateToNewState(newColor: Int) {
-        if (newColor != paintColor) {
-            clearAnimator()
-            paintAnimator = ValueAnimator.ofArgb(paintColor, newColor)
+    private fun animateBackground(newBackgroundColor: Int) {
+        if (newBackgroundColor != paintColor) {
+            clearBackgroundAnimator()
+            paintAnimator = ValueAnimator.ofArgb(paintColor, newBackgroundColor)
                 .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
                     addUpdateListener { animation: ValueAnimator ->
                         val c = animation.animatedValue as Int
-                        backgroundDrawable?.setTintList(ColorStateList.valueOf(c))
-                        paintColor = c
+                        colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(c))?.also {
+                            paintColor = c
+                        }
                     }
                     start()
                 }
         }
     }
 
-    private fun clearAnimator() {
+    override fun changeLabelColor(color: ColorStateList) {
+        val allowAnimations = animationsEnabled()
+        val currentColor = mLabel.textColors.defaultColor
+        if (currentColor != color.defaultColor) {
+            clearLabelAnimator()
+            if (allowAnimations) {
+                labelAnimator = ValueAnimator.ofArgb(currentColor, color.defaultColor)
+                    .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply {
+                        addUpdateListener {
+                            setLabelsColor(ColorStateList.valueOf(it.animatedValue as Int))
+                        }
+                        start()
+                }
+            } else {
+                setLabelsColor(color)
+            }
+        }
+    }
+
+    private fun setLabelsColor(color: ColorStateList) {
+        mLabel.setTextColor(color)
+        if (!mCollapsedView) {
+            mSecondLine.setTextColor(color)
+        }
+    }
+
+    private fun clearBackgroundAnimator() {
         paintAnimator?.cancel()?.also { paintAnimator = null }
     }
 
+    private fun clearLabelAnimator() {
+        labelAnimator?.cancel()?.also { labelAnimator = null }
+    }
+
     override fun handleExpand(dualTarget: Boolean) {}
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index b848590..07abb90 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -38,6 +38,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.GlobalSetting;
@@ -63,6 +64,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -70,8 +72,8 @@
             BroadcastDispatcher broadcastDispatcher,
             Lazy<ConnectivityManager> lazyConnectivityManager
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mBroadcastDispatcher = broadcastDispatcher;
         mLazyConnectivityManager = lazyConnectivityManager;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
index dede627..2945c6b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
@@ -15,6 +15,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.QSHost
@@ -30,6 +31,7 @@
     host: QSHost,
     @Background backgroundLooper: Looper,
     @Main mainHandler: Handler,
+    falsingManager: FalsingManager,
     metricsLogger: MetricsLogger,
     statusBarStateController: StatusBarStateController,
     activityStarter: ActivityStarter,
@@ -41,6 +43,7 @@
     host,
     backgroundLooper,
     mainHandler,
+    falsingManager,
     metricsLogger,
     statusBarStateController,
     activityStarter,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index bf3e4be..49d3ff9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -29,6 +29,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -59,6 +60,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -66,8 +68,8 @@
             BatteryController batteryController,
             SecureSettings secureSettings
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mBatteryController = batteryController;
         mBatteryController.observe(getLifecycle(), this);
         int currentUser = host.getUserContext().getUserId();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 1424244..56df4d8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -42,6 +42,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -70,14 +71,15 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             BluetoothController bluetoothController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = bluetoothController;
         mDetailAdapter = (BluetoothDetailAdapter) createDetailAdapter();
         mController.observe(getLifecycle(), mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
index 70287cd..0d73a5a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java
@@ -21,7 +21,6 @@
 
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
-import android.hardware.SensorPrivacyManager;
 import android.hardware.SensorPrivacyManager.Sensors.Sensor;
 import android.os.Handler;
 import android.os.Looper;
@@ -35,6 +34,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
@@ -50,13 +50,15 @@
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
             MetricsLogger metricsLogger,
+            FalsingManager falsingManager,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             IndividualSensorPrivacyController sensorPrivacyController,
             KeyguardStateController keyguardStateController) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger, sensorPrivacyController, keyguardStateController);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger, sensorPrivacyController,
+                keyguardStateController);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index f03ce2c..fa99eed 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -40,6 +40,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -81,6 +82,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -90,8 +92,8 @@
             NetworkController networkController,
             HotspotController hotspotController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = castController;
         mDetailAdapter = new CastDetailAdapter();
         mKeyguard = keyguardStateController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 6a574d1..8cc6ff2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -45,6 +45,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile.SignalState;
@@ -76,14 +77,15 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             NetworkController networkController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = networkController;
         mDataController = mController.getMobileDataController();
         mDetailAdapter = new CellularDetailAdapter();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index a6d9604..ca7cf83 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -32,6 +32,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -60,6 +61,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -67,8 +69,8 @@
             UserTracker userTracker,
             SecureSettings secureSettings
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
 
         mSetting = new SecureSetting(secureSettings, mHandler,
                 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, userTracker.getUserId()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 85f1245..61376f0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -29,6 +29,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -49,14 +50,15 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             DataSaverController dataSaverController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mDataSaverController = dataSaverController;
         mDataSaverController.observe(getLifecycle(), this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index 257d6a7..a74a50e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.QSHost
@@ -47,6 +48,7 @@
     host: QSHost,
     @Background backgroundLooper: Looper,
     @Main mainHandler: Handler,
+    falsingManager: FalsingManager,
     metricsLogger: MetricsLogger,
     statusBarStateController: StatusBarStateController,
     activityStarter: ActivityStarter,
@@ -58,6 +60,7 @@
         host,
         backgroundLooper,
         mainHandler,
+        falsingManager,
         metricsLogger,
         statusBarStateController,
         activityStarter,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 7ec2691..4b96cf3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -54,6 +54,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -87,6 +88,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -94,8 +96,8 @@
             ZenModeController zenModeController,
             @Main SharedPreferences sharedPreferences
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = zenModeController;
         mSharedPreferences = sharedPreferences;
         mDetailAdapter = new DndDetailAdapter();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index cd45082..31a98db 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -30,6 +30,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -51,14 +52,15 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             FlashlightController flashlightController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mFlashlightController = flashlightController;
         mFlashlightController.observe(getLifecycle(), this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index a45d94a..4e0f634 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -32,6 +32,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -57,6 +58,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -64,8 +66,8 @@
             HotspotController hotspotController,
             DataSaverController dataSaverController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mHotspotController = hotspotController;
         mDataSaverController = dataSaverController;
         mHotspotController.observe(this, mCallbacks);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index e1a1fd2..14a3fc0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -40,9 +40,9 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.plugins.qs.QSTile.Icon;
 import com.android.systemui.plugins.qs.QSTile.SignalState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.AlphaControlledSignalTileView;
@@ -80,14 +80,15 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             NetworkController networkController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = networkController;
         mDataController = mController.getMobileDataController();
         mController.observe(getLifecycle(), mSignalCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index d502d06..830a1fd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -30,6 +30,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -55,6 +56,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -62,8 +64,8 @@
             LocationController locationController,
             KeyguardStateController keyguardStateController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = locationController;
         mKeyguard = keyguardStateController;
         mController.observe(this, mCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
index e9b712d..b8d8792 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java
@@ -21,7 +21,6 @@
 
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
-import android.hardware.SensorPrivacyManager;
 import android.hardware.SensorPrivacyManager.Sensors.Sensor;
 import android.os.Handler;
 import android.os.Looper;
@@ -35,6 +34,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.logging.QSLogger;
@@ -50,13 +50,15 @@
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
             MetricsLogger metricsLogger,
+            FalsingManager falsingManager,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             IndividualSensorPrivacyController sensorPrivacyController,
             KeyguardStateController keyguardStateController) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger, sensorPrivacyController, keyguardStateController);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger, sensorPrivacyController,
+                keyguardStateController);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index 63e27796..6ac2f9ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -36,6 +36,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -60,14 +61,15 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             BroadcastDispatcher broadcastDispatcher
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mBroadcastDispatcher = broadcastDispatcher;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index d8548de..5369086 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -40,6 +40,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -78,6 +79,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -86,8 +88,8 @@
             ColorDisplayManager colorDisplayManager,
             NightDisplayListenerModule.Builder nightDisplayListenerBuilder
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mLocationController = locationController;
         mManager = colorDisplayManager;
         mNightDisplayListenerBuilder = nightDisplayListenerBuilder;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
index aec7b9a..479be65 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
@@ -30,6 +30,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -57,13 +58,14 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mReduceBrightColorsController = reduceBrightColorsController;
         mReduceBrightColorsController.observe(getLifecycle(), this);
         mIsAvailable = isAvailable;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index c46cc4f..173cc05 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -31,6 +31,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -52,14 +53,15 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             RotationLockController rotationLockController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = rotationLockController;
         mController.observe(this, mCallback);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 0f0a9a2..6845dc5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -29,6 +29,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -55,6 +56,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -62,8 +64,8 @@
             RecordingController controller,
             KeyguardDismissUtil keyguardDismissUtil
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = controller;
         mController.observe(this, mCallback);
         mKeyguardDismissUtil = keyguardDismissUtil;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
index 0c582bd..a492330 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java
@@ -29,6 +29,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -59,14 +60,15 @@
     protected SensorPrivacyToggleTile(QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             IndividualSensorPrivacyController sensorPrivacyController,
             KeyguardStateController keyguardStateController) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mSensorPrivacyController = sensorPrivacyController;
         mKeyguard = keyguardStateController;
         mSensorPrivacyController.observe(getLifecycle(), this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 78975a47..0ef5bc8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -32,6 +32,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -66,6 +67,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -74,8 +76,8 @@
             BatteryController batteryController,
             LocationController locationController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mBatteryController = batteryController;
         mUiModeManager = host.getUserContext().getSystemService(UiModeManager.class);
         mLocationController = locationController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index a6cddd3..2590f37 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -28,6 +28,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.State;
@@ -51,6 +52,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -58,8 +60,8 @@
             UserSwitcherController userSwitcherController,
             UserInfoController userInfoController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mUserSwitcherController = userSwitcherController;
         mUserInfoController = userInfoController;
         mUserInfoController.observe(getLifecycle(), this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 341e67c..dab68ed 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -37,6 +37,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
 import com.android.systemui.plugins.qs.QSTile;
@@ -77,6 +78,7 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
@@ -84,8 +86,8 @@
             NetworkController networkController,
             AccessPointController accessPointController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mController = networkController;
         mWifiController = accessPointController;
         mDetailAdapter = (WifiDetailAdapter) createDetailAdapter();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 5235b6d..c88a002 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -29,6 +29,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile.BooleanState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -50,14 +51,15 @@
             QSHost host,
             @Background Looper backgroundLooper,
             @Main Handler mainHandler,
+            FalsingManager falsingManager,
             MetricsLogger metricsLogger,
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             ManagedProfileController managedProfileController
     ) {
-        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                activityStarter, qsLogger);
+        super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                statusBarStateController, activityStarter, qsLogger);
         mProfileController = managedProfileController;
         mProfileController.observe(getLifecycle(), this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index c4fa6df..0599039 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -141,6 +141,7 @@
     private static final int MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND = 57 << MSG_SHIFT;
     //TODO(b/169175022) Update name and when feature name is locked.
     private static final int MSG_EMERGENCY_ACTION_LAUNCH_GESTURE      = 58 << MSG_SHIFT;
+    private static final int MSG_SET_NAVIGATION_BAR_LUMA_SAMPLING_ENABLED = 59 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -369,6 +370,11 @@
          * Handles a window manager shell logging command.
          */
         default void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd) {}
+
+        /**
+         * @see IStatusBar#setNavigationBarLumaSamplingEnabled(int, boolean)
+         */
+        default void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {}
     }
 
     public CommandQueue(Context context) {
@@ -1019,6 +1025,14 @@
     }
 
     @Override
+    public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+        synchronized (mLock) {
+            mHandler.obtainMessage(MSG_SET_NAVIGATION_BAR_LUMA_SAMPLING_ENABLED, displayId,
+                    enable ? 1 : 0).sendToTarget();
+        }
+    }
+
+    @Override
     public void passThroughShellCommand(String[] args, ParcelFileDescriptor pfd) {
         final FileOutputStream fos = new FileOutputStream(pfd.getFileDescriptor());
         final PrintWriter pw = new PrintWriter(fos);
@@ -1400,6 +1414,12 @@
                     }
                     args.recycle();
                     break;
+                case MSG_SET_NAVIGATION_BAR_LUMA_SAMPLING_ENABLED:
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).setNavigationBarLumaSamplingEnabled(msg.arg1,
+                                msg.arg2 != 0);
+                    }
+                    break;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 89b97ae..6b144c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -28,6 +28,7 @@
 import android.graphics.drawable.Icon;
 import android.util.AttributeSet;
 import android.util.Property;
+import android.view.ContextThemeWrapper;
 import android.view.View;
 import android.view.animation.Interpolator;
 
@@ -35,6 +36,7 @@
 
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.settingslib.Utils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.AlphaOptimizedFrameLayout;
@@ -168,6 +170,7 @@
     private Rect mIsolatedIconLocation;
     private int[] mAbsolutePosition = new int[2];
     private View mIsolatedIconForAnimation;
+    private int mThemedTextColorPrimary;
 
     public NotificationIconContainer(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -179,6 +182,10 @@
         mDotPadding = getResources().getDimensionPixelSize(R.dimen.overflow_icon_dot_padding);
         mStaticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius);
         mStaticDotDiameter = 2 * mStaticDotRadius;
+        final Context themedContext = new ContextThemeWrapper(getContext(),
+                com.android.internal.R.style.Theme_DeviceDefault_DayNight);
+        mThemedTextColorPrimary = Utils.getColorAttr(themedContext,
+                com.android.internal.R.attr.textColorPrimary).getDefaultColor();
     }
 
     @Override
@@ -806,7 +813,8 @@
                     }
                 }
                 icon.setVisibleState(visibleState, animationsAllowed);
-                icon.setIconColor(iconColor, needsCannedAnimation && animationsAllowed);
+                icon.setIconColor(mThemedTextColorPrimary,
+                        needsCannedAnimation && animationsAllowed);
                 if (animate) {
                     animateTo(icon, animationProperties);
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 626162d..ca6e53d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -575,8 +575,8 @@
             FeatureFlags featureFlags) {
         super(view, falsingManager, dozeLog, keyguardStateController,
                 (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
-                latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager,
-                ambientState);
+                statusBarKeyguardViewManager, latencyTracker, flingAnimationUtilsBuilder.get(),
+                statusBarTouchableRegionManager, ambientState);
         mView = view;
         mMetricsLogger = metricsLogger;
         mActivityManager = activityManager;
@@ -1348,7 +1348,6 @@
         super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
     }
 
-
     private boolean onQsIntercept(MotionEvent event) {
         int pointerIndex = event.findPointerIndex(mTrackingPointer);
         if (pointerIndex < 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
index 50c8e2e..66df936 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationTapHelper.java
@@ -64,7 +64,7 @@
                 mTrackTouch = event.getY() <= maxTouchableHeight;
                 break;
             case MotionEvent.ACTION_MOVE:
-                if (mTrackTouch && mFalsingManager.isFalseTap(false)) {
+                if (mTrackTouch && mFalsingManager.isFalseTap(false, 0)) {
                     makeInactive();
                     mTrackTouch = false;
                 }
@@ -78,10 +78,10 @@
 
                 // 1) See if we have confidence that we can activate after a single tap.
                 // 2) Else, see if it looks like a tap at all and check for a double-tap.
-                if (!mFalsingManager.isFalseTap(true)) {
+                if (!mFalsingManager.isFalseTap(true, 0)) {
                     makeInactive();
                     return mDoubleTapListener.onDoubleTap();
-                } else if (!mFalsingManager.isFalseTap(false)) {
+                } else if (!mFalsingManager.isFalseTap(false, 0)) {
                     if (mSlideBackListener != null && mSlideBackListener.onSlideBack()) {
                         return true;
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index b6ed3e5..3ac6937 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -167,6 +167,7 @@
     private boolean mIgnoreXTouchSlop;
     private boolean mExpandLatencyTracking;
     private final PanelView mView;
+    private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     protected final Resources mResources;
     protected final KeyguardStateController mKeyguardStateController;
     protected final SysuiStatusBarStateController mStatusBarStateController;
@@ -235,12 +236,14 @@
             FalsingManager falsingManager, DozeLog dozeLog,
             KeyguardStateController keyguardStateController,
             SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper,
+            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             LatencyTracker latencyTracker,
             FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
             AmbientState ambientState) {
         mAmbientState = ambientState;
         mView = view;
+        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View v) {
@@ -1391,8 +1394,13 @@
 
                 case MotionEvent.ACTION_UP:
                 case MotionEvent.ACTION_CANCEL:
-                    addMovement(event);
-                    endMotionEvent(event, x, y, false /* forceCancel */);
+                    if (mStatusBarKeyguardViewManager.isBouncerShowing()
+                            && mFalsingManager.isFalseTap(true, 0.5)) {
+                        endMotionEvent(event, x, y, true /* forceCancel */);
+                    } else {
+                        addMovement(event);
+                        endMotionEvent(event, x, y, false /* forceCancel */);
+                    }
                     InteractionJankMonitor monitor = InteractionJankMonitor.getInstance();
                     if (event.getActionMasked() == MotionEvent.ACTION_UP) {
                         monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 5083e33..01ada0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -76,7 +76,7 @@
  * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
  * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
  * which is in turn, reported to this class by the current
- * {@link com.android.keyguard.KeyguardViewBase}.
+ * {@link com.android.keyguard.KeyguardViewController}.
  */
 @SysUISingleton
 public class StatusBarKeyguardViewManager implements RemoteInputController.Callback,
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index ba58ed2..5cd3e57 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -52,6 +52,7 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
@@ -408,14 +409,15 @@
                 QSHost host,
                 @Background Looper backgroundLooper,
                 @Main Handler mainHandler,
+                FalsingManager falsingManager,
                 MetricsLogger metricsLogger,
                 StatusBarStateController statusBarStateController,
                 ActivityStarter activityStarter,
                 QSLogger qsLogger,
                 GarbageMonitor monitor
         ) {
-            super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                    activityStarter, qsLogger);
+            super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                    statusBarStateController, activityStarter, qsLogger);
             gm = monitor;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index a123269..1b6c612 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -480,8 +480,8 @@
     @WMSingleton
     @Provides
     static StartingWindowController provideStartingWindowController(Context context,
-            @ShellSplashscreenThread ShellExecutor executor) {
-        return new StartingWindowController(context, executor);
+            @ShellAnimationThread ShellExecutor executor, TransactionPool pool) {
+        return new StartingWindowController(context, executor, pool);
     }
 
     //
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index c2ade81..0cf343c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -36,6 +36,8 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.classifier.FalsingCollectorFake;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -69,6 +71,7 @@
     private KeyguardMessageAreaController mKeyguardMessageAreaController;
     @Mock
     private LatencyTracker mLatencyTracker;
+    private final FalsingCollector mFalsingCollector = new FalsingCollectorFake();
 
     private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
 
@@ -84,7 +87,7 @@
                 .thenReturn(mKeyguardMessageArea);
         mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
                 mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
-                mKeyguardMessageAreaControllerFactory, mLatencyTracker) {
+                mKeyguardMessageAreaControllerFactory, mLatencyTracker, mFalsingCollector) {
             @Override
             void resetState() {
             }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index c69ec1a..fc93ded 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -24,6 +24,8 @@
 import com.android.internal.widget.LockPatternView
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.classifier.FalsingCollectorFake
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -48,6 +50,7 @@
     private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback
     @Mock
     private lateinit var mLatencyTracker: LatencyTracker
+    private var mFalsingCollector: FalsingCollector = FalsingCollectorFake()
     @Mock
     private lateinit
     var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
@@ -72,7 +75,7 @@
                 .thenReturn(mKeyguardMessageAreaController)
         mKeyguardPatternViewController = KeyguardPatternViewController(mKeyguardPatternView,
         mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
-                mLatencyTracker, mKeyguardMessageAreaControllerFactory)
+                mLatencyTracker, mFalsingCollector, mKeyguardMessageAreaControllerFactory)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 31cc7bb..33a0dcd0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -33,6 +33,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.classifier.FalsingCollectorFake;
+import com.android.systemui.classifier.SingleTapClassifier;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -69,9 +70,12 @@
     private LiftToActivateListener mLiftToactivateListener;
     private FalsingCollector mFalsingCollector = new FalsingCollectorFake();
     @Mock
+    private SingleTapClassifier mSingleTapClassifier;
+    @Mock
     private View mDeleteButton;
     @Mock
     private View mOkButton;
+    private NumPadKey[] mButtons = new NumPadKey[]{};
 
     private KeyguardPinBasedInputViewController mKeyguardPinViewController;
 
@@ -83,6 +87,7 @@
         when(mPinBasedInputView.getPasswordTextViewId()).thenReturn(1);
         when(mPinBasedInputView.findViewById(1)).thenReturn(mPasswordEntry);
         when(mPinBasedInputView.isAttachedToWindow()).thenReturn(true);
+        when(mPinBasedInputView.getButtons()).thenReturn(mButtons);
         when(mPinBasedInputView.findViewById(R.id.keyguard_message_area))
                 .thenReturn(mKeyguardMessageArea);
         when(mPinBasedInputView.findViewById(R.id.delete_button))
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index b03dc94..49ba646 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -34,6 +34,8 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
@@ -83,6 +85,9 @@
     private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
     @Mock
     private ConfigurationController mConfigurationController;
+    @Mock
+    private KeyguardViewController mKeyguardViewController;
+    private FalsingManager mFalsingManager = new FalsingManagerFake();
 
     private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
 
@@ -96,7 +101,8 @@
                 mView,  mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
                 mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
                 mKeyguardStateController, mKeyguardSecurityViewFlipperController,
-                mConfigurationController).create(mSecurityCallback);
+                mConfigurationController, mKeyguardViewController, mFalsingManager)
+                .create(mSecurityCallback);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 0768618..d6f4958 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -246,20 +246,4 @@
         // THEN the illumination is hidden
         verify(mUdfpsView).stopIllumination();
     }
-
-    @Test
-    public void registersAndUnregistersViewForCallbacks() throws RemoteException {
-        mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
-                IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
-        mFgExecutor.runAllReady();
-        verify(mStatusBarStateController).addCallback(mUdfpsController.mStatusBarStateListener);
-        verify(mStatusBar).addExpansionChangedListener(
-                mUdfpsController.mStatusBarExpansionListener);
-
-        mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
-        mFgExecutor.runAllReady();
-        verify(mStatusBarStateController).removeCallback(mUdfpsController.mStatusBarStateListener);
-        verify(mStatusBar).removeExpansionChangedListener(
-                mUdfpsController.mStatusBarExpansionListener);
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
new file mode 100644
index 0000000..480b335
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+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;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
+    // Dependencies
+    @Mock
+    private UdfpsKeyguardView mView;
+    @Mock
+    private StatusBarStateController mStatusBarStateController;
+    @Mock
+    private StatusBar mStatusBar;
+
+    private UdfpsKeyguardViewController mController;
+
+    // Capture listeners so that they can be used to send events
+    @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor;
+    private StatusBarStateController.StateListener mParentListener;
+    private StatusBarStateController.StateListener mDozeListener;
+
+    @Captor private ArgumentCaptor<StatusBar.ExpansionChangedListener> mExpansionListenerCaptor;
+    private StatusBar.ExpansionChangedListener mExpansionListener;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mController = new UdfpsKeyguardViewController(
+                mView,
+                mStatusBarStateController,
+                mStatusBar);
+    }
+
+    @Test
+    public void testRegistersExpansionChangedListenerOnAttached() {
+        mController.onViewAttached();
+        captureExpansionListener();
+    }
+
+    @Test
+    public void testRegistersStatusBarStateListenersOnAttached() {
+        mController.onViewAttached();
+        captureStatusBarStateListeners();
+    }
+
+    @Test
+    public void testViewControllerQueriesSBStateOnAttached() {
+        mController.onViewAttached();
+        verify(mStatusBarStateController).getState();
+        verify(mStatusBarStateController).getDozeAmount();
+
+        final float dozeAmount = .88f;
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
+        when(mStatusBarStateController.getDozeAmount()).thenReturn(dozeAmount);
+        captureStatusBarStateListeners();
+
+        mController.onViewAttached();
+        verify(mView).setPauseAuth(true);
+        verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount);
+    }
+
+    @Test
+    public void testListenersUnregisteredOnDetached() {
+        mController.onViewAttached();
+        captureStatusBarStateListeners();
+        captureExpansionListener();
+        mController.onViewDetached();
+
+        verify(mStatusBarStateController).removeCallback(mParentListener);
+        verify(mStatusBarStateController).removeCallback(mDozeListener);
+        verify(mStatusBar).removeExpansionChangedListener(mExpansionListener);
+    }
+
+    @Test
+    public void testDozeEventsSentToView() {
+        mController.onViewAttached();
+        captureStatusBarStateListeners();
+
+        final float linear = .55f;
+        final float eased = .65f;
+        mDozeListener.onDozeAmountChanged(linear, eased);
+
+        verify(mView).onDozeAmountChanged(linear, eased);
+    }
+
+    private void captureStatusBarStateListeners() {
+        verify(mStatusBarStateController, times(2)).addCallback(mStateListenerCaptor.capture());
+        List<StatusBarStateController.StateListener> stateListeners =
+                mStateListenerCaptor.getAllValues();
+        mParentListener = stateListeners.get(0);
+        mDozeListener = stateListeners.get(1);
+    }
+
+    private void captureExpansionListener() {
+        verify(mStatusBar).addExpansionChangedListener(mExpansionListenerCaptor.capture());
+        mExpansionListener = mExpansionListenerCaptor.getValue();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index 5709ce30..b232850 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -21,10 +21,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyCollection;
 import static org.mockito.ArgumentMatchers.anyDouble;
-import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -93,7 +90,7 @@
         when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
         mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider, mDockManager,
                 mMetricsLogger, mClassifiers, mSingleTapClassfier, mDoubleTapClassifier,
-                mHistoryTracker, mFakeExecutor, DOUBLE_TAP_TIMEOUT_MS, false);
+                mHistoryTracker, false);
 
 
         ArgumentCaptor<GestureCompleteListener> gestureCompleteListenerCaptor =
@@ -161,25 +158,27 @@
     public void testIsFalseTap_BasicCheck() {
         when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mFalsedResult);
 
-        assertThat(mBrightLineFalsingManager.isFalseTap(false)).isTrue();
+        assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isTrue();
 
         when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
 
-        assertThat(mBrightLineFalsingManager.isFalseTap(false)).isFalse();
+        assertThat(mBrightLineFalsingManager.isFalseTap(false, 0)).isFalse();
     }
 
     @Test
     public void testIsFalseTap_RobustCheck_NoFaceAuth() {
         when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
+        when(mDoubleTapClassifier.classifyGesture()).thenReturn(mFalsedResult);
+        when(mHistoryTracker.falseBelief()).thenReturn(1.0);
         mFalsingDataProvider.setJustUnlockedWithFace(false);
-        assertThat(mBrightLineFalsingManager.isFalseTap(true)).isTrue();
+        assertThat(mBrightLineFalsingManager.isFalseTap(true, 0)).isTrue();
     }
 
     @Test
     public void testIsFalseTap_RobustCheck_FaceAuth() {
         when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(mPassedResult);
         when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true);
-        assertThat(mBrightLineFalsingManager.isFalseTap(true)).isFalse();
+        assertThat(mBrightLineFalsingManager.isFalseTap(true, 0)).isFalse();
     }
 
     @Test
@@ -203,43 +202,29 @@
     @Test
     public void testHistory_singleTap() {
         // When trying to classify single taps, we don't immediately add results to history.
-        mBrightLineFalsingManager.isFalseTap(false);
+        mBrightLineFalsingManager.isFalseTap(false, 0);
         mGestureCompleteListener.onGestureComplete(1000);
-
-        verify(mHistoryTracker, never()).addResults(any(), anyLong());
-
-        mFakeExecutor.advanceClockToNext();
-        mFakeExecutor.runAllReady();
-
         verify(mHistoryTracker).addResults(anyCollection(), eq(1000L));
     }
 
     @Test
     public void testHistory_multipleSingleTaps() {
         // When trying to classify single taps, we don't immediately add results to history.
-        mBrightLineFalsingManager.isFalseTap(false);
+        mBrightLineFalsingManager.isFalseTap(false, 0);
         mGestureCompleteListener.onGestureComplete(1000);
-        mBrightLineFalsingManager.isFalseTap(false);
+        mBrightLineFalsingManager.isFalseTap(false, 0);
         mGestureCompleteListener.onGestureComplete(2000);
-
-        verify(mHistoryTracker, never()).addResults(any(), anyLong());
-
-        mFakeExecutor.advanceClockToNext();
-        mFakeExecutor.runNextReady();
         verify(mHistoryTracker).addResults(anyCollection(), eq(1000L));
-        reset(mHistoryTracker);
-        mFakeExecutor.advanceClockToNext();
-        mFakeExecutor.runNextReady();
         verify(mHistoryTracker).addResults(anyCollection(), eq(2000L));
     }
 
     @Test
     public void testHistory_doubleTap() {
         // When trying to classify single taps, we don't immediately add results to history.
-        mBrightLineFalsingManager.isFalseTap(false);
+        mBrightLineFalsingManager.isFalseTap(false, 0);
         mGestureCompleteListener.onGestureComplete(1000);
         // Before checking for double tap, we may check for single-tap on the second gesture.
-        mBrightLineFalsingManager.isFalseTap(false);
+        mBrightLineFalsingManager.isFalseTap(false, 0);
         mBrightLineFalsingManager.isFalseDoubleTap();
         mGestureCompleteListener.onGestureComplete(2000);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index 23ef865..dc79b88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -36,6 +36,7 @@
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.util.sensors.ProximitySensor;
 import com.android.systemui.util.sensors.ThresholdSensor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -56,6 +57,8 @@
     @Mock
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock
+    private HistoryTracker mHistoryTracker;
+    @Mock
     private ProximitySensor mProximitySensor;
     @Mock
     private SysuiStatusBarStateController mStatusBarStateController;
@@ -67,7 +70,8 @@
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
 
         mFalsingCollector = new FalsingCollectorImpl(mFalsingDataProvider, mFalsingManager,
-                mKeyguardUpdateMonitor, mProximitySensor, mStatusBarStateController);
+                mKeyguardUpdateMonitor, mHistoryTracker, mProximitySensor,
+                mStatusBarStateController, new FakeSystemClock());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java
index 01cce35..bb7545f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/HistoryTrackerTest.java
@@ -48,14 +48,14 @@
 
     @Test
     public void testNoDataNoPenalty() {
-        assertThat(mHistoryTracker.falsePenalty()).isEqualTo(0);
+        assertThat(mHistoryTracker.falseBelief()).isEqualTo(0.5);
         assertThat(mHistoryTracker.falseConfidence()).isEqualTo(0);
     }
 
     @Test
     public void testOneResultFullConfidence() {
         addResult(true, 1);
-        assertThat(mHistoryTracker.falsePenalty()).isEqualTo(1);
+        assertThat(mHistoryTracker.falseBelief()).isWithin(0.001).of(1);
         assertThat(mHistoryTracker.falseConfidence()).isEqualTo(1);
     }
 
@@ -64,8 +64,8 @@
         addResult(true, 1);
         addResult(false, 1);
 
-        assertThat(mHistoryTracker.falsePenalty()).isEqualTo(0.5);
-        assertThat(mHistoryTracker.falseConfidence()).isEqualTo(0.5);
+        assertThat(mHistoryTracker.falseBelief()).isWithin(0.001).of(0.5);
+        assertThat(mHistoryTracker.falseConfidence()).isWithin(0.001).of(0.5);
     }
 
     @Test
@@ -73,20 +73,20 @@
         addResult(true, 1);
         addResult(true, 0);
 
-        assertThat(mHistoryTracker.falsePenalty()).isEqualTo(0.75);
-        assertThat(mHistoryTracker.falseConfidence()).isEqualTo(.75);
+        assertThat(mHistoryTracker.falseBelief()).isWithin(0.001).of(1);
+        assertThat(mHistoryTracker.falseConfidence()).isWithin(0.001).of(.75);
     }
 
     @Test
     public void testDecay() {
         addResult(true, 1);
 
-        assertThat(mHistoryTracker.falsePenalty()).isEqualTo(1);
+        assertThat(mHistoryTracker.falseBelief()).isWithin(0.001).of(1);
         assertThat(mHistoryTracker.falseConfidence()).isEqualTo(1);
 
-        mSystemClock.advanceTime(1000);
+        mSystemClock.advanceTime(9999);
 
-        assertThat(mHistoryTracker.falsePenalty()).isWithin(0.01).of(0.1);
+        assertThat(mHistoryTracker.falseBelief()).isWithin(0.005).of(0.55);
         assertThat(mHistoryTracker.falseConfidence()).isEqualTo(1);
     }
 
@@ -96,25 +96,25 @@
         mSystemClock.advanceTime(1000);
         addResult(false, .5);
 
-        assertThat(mHistoryTracker.falsePenalty()).isWithin(0.01).of(0.17);
-        assertThat(mHistoryTracker.falseConfidence()).isEqualTo(0.625);
+        assertThat(mHistoryTracker.falseBelief()).isWithin(0.01).of(0.74);
+        assertThat(mHistoryTracker.falseConfidence()).isWithin(0.001).of(0.625);
     }
 
     @Test
     public void testCompleteDecay() {
         addResult(true, 1);
 
-        assertThat(mHistoryTracker.falsePenalty()).isEqualTo(1);
+        assertThat(mHistoryTracker.falseBelief()).isWithin(0.001).of(1);
         assertThat(mHistoryTracker.falseConfidence()).isEqualTo(1);
 
-        mSystemClock.advanceTime(2999);
+        mSystemClock.advanceTime(9999);
 
-        assertThat(mHistoryTracker.falsePenalty()).isGreaterThan(0);
+        assertThat(mHistoryTracker.falseBelief()).isGreaterThan(0);
         assertThat(mHistoryTracker.falseConfidence()).isEqualTo(1);
 
         mSystemClock.advanceTime(1);
 
-        assertThat(mHistoryTracker.falsePenalty()).isEqualTo(0);
+        assertThat(mHistoryTracker.falseBelief()).isEqualTo(0.5);
         assertThat(mHistoryTracker.falseConfidence()).isEqualTo(0);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
index 1f9862c..3d4425c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt
@@ -35,6 +35,7 @@
 
 import org.junit.After
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
@@ -49,7 +50,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class SeekBarViewModelTest : SysuiTestCase() {
 
     private lateinit var viewModel: SeekBarViewModel
@@ -124,6 +125,7 @@
     }
 
     @Test
+    @Ignore
     fun updateDurationWithPlayback() {
         // GIVEN that the duration is contained within the metadata
         val duration = 12000L
@@ -146,6 +148,7 @@
     }
 
     @Test
+    @Ignore
     fun updateDurationWithoutPlayback() {
         // GIVEN that the duration is contained within the metadata
         val duration = 12000L
@@ -204,6 +207,7 @@
     }
 
     @Test
+    @Ignore
     fun updateDurationNoMetadata() {
         // GIVEN that the metadata is null
         whenever(mockController.getMetadata()).thenReturn(null)
@@ -235,6 +239,7 @@
     }
 
     @Test
+    @Ignore
     fun updateSeekAvailable() {
         // GIVEN that seek is included in actions
         val state = PlaybackState.Builder().run {
@@ -249,6 +254,7 @@
     }
 
     @Test
+    @Ignore
     fun updateSeekNotAvailable() {
         // GIVEN that seek is not included in actions
         val state = PlaybackState.Builder().run {
@@ -303,6 +309,7 @@
     }
 
     @Test
+    @Ignore
     fun onSeekProgressWithSeekStarting() {
         val pos = 42L
         with(viewModel) {
@@ -314,6 +321,7 @@
     }
 
     @Test
+    @Ignore
     fun onProgressChangedFromUser() {
         // WHEN user starts dragging the seek bar
         val pos = 42
@@ -614,6 +622,7 @@
     }
 
     @Test
+    @Ignore
     fun clearSeekBar() {
         // GIVEN that the duration is contained within the metadata
         val metadata = MediaMetadata.Builder().run {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index cfef5be..2ca8082 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -49,6 +49,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.QSFactory;
@@ -360,6 +361,7 @@
                     host,
                     mLooper.getLooper(),
                     new Handler(mLooper.getLooper()),
+                    new FalsingManagerFake(),
                     mock(MetricsLogger.class),
                     mock(StatusBarStateController.class),
                     mock(ActivityStarter.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 3aa40de..b1c3d1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -32,6 +32,7 @@
 import android.view.IWindowManager
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -103,6 +104,7 @@
                 { tileHost },
                 testableLooper.looper,
                 Handler(testableLooper.looper),
+                FalsingManagerFake(),
                 metricsLogger,
                 statusBarStateController,
                 activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 61a0d6c..937ab1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -26,6 +26,8 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_STATUS_BAR_STATE;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -55,7 +57,9 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.testing.UiEventLoggerFake;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSEvent;
@@ -90,6 +94,7 @@
     private QSTileHost mHost;
     @Mock
     private MetricsLogger mMetricsLogger;
+    private final FalsingManagerFake mFalsingManager = new FalsingManagerFake();
     @Mock
     private StatusBarStateController mStatusBarStateController;
     @Mock
@@ -112,7 +117,7 @@
 
         Handler mainHandler = new Handler(mTestableLooper.getLooper());
 
-        mTile = new TileImpl(mHost, mTestableLooper.getLooper(), mainHandler,
+        mTile = new TileImpl(mHost, mTestableLooper.getLooper(), mainHandler, mFalsingManager,
                 mMetricsLogger, mStatusBarStateController, mActivityStarter, mQsLogger);
         mTile.setTileSpec(SPEC);
     }
@@ -144,6 +149,19 @@
     }
 
     @Test
+    public void testClick_falsing() {
+        mFalsingManager.setFalseRobustTap(true);
+        mTile.click();
+        mTestableLooper.processAllMessages();
+        assertThat(mTile.mClicked).isFalse();
+
+        mFalsingManager.setFalseRobustTap(false);
+        mTile.click();
+        mTestableLooper.processAllMessages();
+        assertThat(mTile.mClicked).isTrue();
+    }
+
+    @Test
     public void testSecondaryClick_Metrics() {
         mTile.secondaryClick();
         verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_SECONDARY_CLICK)));
@@ -360,17 +378,20 @@
     }
 
     private static class TileImpl extends QSTileImpl<QSTile.BooleanState> {
+        boolean mClicked;
+
         protected TileImpl(
                 QSHost host,
                 Looper backgroundLooper,
                 Handler mainHandler,
+                FalsingManager falsingManager,
                 MetricsLogger metricsLogger,
                 StatusBarStateController statusBarStateController,
                 ActivityStarter activityStarter,
                 QSLogger qsLogger
         ) {
-            super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
-                    activityStarter, qsLogger);
+            super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+                    statusBarStateController, activityStarter, qsLogger);
             getState().state = Tile.STATE_ACTIVE;
         }
 
@@ -381,7 +402,7 @@
 
         @Override
         protected void handleClick() {
-
+            mClicked = true;
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
index a9d10e9..9674a60 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
@@ -10,6 +10,7 @@
 import com.android.internal.logging.MetricsLogger
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.QSHost
@@ -73,6 +74,7 @@
             qsHost,
             testableLooper.looper,
             Handler(testableLooper.looper),
+            FalsingManagerFake(),
             metricsLogger,
             statusBarStateController,
             activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index bcfc835..f17bd56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -24,6 +24,7 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.QSHost
@@ -79,6 +80,7 @@
                 qsHost,
                 testableLooper.looper,
                 Handler(testableLooper.looper),
+                FalsingManagerFake(),
                 metricsLogger,
                 statusBarStateController,
                 activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 1c29a81..7d39361 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -37,6 +37,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSTileHost;
@@ -101,6 +102,7 @@
                 mHost,
                 mTestableLooper.getLooper(),
                 new Handler(mTestableLooper.getLooper()),
+                new FalsingManagerFake(),
                 mMetricsLogger,
                 mStatusBarStateController,
                 mActivityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index 9363b248..9fe5687 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -28,6 +28,7 @@
 import com.android.internal.logging.MetricsLogger
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
 import com.android.systemui.controls.ControlsServiceInfo
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.dagger.ControlsComponent
@@ -323,6 +324,7 @@
                 qsHost,
                 testableLooper.looper,
                 Handler(testableLooper.looper),
+                FalsingManagerFake(),
                 metricsLogger,
                 statusBarStateController,
                 activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
index b37ac4a..99d028c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
@@ -33,6 +33,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSTileHost;
@@ -84,6 +85,7 @@
                 mHost,
                 mTestableLooper.getLooper(),
                 new Handler(mTestableLooper.getLooper()),
+                new FalsingManagerFake(),
                 mMetricsLogger,
                 mStatusBarStateController,
                 mActivityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index 880c290..6032e51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -33,6 +33,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSTileHost;
@@ -82,6 +83,7 @@
                 mHost,
                 mTestableLooper.getLooper(),
                 new Handler(mTestableLooper.getLooper()),
+                new FalsingManagerFake(),
                 mMetricsLogger,
                 mStatusBarStateController,
                 mActivityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 6b54791..2215433 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -33,6 +33,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSTileHost;
@@ -81,6 +82,7 @@
                 mHost,
                 mTestableLooper.getLooper(),
                 new Handler(mTestableLooper.getLooper()),
+                new FalsingManagerFake(),
                 mMetricsLogger,
                 mStatusBarStateController,
                 mActivityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 2917dfa..8ec03d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -476,4 +476,11 @@
         waitForIdleSync();
         verify(mCallbacks).requestWindowMagnificationConnection(true);
     }
+
+    @Test
+    public void testSetEnableNavigationBarLumaSampling() {
+        mCommandQueue.setNavigationBarLumaSamplingEnabled(1, true);
+        waitForIdleSync();
+        verify(mCallbacks).setNavigationBarLumaSamplingEnabled(eq(1), eq(true));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index a844d09..a60baa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -45,6 +45,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -86,6 +87,7 @@
                 mRemoteInputQuickSettingsDisabler);
         mDependency.injectTestDependency(LightBarController.class,
                 mLightBarController);
+        mDependency.injectMockDependency(NotificationRemoteInputManager.class);
 
         mReceiver = new BlockingQueueIntentReceiver();
         mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION), null,
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 8ccfad6..b00689b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -98,7 +98,6 @@
         ":platform-compat-overrides",
         ":display-device-config",
         ":display-layout-config",
-        ":cec-config",
         ":device-state-config",
         "java/com/android/server/EventLogTags.logtags",
         "java/com/android/server/am/EventLogTags.logtags",
@@ -119,7 +118,6 @@
     ],
 
     required: [
-        "cec_config.xml",
         "gps_debug.conf",
         "protolog.conf.json.gz",
     ],
@@ -185,11 +183,6 @@
 }
 
 prebuilt_etc {
-    name: "cec_config.xml",
-    src: "java/com/android/server/hdmi/cec_config.xml",
-}
-
-prebuilt_etc {
     name: "gps_debug.conf",
     src: "java/com/android/server/location/gnss/gps_debug.conf",
 }
@@ -223,6 +216,7 @@
         "java/com/android/server/TestNetworkService.java",
         "java/com/android/server/connectivity/AutodestructReference.java",
         "java/com/android/server/connectivity/ConnectivityConstants.java",
+        "java/com/android/server/connectivity/ConnectivityResources.java",
         "java/com/android/server/connectivity/DnsManager.java",
         "java/com/android/server/connectivity/KeepaliveTracker.java",
         "java/com/android/server/connectivity/LingerMonitor.java",
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index bb4bbd5..1e608f5 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -918,19 +918,7 @@
                 int opts = parseOptions(shell);
                 getContext().enforceCallingOrSelfPermission(
                         android.Manifest.permission.DEVICE_POWER, null);
-                if (!mUpdatesStopped) {
-                    copy(mLastHealthInfo, mHealthInfo);
-                }
-                mHealthInfo.chargerAcOnline = false;
-                mHealthInfo.chargerUsbOnline = false;
-                mHealthInfo.chargerWirelessOnline = false;
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    mUpdatesStopped = true;
-                    processValuesFromShellLocked(pw, opts);
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
+                unplugBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
             } break;
             case "set": {
                 int opts = parseOptions(shell);
@@ -990,7 +978,8 @@
                         final long ident = Binder.clearCallingIdentity();
                         try {
                             mUpdatesStopped = true;
-                            processValuesFromShellLocked(pw, opts);
+                            processValuesLocked(
+                                    /* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
                         } finally {
                             Binder.restoreCallingIdentity(ident);
                         }
@@ -1004,30 +993,12 @@
                 int opts = parseOptions(shell);
                 getContext().enforceCallingOrSelfPermission(
                         android.Manifest.permission.DEVICE_POWER, null);
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    if (mUpdatesStopped) {
-                        mUpdatesStopped = false;
-                        copy(mHealthInfo, mLastHealthInfo);
-                        processValuesFromShellLocked(pw, opts);
-                    }
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-                if (mBatteryInputSuspended) {
-                    PowerProperties.battery_input_suspended(false);
-                    mBatteryInputSuspended = false;
-                }
+                resetBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw);
             } break;
             case "suspend_input": {
-                if (!Build.IS_DEBUGGABLE) {
-                    throw new SecurityException(
-                            "battery suspend_input is only supported on debuggable builds");
-                }
                 getContext().enforceCallingOrSelfPermission(
                         android.Manifest.permission.DEVICE_POWER, null);
-                PowerProperties.battery_input_suspended(true);
-                mBatteryInputSuspended = true;
+                suspendBatteryInput();
             } break;
             default:
                 return shell.handleDefaultCommands(cmd);
@@ -1035,9 +1006,59 @@
         return 0;
     }
 
-    private void processValuesFromShellLocked(PrintWriter pw, int opts) {
-        processValuesLocked((opts & OPTION_FORCE_UPDATE) != 0);
-        if ((opts & OPTION_FORCE_UPDATE) != 0) {
+    private void setChargerAcOnline(boolean online, boolean forceUpdate) {
+        if (!mUpdatesStopped) {
+            copy(mLastHealthInfo, mHealthInfo);
+        }
+        mHealthInfo.chargerAcOnline = online;
+        mUpdatesStopped = true;
+        Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate));
+    }
+
+    private void setBatteryLevel(int level, boolean forceUpdate) {
+        if (!mUpdatesStopped) {
+            copy(mLastHealthInfo, mHealthInfo);
+        }
+        mHealthInfo.batteryLevel = level;
+        mUpdatesStopped = true;
+        Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate));
+    }
+
+    private void unplugBattery(boolean forceUpdate, PrintWriter pw) {
+        if (!mUpdatesStopped) {
+            copy(mLastHealthInfo, mHealthInfo);
+        }
+        mHealthInfo.chargerAcOnline = false;
+        mHealthInfo.chargerUsbOnline = false;
+        mHealthInfo.chargerWirelessOnline = false;
+        mUpdatesStopped = true;
+        Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw));
+    }
+
+    private void resetBattery(boolean forceUpdate, @Nullable PrintWriter pw) {
+        if (mUpdatesStopped) {
+            mUpdatesStopped = false;
+            copy(mHealthInfo, mLastHealthInfo);
+            Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw));
+        }
+        if (mBatteryInputSuspended) {
+            PowerProperties.battery_input_suspended(false);
+            mBatteryInputSuspended = false;
+        }
+    }
+
+    private void suspendBatteryInput() {
+        if (!Build.IS_DEBUGGABLE) {
+            throw new SecurityException(
+                    "battery suspend_input is only supported on debuggable builds");
+        }
+        PowerProperties.battery_input_suspended(true);
+        mBatteryInputSuspended = true;
+    }
+
+    private void processValuesLocked(boolean forceUpdate, @Nullable PrintWriter pw) {
+        processValuesLocked(forceUpdate);
+        if (pw != null && forceUpdate) {
             pw.println(mSequence);
         }
     }
@@ -1363,6 +1384,41 @@
                 return mInvalidCharger;
             }
         }
+
+        @Override
+        public void setChargerAcOnline(boolean online, boolean forceUpdate) {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+            BatteryService.this.setChargerAcOnline(online, forceUpdate);
+        }
+
+        @Override
+        public void setBatteryLevel(int level, boolean forceUpdate) {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+            BatteryService.this.setBatteryLevel(level, forceUpdate);
+        }
+
+        @Override
+        public void unplugBattery(boolean forceUpdate) {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+            BatteryService.this.unplugBattery(forceUpdate, /* printWriter= */ null);
+        }
+
+        @Override
+        public void resetBattery(boolean forceUpdate) {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+            BatteryService.this.resetBattery(forceUpdate, /* printWriter= */ null);
+        }
+
+        @Override
+        public void suspendBatteryInput() {
+            getContext().enforceCallingOrSelfPermission(
+                    android.Manifest.permission.DEVICE_POWER, /* message= */ null);
+            BatteryService.this.suspendBatteryInput();
+        }
     }
 
     /**
@@ -1539,6 +1595,8 @@
                             if (Objects.equals(newService, oldService)) return;
 
                             Slog.i(TAG, "health: new instance registered " + mInstanceName);
+                            // #init() may be called with null callback. Skip null callbacks.
+                            if (mCallback == null) return;
                             mCallback.onRegistration(oldService, newService, mInstanceName);
                         } catch (NoSuchElementException | RemoteException ex) {
                             Slog.e(TAG, "health: Cannot get instance '" + mInstanceName
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 78853c7..b4fcaee 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -85,6 +85,7 @@
 import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
 import android.net.ConnectivityDiagnosticsManager.DataStallReport;
 import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
 import android.net.DataStallReportParcelable;
 import android.net.DnsResolverServiceManager;
 import android.net.ICaptivePortal;
@@ -187,10 +188,8 @@
 import android.util.SparseIntArray;
 
 import com.android.connectivity.aidl.INetworkAgent;
-import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.LocationPermissionChecker;
 import com.android.internal.util.MessageUtils;
@@ -202,6 +201,7 @@
 import com.android.net.module.util.NetworkCapabilitiesUtils;
 import com.android.net.module.util.PermissionUtils;
 import com.android.server.connectivity.AutodestructReference;
+import com.android.server.connectivity.ConnectivityResources;
 import com.android.server.connectivity.DnsManager;
 import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
 import com.android.server.connectivity.KeepaliveTracker;
@@ -317,6 +317,7 @@
     private boolean mRestrictBackground;
 
     private final Context mContext;
+    private final ConnectivityResources mResources;
     // The Context is created for UserHandle.ALL.
     private final Context mUserAllContext;
     private final Dependencies mDeps;
@@ -344,8 +345,7 @@
     private String mCurrentTcpBufferSizes;
 
     private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(
-            new Class[] { AsyncChannel.class, ConnectivityService.class, NetworkAgent.class,
-                    NetworkAgentInfo.class });
+            new Class[] { ConnectivityService.class, NetworkAgent.class, NetworkAgentInfo.class });
 
     private enum ReapUnvalidatedNetworks {
         // Tear down networks that have no chance (e.g. even if validated) of becoming
@@ -605,7 +605,6 @@
     private Intent mInitialBroadcast;
 
     private PowerManager.WakeLock mNetTransitionWakeLock;
-    private int mNetTransitionWakeLockTimeout;
     private final PowerManager.WakeLock mPendingIntentWakeLock;
 
     // A helper object to track the current default HTTP proxy. ConnectivityService needs to tell
@@ -1013,6 +1012,13 @@
         }
 
         /**
+         * Get the {@link ConnectivityResources} to use in ConnectivityService.
+         */
+        public ConnectivityResources getResources(@NonNull Context ctx) {
+            return new ConnectivityResources(ctx);
+        }
+
+        /**
          * Create a HandlerThread to use in ConnectivityService.
          */
         public HandlerThread makeHandlerThread() {
@@ -1094,13 +1100,15 @@
         mSystemProperties = mDeps.getSystemProperties();
         mNetIdManager = mDeps.makeNetIdManager();
         mContext = Objects.requireNonNull(context, "missing Context");
+        mResources = deps.getResources(mContext);
         mNetworkRequestCounter = new PerUidCounter(MAX_NETWORK_REQUESTS_PER_UID);
 
         mMetricsLog = logger;
         mNetworkRanker = new NetworkRanker();
         final NetworkRequest defaultInternetRequest = createDefaultRequest();
         mDefaultRequest = new NetworkRequestInfo(
-                defaultInternetRequest, null, new Binder(),
+                defaultInternetRequest, null,
+                new Binder(), NetworkCallback.FLAG_INCLUDE_LOCATION_INFO,
                 null /* attributionTags */);
         mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
         mDefaultNetworkRequests.add(mDefaultRequest);
@@ -1154,8 +1162,6 @@
         final PowerManager powerManager = (PowerManager) context.getSystemService(
                 Context.POWER_SERVICE);
         mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-        mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_networkTransitionTimeout);
         mPendingIntentWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
 
         mNetConfigs = new NetworkConfig[ConnectivityManager.MAX_NETWORK_TYPE+1];
@@ -1222,10 +1228,6 @@
             }
         }
 
-        mWolSupportedInterfaces = new ArraySet(
-                mContext.getResources().getStringArray(
-                        com.android.internal.R.array.config_wakeonlan_supported_interfaces));
-
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
 
         mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
@@ -1356,7 +1358,9 @@
 
         if (enable) {
             handleRegisterNetworkRequest(new NetworkRequestInfo(
-                    networkRequest, null, new Binder(),
+                    networkRequest, null,
+                    new Binder(),
+                    NetworkCallback.FLAG_INCLUDE_LOCATION_INFO,
                     null /* attributionTags */));
         } else {
             handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID,
@@ -1717,8 +1721,8 @@
                 result.put(
                         nai.network,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                nc, mDeps.getCallingUid(), callingPackageName,
-                                callingAttributionTag));
+                                nc, false /* includeLocationSensitiveInfo */,
+                                mDeps.getCallingUid(), callingPackageName, callingAttributionTag));
             }
         }
 
@@ -1731,7 +1735,9 @@
                     result.put(
                             network,
                             createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                    nc, mDeps.getCallingUid(), callingPackageName,
+                                    nc,
+                                    false /* includeLocationSensitiveInfo */,
+                                    mDeps.getCallingUid(), callingPackageName,
                                     callingAttributionTag));
                 }
             }
@@ -1813,6 +1819,7 @@
         enforceAccessPermission();
         return createWithLocationInfoSanitizedIfNecessaryWhenParceled(
                 getNetworkCapabilitiesInternal(network),
+                false /* includeLocationSensitiveInfo */,
                 mDeps.getCallingUid(), callingPackageName, callingAttributionTag);
     }
 
@@ -1846,8 +1853,8 @@
     @VisibleForTesting
     @Nullable
     NetworkCapabilities createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-            @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName,
-            @Nullable String callingAttributionTag) {
+            @Nullable NetworkCapabilities nc, boolean includeLocationSensitiveInfo,
+            int callerUid, @NonNull String callerPkgName, @Nullable String callingAttributionTag) {
         if (nc == null) {
             return null;
         }
@@ -1855,7 +1862,9 @@
         final NetworkCapabilities newNc;
         // Avoid doing location permission check if the transport info has no location sensitive
         // data.
-        if (nc.getTransportInfo() != null && nc.getTransportInfo().hasLocationSensitiveFields()) {
+        if (includeLocationSensitiveInfo
+                && nc.getTransportInfo() != null
+                && nc.getTransportInfo().hasLocationSensitiveFields()) {
             hasLocationPermission =
                     hasLocationPermission(callerUid, callerPkgName, callingAttributionTag);
             newNc = new NetworkCapabilities(nc, hasLocationPermission);
@@ -1872,6 +1881,16 @@
             // Owner UIDs already checked above. No need to re-check.
             return newNc;
         }
+        // If the caller does not want location sensitive data & target SDK >= S, then mask info.
+        // Else include the owner UID iff the caller has location permission to provide backwards
+        // compatibility for older apps.
+        if (!includeLocationSensitiveInfo
+                && isTargetSdkAtleast(
+                        Build.VERSION_CODES.S, callerUid, callerPkgName)) {
+            newNc.setOwnerUid(INVALID_UID);
+            return newNc;
+        }
+
         if (hasLocationPermission == null) {
             // Location permission not checked yet, check now for masking owner UID.
             hasLocationPermission =
@@ -2890,22 +2909,6 @@
             super(looper);
         }
 
-        private boolean maybeHandleAsyncChannelMessage(Message msg) {
-            switch (msg.what) {
-                default:
-                    return false;
-                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
-                    handleAsyncChannelHalfConnect(msg);
-                    break;
-                }
-                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
-                    handleAsyncChannelDisconnected(msg);
-                    break;
-                }
-            }
-            return true;
-        }
-
         private void maybeHandleNetworkAgentMessage(Message msg) {
             final Pair<NetworkAgentInfo, Object> arg = (Pair<NetworkAgentInfo, Object>) msg.obj;
             final NetworkAgentInfo nai = arg.first;
@@ -3197,8 +3200,7 @@
 
         @Override
         public void handleMessage(Message msg) {
-            if (!maybeHandleAsyncChannelMessage(msg)
-                    && !maybeHandleNetworkMonitorMessage(msg)
+            if (!maybeHandleNetworkMonitorMessage(msg)
                     && !maybeHandleNetworkAgentInfoMessage(msg)) {
                 maybeHandleNetworkAgentMessage(msg);
             }
@@ -3462,21 +3464,6 @@
         return false;
     }
 
-    private void handleAsyncChannelHalfConnect(Message msg) {
-        ensureRunningOnConnectivityServiceThread();
-        if (mNetworkProviderInfos.containsKey(msg.replyTo)) {
-            if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                if (VDBG) log("NetworkFactory connected");
-                // Finish setting up the full connection
-                NetworkProviderInfo npi = mNetworkProviderInfos.get(msg.replyTo);
-                sendAllRequestsToProvider(npi);
-            } else {
-                loge("Error connecting NetworkFactory");
-                mNetworkProviderInfos.remove(msg.obj);
-            }
-        }
-    }
-
     private void handleNetworkAgentRegistered(Message msg) {
         final NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj;
         if (!mNetworkAgentInfos.contains(nai)) {
@@ -3507,14 +3494,6 @@
         }
     }
 
-    // This is a no-op if it's called with a message designating a provider that has
-    // already been destroyed, because its reference will not be found in the relevant
-    // maps.
-    private void handleAsyncChannelDisconnected(Message msg) {
-        NetworkProviderInfo npi = mNetworkProviderInfos.remove(msg.replyTo);
-        if (DBG && npi != null) log("unregisterNetworkFactory for " + npi.name);
-    }
-
     // Destroys a network, remove references to it from the internal state managed by
     // ConnectivityService, free its interfaces and clean up.
     // Must be called on the Handler thread.
@@ -4623,7 +4602,9 @@
         }
         mWakelockLogs.log("ACQUIRE for " + forWhom);
         Message msg = mHandler.obtainMessage(EVENT_EXPIRE_NET_TRANSITION_WAKELOCK);
-        mHandler.sendMessageDelayed(msg, mNetTransitionWakeLockTimeout);
+        final int lockTimeout = mResources.get().getInteger(
+                com.android.connectivity.resources.R.integer.config_networkTransitionTimeout);
+        mHandler.sendMessageDelayed(msg, lockTimeout);
     }
 
     // Called when we gain a new default network to release the network transition wakelock in a
@@ -5155,8 +5136,8 @@
         private final IBinder.DeathRecipient mDeathRecipient;
         public final int providerId;
 
-        NetworkProviderInfo(String name, Messenger messenger, AsyncChannel asyncChannel,
-                int providerId, @NonNull IBinder.DeathRecipient deathRecipient) {
+        NetworkProviderInfo(String name, Messenger messenger, int providerId,
+                @NonNull IBinder.DeathRecipient deathRecipient) {
             this.name = name;
             this.messenger = messenger;
             this.providerId = providerId;
@@ -5250,6 +5231,7 @@
         private final IBinder mBinder;
         final int mPid;
         final int mUid;
+        final @NetworkCallback.Flag int mCallbackFlags;
         @Nullable
         final String mCallingAttributionTag;
         // In order to preserve the mapping of NetworkRequest-to-callback when apps register
@@ -5297,17 +5279,26 @@
             mPid = getCallingPid();
             mUid = mDeps.getCallingUid();
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            /**
+             * Location sensitive data not included in pending intent. Only included in
+             * {@link NetworkCallback}.
+             */
+            mCallbackFlags = NetworkCallback.FLAG_NONE;
             mCallingAttributionTag = callingAttributionTag;
         }
 
         NetworkRequestInfo(@NonNull final NetworkRequest r, @Nullable final Messenger m,
-                @Nullable final IBinder binder, @Nullable String callingAttributionTag) {
-            this(Collections.singletonList(r), r, m, binder, callingAttributionTag);
+                @Nullable final IBinder binder,
+                @NetworkCallback.Flag int callbackFlags,
+                @Nullable String callingAttributionTag) {
+            this(Collections.singletonList(r), r, m, binder, callbackFlags, callingAttributionTag);
         }
 
         NetworkRequestInfo(@NonNull final List<NetworkRequest> r,
                 @NonNull final NetworkRequest requestForCallback, @Nullable final Messenger m,
-                @Nullable final IBinder binder, @Nullable String callingAttributionTag) {
+                @Nullable final IBinder binder,
+                @NetworkCallback.Flag int callbackFlags,
+                @Nullable String callingAttributionTag) {
             super();
             ensureAllNetworkRequestsHaveType(r);
             mRequests = initializeRequests(r);
@@ -5318,6 +5309,7 @@
             mUid = mDeps.getCallingUid();
             mPendingIntent = null;
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            mCallbackFlags = callbackFlags;
             mCallingAttributionTag = callingAttributionTag;
 
             try {
@@ -5359,6 +5351,7 @@
             mUid = nri.mUid;
             mPendingIntent = nri.mPendingIntent;
             mNetworkRequestCounter.incrementCountOrThrow(mUid);
+            mCallbackFlags = nri.mCallbackFlags;
             mCallingAttributionTag = nri.mCallingAttributionTag;
         }
 
@@ -5408,7 +5401,8 @@
                     + " callback request Id: "
                     + mNetworkRequestForCallback.requestId
                     + " " + mRequests
-                    + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
+                    + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent)
+                    + "callback flags: " + mCallbackFlags;
         }
     }
 
@@ -5492,13 +5486,13 @@
         }
     }
 
-    private boolean checkUnsupportedStartingFrom(int version, String callingPackageName) {
-        final UserHandle user = UserHandle.getUserHandleForUid(mDeps.getCallingUid());
+    private boolean isTargetSdkAtleast(int version, int callingUid,
+            @NonNull String callingPackageName) {
+        final UserHandle user = UserHandle.getUserHandleForUid(callingUid);
         final PackageManager pm =
                 mContext.createContextAsUser(user, 0 /* flags */).getPackageManager();
         try {
-            final int callingVersion = pm.getApplicationInfo(
-                    callingPackageName, 0 /* flags */).targetSdkVersion;
+            final int callingVersion = pm.getTargetSdkVersion(callingPackageName);
             if (callingVersion < version) return false;
         } catch (PackageManager.NameNotFoundException e) { }
         return true;
@@ -5507,10 +5501,11 @@
     @Override
     public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
             int reqTypeInt, Messenger messenger, int timeoutMs, IBinder binder,
-            int legacyType, @NonNull String callingPackageName,
+            int legacyType, int callbackFlags, @NonNull String callingPackageName,
             @Nullable String callingAttributionTag) {
         if (legacyType != TYPE_NONE && !checkNetworkStackPermission()) {
-            if (checkUnsupportedStartingFrom(Build.VERSION_CODES.M, callingPackageName)) {
+            if (isTargetSdkAtleast(Build.VERSION_CODES.M, mDeps.getCallingUid(),
+                    callingPackageName)) {
                 throw new SecurityException("Insufficient permissions to specify legacy type");
             }
         }
@@ -5547,6 +5542,8 @@
                 //  request if the app changes network state. http://b/29964605
                 enforceMeteredApnPolicy(networkCapabilities);
                 break;
+            case TRACK_BEST:
+                throw new UnsupportedOperationException("Not implemented yet");
             default:
                 throw new IllegalArgumentException("Unsupported request type " + reqType);
         }
@@ -5570,7 +5567,7 @@
         final NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
                 nextNetworkRequestId(), reqType);
         final NetworkRequestInfo nri = getNriToRegister(
-                networkRequest, messenger, binder, callingAttributionTag);
+                networkRequest, messenger, binder, callbackFlags, callingAttributionTag);
         if (DBG) log("requestNetwork for " + nri);
 
         // For TRACK_SYSTEM_DEFAULT callbacks, the capabilities have been modified since they were
@@ -5605,6 +5602,7 @@
      */
     private NetworkRequestInfo getNriToRegister(@NonNull final NetworkRequest nr,
             @Nullable final Messenger msgr, @Nullable final IBinder binder,
+            @NetworkCallback.Flag int callbackFlags,
             @Nullable String callingAttributionTag) {
         final List<NetworkRequest> requests;
         if (NetworkRequest.Type.TRACK_DEFAULT == nr.type) {
@@ -5613,7 +5611,8 @@
         } else {
             requests = Collections.singletonList(nr);
         }
-        return new NetworkRequestInfo(requests, nr, msgr, binder, callingAttributionTag);
+        return new NetworkRequestInfo(
+                requests, nr, msgr, binder, callbackFlags, callingAttributionTag);
     }
 
     private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities,
@@ -5739,8 +5738,9 @@
 
     @Override
     public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
-            Messenger messenger, IBinder binder, @NonNull String callingPackageName,
-            @Nullable String callingAttributionTag) {
+            Messenger messenger, IBinder binder,
+            @NetworkCallback.Flag int callbackFlags,
+            @NonNull String callingPackageName, @NonNull String callingAttributionTag) {
         final int callingUid = mDeps.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
@@ -5761,7 +5761,8 @@
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
         NetworkRequestInfo nri =
-                new NetworkRequestInfo(networkRequest, messenger, binder, callingAttributionTag);
+                new NetworkRequestInfo(networkRequest, messenger, binder, callbackFlags,
+                        callingAttributionTag);
         if (VDBG) log("listenForNetwork for " + nri);
 
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
@@ -5830,8 +5831,7 @@
     public int registerNetworkProvider(Messenger messenger, String name) {
         enforceNetworkFactoryOrSettingsPermission();
         NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger,
-                null /* asyncChannel */, nextNetworkProviderId(),
-                () -> unregisterNetworkProvider(messenger));
+                nextNetworkProviderId(), () -> unregisterNetworkProvider(messenger));
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_PROVIDER, npi));
         return npi.providerId;
     }
@@ -6518,6 +6518,11 @@
     }
 
     private void updateWakeOnLan(@NonNull LinkProperties lp) {
+        if (mWolSupportedInterfaces == null) {
+            mWolSupportedInterfaces = new ArraySet<>(mResources.get().getStringArray(
+                    com.android.connectivity.resources.R.array
+                            .config_wakeonlan_supported_interfaces));
+        }
         lp.setWakeOnLanSupported(mWolSupportedInterfaces.contains(lp.getInterfaceName()));
     }
 
@@ -7090,6 +7095,8 @@
         if (notificationType != ConnectivityManager.CALLBACK_UNAVAIL) {
             putParcelable(bundle, networkAgent.network);
         }
+        final boolean includeLocationSensitiveInfo =
+                (nri.mCallbackFlags & NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) != 0;
         switch (notificationType) {
             case ConnectivityManager.CALLBACK_AVAILABLE: {
                 final NetworkCapabilities nc =
@@ -7098,7 +7105,8 @@
                 putParcelable(
                         bundle,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                nc, nri.mUid, nrForCallback.getRequestorPackageName(),
+                                nc, includeLocationSensitiveInfo, nri.mUid,
+                                nrForCallback.getRequestorPackageName(),
                                 nri.mCallingAttributionTag));
                 putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
                         networkAgent.linkProperties, nri.mPid, nri.mUid));
@@ -7118,7 +7126,8 @@
                 putParcelable(
                         bundle,
                         createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                                netCap, nri.mUid, nrForCallback.getRequestorPackageName(),
+                                netCap, includeLocationSensitiveInfo, nri.mUid,
+                                nrForCallback.getRequestorPackageName(),
                                 nri.mCallingAttributionTag));
                 break;
             }
@@ -8075,8 +8084,8 @@
     @Override
     public String getCaptivePortalServerUrl() {
         enforceNetworkStackOrSettingsPermission();
-        String settingUrl = mContext.getResources().getString(
-                R.string.config_networkCaptivePortalServerUrl);
+        String settingUrl = mResources.get().getString(
+                com.android.connectivity.resources.R.string.config_networkCaptivePortalServerUrl);
 
         if (!TextUtils.isEmpty(settingUrl)) {
             return settingUrl;
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 81d4b9d..4c3c6ef 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -56,6 +56,7 @@
 import android.system.OsConstants;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Range;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -756,13 +757,9 @@
         }
     }
 
-    // These values have been reserved in NetIdManager
-    @VisibleForTesting static final int TUN_INTF_NETID_START = 0xFC00;
-
-    public static final int TUN_INTF_NETID_RANGE = 0x0400;
-
     private final SparseBooleanArray mTunnelNetIds = new SparseBooleanArray();
-    private int mNextTunnelNetIdIndex = 0;
+    final Range<Integer> mNetIdRange = ConnectivityManager.getIpSecNetIdRange();
+    private int mNextTunnelNetId = mNetIdRange.getLower();
 
     /**
      * Reserves a netId within the range of netIds allocated for IPsec tunnel interfaces
@@ -775,11 +772,13 @@
      */
     @VisibleForTesting
     int reserveNetId() {
+        final int range = mNetIdRange.getUpper() - mNetIdRange.getLower() + 1;
         synchronized (mTunnelNetIds) {
-            for (int i = 0; i < TUN_INTF_NETID_RANGE; i++) {
-                int index = mNextTunnelNetIdIndex;
-                int netId = index + TUN_INTF_NETID_START;
-                if (++mNextTunnelNetIdIndex >= TUN_INTF_NETID_RANGE) mNextTunnelNetIdIndex = 0;
+            for (int i = 0; i < range; i++) {
+                final int netId = mNextTunnelNetId;
+                if (++mNextTunnelNetId > mNetIdRange.getUpper()) {
+                    mNextTunnelNetId = mNetIdRange.getLower();
+                }
                 if (!mTunnelNetIds.get(netId)) {
                     mTunnelNetIds.put(netId, true);
                     return netId;
diff --git a/services/core/java/com/android/server/NetIdManager.java b/services/core/java/com/android/server/NetIdManager.java
index 097fb3a..61925c8 100644
--- a/services/core/java/com/android/server/NetIdManager.java
+++ b/services/core/java/com/android/server/NetIdManager.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.annotation.NonNull;
+import android.net.ConnectivityManager;
 import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -31,7 +32,7 @@
     // Sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
     public static final int MIN_NET_ID = 100; // some reserved marks
     // Top IDs reserved by IpSecService
-    public static final int MAX_NET_ID = 65535 - IpSecService.TUN_INTF_NETID_RANGE;
+    public static final int MAX_NET_ID = ConnectivityManager.getIpSecNetIdRange().getLower() - 1;
 
     @GuardedBy("mNetIdInUse")
     private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray();
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index f2782f6..18d47c6 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -51,6 +51,7 @@
 import android.hardware.ISensorPrivacyListener;
 import android.hardware.ISensorPrivacyManager;
 import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManagerInternal;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
@@ -67,6 +68,7 @@
 import android.service.SensorPrivacyUserProto;
 import android.text.Html;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
@@ -80,6 +82,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FunctionalUtils;
 import com.android.internal.util.XmlUtils;
@@ -137,6 +140,8 @@
     private final ActivityManager mActivityManager;
     private final ActivityTaskManager mActivityTaskManager;
 
+    private SensorPrivacyManagerInternalImpl mSensorPrivacyManagerInternal;
+
     public SensorPrivacyService(Context context) {
         super(context);
         mUserManagerInternal = getLocalService(UserManagerInternal.class);
@@ -148,6 +153,9 @@
     @Override
     public void onStart() {
         publishBinderService(Context.SENSOR_PRIVACY_SERVICE, mSensorPrivacyServiceImpl);
+        mSensorPrivacyManagerInternal = new SensorPrivacyManagerInternalImpl();
+        publishLocalService(SensorPrivacyManagerInternal.class,
+                mSensorPrivacyManagerInternal);
     }
 
     class SensorPrivacyServiceImpl extends ISensorPrivacyManager.Stub implements
@@ -1108,6 +1116,7 @@
         }
 
         public void handleSensorPrivacyChanged(int userId, int sensor, boolean enabled) {
+            mSensorPrivacyManagerInternal.dispatch(userId, sensor, enabled);
             SparseArray<RemoteCallbackList<ISensorPrivacyListener>> listenersForUser =
                     mIndividualSensorListeners.get(userId);
             if (listenersForUser == null) {
@@ -1168,4 +1177,87 @@
             c.accept(userIds[i]);
         }
     }
+
+    private class SensorPrivacyManagerInternalImpl extends SensorPrivacyManagerInternal {
+
+        private ArrayMap<Integer, ArrayMap<Integer, ArraySet<OnSensorPrivacyChangedListener>>>
+                mListeners = new ArrayMap<>();
+        private ArrayMap<Integer, ArraySet<OnUserSensorPrivacyChangedListener>> mAllUserListeners =
+                new ArrayMap<>();
+
+        private final Object mLock = new Object();
+
+        private void dispatch(int userId, int sensor, boolean enabled) {
+            synchronized (mLock) {
+                ArraySet<OnUserSensorPrivacyChangedListener> allUserSensorListeners =
+                        mAllUserListeners.get(sensor);
+                if (allUserSensorListeners != null) {
+                    for (int i = 0; i < allUserSensorListeners.size(); i++) {
+                        OnUserSensorPrivacyChangedListener listener =
+                                allUserSensorListeners.valueAt(i);
+                        BackgroundThread.getHandler().post(() ->
+                                listener.onSensorPrivacyChanged(userId, enabled));
+                    }
+                }
+
+                ArrayMap<Integer, ArraySet<OnSensorPrivacyChangedListener>> userSensorListeners =
+                        mListeners.get(userId);
+                if (userSensorListeners != null) {
+                    ArraySet<OnSensorPrivacyChangedListener> sensorListeners =
+                            userSensorListeners.get(sensor);
+                    if (sensorListeners != null) {
+                        for (int i = 0; i < sensorListeners.size(); i++) {
+                            OnSensorPrivacyChangedListener listener = sensorListeners.valueAt(i);
+                            BackgroundThread.getHandler().post(() ->
+                                    listener.onSensorPrivacyChanged(enabled));
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
+        public boolean isSensorPrivacyEnabled(int userId, int sensor) {
+            return SensorPrivacyService.this
+                    .mSensorPrivacyServiceImpl.isIndividualSensorPrivacyEnabled(userId, sensor);
+        }
+
+        @Override
+        public void addSensorPrivacyListener(int userId, int sensor,
+                OnSensorPrivacyChangedListener listener) {
+            synchronized (mLock) {
+                ArrayMap<Integer, ArraySet<OnSensorPrivacyChangedListener>> userSensorListeners =
+                        mListeners.get(userId);
+                if (userSensorListeners == null) {
+                    userSensorListeners = new ArrayMap<>();
+                    mListeners.put(userId, userSensorListeners);
+                }
+
+                ArraySet<OnSensorPrivacyChangedListener> sensorListeners =
+                        userSensorListeners.get(sensor);
+                if (sensorListeners == null) {
+                    sensorListeners = new ArraySet<>();
+                    userSensorListeners.put(sensor, sensorListeners);
+                }
+
+                sensorListeners.add(listener);
+            }
+        }
+
+        @Override
+        public void addSensorPrivacyListenerForAllUsers(int sensor,
+                OnUserSensorPrivacyChangedListener listener) {
+            synchronized (mLock) {
+                ArraySet<OnUserSensorPrivacyChangedListener> sensorListeners =
+                        mAllUserListeners.get(sensor);
+                if (sensorListeners == null) {
+                    sensorListeners = new ArraySet<>();
+                    mAllUserListeners.put(sensor, sensorListeners);
+                }
+
+                sensorListeners.add(listener);
+            }
+        }
+    }
+
 }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index a9904ba..5adbdff 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -318,7 +318,8 @@
 
     private int[] mDataEnabledReason;
 
-    private Map<Integer, Long> mAllowedNetworkTypesList;
+    private int[] mAllowedNetworkTypeReason;
+    private long[] mAllowedNetworkTypeValue;
 
     /**
      * Per-phone map of precise data connection state. The key of the map is the pair of transport
@@ -383,7 +384,8 @@
     private boolean isPrivilegedPhoneStatePermissionRequired(Set<Integer> events) {
         return events.contains(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)
                 || events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
-                || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED);
+                || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)
+                || events.contains(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED);
     }
 
     private static final int MSG_USER_SWITCHED = 1;
@@ -527,6 +529,8 @@
         mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones);
         mIsDataEnabled= copyOf(mIsDataEnabled, mNumPhones);
         mDataEnabledReason = copyOf(mDataEnabledReason, mNumPhones);
+        mAllowedNetworkTypeReason = copyOf(mAllowedNetworkTypeReason, mNumPhones);
+        mAllowedNetworkTypeValue = copyOf(mAllowedNetworkTypeValue, mNumPhones);
 
         // ds -> ss switch.
         if (mNumPhones < oldNumPhones) {
@@ -571,6 +575,8 @@
             mIsDataEnabled[i] = false;
             mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
             mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build());
+            mAllowedNetworkTypeReason[i] = -1;
+            mAllowedNetworkTypeValue[i] = -1;
         }
     }
 
@@ -630,9 +636,11 @@
         mBarringInfo = new ArrayList<>();
         mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones];
         mPhysicalChannelConfigs = new ArrayList<>();
-        mAllowedNetworkTypesList = new HashMap<>();
+        mAllowedNetworkTypeReason = new int[numPhones];
+        mAllowedNetworkTypeValue = new long[numPhones];
         mIsDataEnabled = new boolean[numPhones];
         mDataEnabledReason = new int[numPhones];
+
         for (int i = 0; i < numPhones; i++) {
             mCallState[i] =  TelephonyManager.CALL_STATE_IDLE;
             mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -665,6 +673,8 @@
             mIsDataEnabled[i] = false;
             mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
             mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build());
+            mAllowedNetworkTypeReason[i] = -1;
+            mAllowedNetworkTypeValue[i] = -1;
         }
 
         mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -1172,14 +1182,6 @@
                         remove(r.binder);
                     }
                 }
-                if (events.contains(
-                        TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)) {
-                    try {
-                        r.callback.onAllowedNetworkTypesChanged(mAllowedNetworkTypesList);
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
-                    }
-                }
             }
         }
     }
@@ -2454,18 +2456,19 @@
      *
      * @param phoneId the phone id.
      * @param subId the subId.
-     * @param allowedNetworkTypesList Map associating all allowed network type reasons with reason's
-     *                                allowed network type values.
+     * @param reason the allowed network type reason.
+     * @param allowedNetworkType the allowed network type value.
      */
-    public void notifyAllowedNetworkTypesChanged(int phoneId, int subId,
-            Map allowedNetworkTypesList) {
+    public void notifyAllowedNetworkTypesChanged(int phoneId, int subId, int reason,
+            long allowedNetworkType) {
         if (!checkNotifyPermission("notifyAllowedNetworkTypesChanged()")) {
             return;
         }
 
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
-                mAllowedNetworkTypesList = allowedNetworkTypesList;
+                mAllowedNetworkTypeReason[phoneId] = reason;
+                mAllowedNetworkTypeValue[phoneId] = allowedNetworkType;
 
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
@@ -2473,10 +2476,12 @@
                             && idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (VDBG) {
-                                log("notifyAllowedNetworkTypesChanged: AllowedNetworkTypesList= "
-                                        + mAllowedNetworkTypesList.toString());
+                                log("notifyAllowedNetworkTypesChanged: reason= " + reason
+                                        + ", allowed network type:"
+                                        + TelephonyManager.convertNetworkTypeBitmaskToString(
+                                        allowedNetworkType));
                             }
-                            r.callback.onAllowedNetworkTypesChanged(mAllowedNetworkTypesList);
+                            r.callback.onAllowedNetworkTypesChanged(reason, allowedNetworkType);
                         } catch (RemoteException ex) {
                             mRemoveList.add(r.binder);
                         }
@@ -2531,6 +2536,8 @@
                 pw.println("mTelephonyDisplayInfo=" + mTelephonyDisplayInfos[i]);
                 pw.println("mIsDataEnabled=" + mIsDataEnabled);
                 pw.println("mDataEnabledReason=" + mDataEnabledReason);
+                pw.println("mAllowedNetworkTypeReason=" + mAllowedNetworkTypeReason[i]);
+                pw.println("mAllowedNetworkTypeValue=" + mAllowedNetworkTypeValue[i]);
                 pw.decreaseIndent();
             }
             pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index cd3892d..140f24f 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -666,6 +666,10 @@
             @NonNull IVcnUnderlyingNetworkPolicyListener listener) {
         requireNonNull(listener, "listener was null");
 
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.NETWORK_FACTORY,
+                "Must have permission NETWORK_FACTORY to unregister a policy listener");
+
         Binder.withCleanCallingIdentity(() -> {
             synchronized (mLock) {
                 PolicyListenerBinderDeath listenerBinderDeath =
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8ea6194..06a1abb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -44,11 +44,11 @@
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.FactoryTest.FACTORY_TEST_OFF;
-import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
 import static android.os.IServiceManager.DUMP_FLAG_PROTO;
+import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALLOW_LISTED;
 import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
 import static android.os.Process.BLUETOOTH_UID;
@@ -273,6 +273,9 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.WorkSource;
+import android.os.incremental.IIncrementalService;
+import android.os.incremental.IncrementalManager;
+import android.os.incremental.IncrementalMetrics;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.provider.DeviceConfig;
@@ -7697,18 +7700,32 @@
      */
     void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
             ApplicationErrorReport.CrashInfo crashInfo) {
-        boolean isPackageLoading = false;
+        boolean isIncremental = false;
+        float loadingProgress = 1;
+        long millisSinceOldestPendingRead = 0;
         // Notify package manager service to possibly update package state
         if (r != null && r.info != null && r.info.packageName != null) {
+            final String codePath = r.info.getCodePath();
             mPackageManagerInt.notifyPackageCrashOrAnr(r.info.packageName);
             IncrementalStatesInfo incrementalStatesInfo =
                     mPackageManagerInt.getIncrementalStatesInfo(r.info.packageName, r.uid,
                             r.userId);
-            isPackageLoading = incrementalStatesInfo.isLoading();
-            if (isPackageLoading) {
-                // Report in the main log that the package is still loading
-                Slog.e(TAG, "App crashed when package " + r.info.packageName + " is "
-                        + ((int) (incrementalStatesInfo.getProgress() * 100)) + "% loaded.");
+            if (incrementalStatesInfo != null) {
+                loadingProgress = incrementalStatesInfo.getProgress();
+            }
+            isIncremental = IncrementalManager.isIncrementalPath(codePath);
+            if (isIncremental) {
+                // Report in the main log about the incremental package
+                Slog.e(TAG, "App crashed on incremental package " + r.info.packageName
+                        + " which is " + ((int) (loadingProgress * 100)) + "% loaded.");
+                final IBinder incrementalService = ServiceManager.getService(
+                        Context.INCREMENTAL_SERVICE);
+                if (incrementalService != null) {
+                    final IncrementalManager incrementalManager = new IncrementalManager(
+                            IIncrementalService.Stub.asInterface(incrementalService));
+                    IncrementalMetrics metrics = incrementalManager.getMetrics(codePath);
+                    millisSinceOldestPendingRead = metrics.getMillisSinceOldestPendingRead();
+                }
             }
         }
 
@@ -7737,7 +7754,7 @@
                 processName.equals("system_server") ? ServerProtoEnums.SYSTEM_SERVER
                         : (r != null) ? r.getProcessClassEnum()
                                       : ServerProtoEnums.ERROR_SOURCE_UNKNOWN,
-                isPackageLoading
+                isIncremental, loadingProgress, millisSinceOldestPendingRead
         );
 
         final int relaunchReason = r == null ? RELAUNCH_REASON_NONE
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index dbfa7f3..6b9fc07 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -32,6 +32,7 @@
 import android.net.INetworkManagementEventObserver;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.os.BatteryManagerInternal;
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.BatteryUsageStats;
@@ -184,6 +185,8 @@
                 }
             };
 
+    private BatteryManagerInternal mBatteryManagerInternal;
+
     private void populatePowerEntityMaps() {
         PowerEntity[] entities = mPowerStatsInternal.getPowerEntityInfo();
         if (entities == null) {
@@ -370,6 +373,7 @@
                 Slog.e(TAG, "Could not register PowerStatsInternal");
             }
         }
+        mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
 
         Watchdog.getInstance().addMonitor(this);
 
@@ -2715,4 +2719,44 @@
             });
         }
     }
+
+    /**
+     * Sets battery AC charger to enabled/disabled, and freezes the battery state.
+     */
+    @Override
+    public void setChargerAcOnline(boolean online, boolean forceUpdate) {
+        mBatteryManagerInternal.setChargerAcOnline(online, forceUpdate);
+    }
+
+    /**
+     * Sets battery level, and freezes the battery state.
+     */
+    @Override
+    public void setBatteryLevel(int level, boolean forceUpdate) {
+        mBatteryManagerInternal.setBatteryLevel(level, forceUpdate);
+    }
+
+    /**
+     * Unplugs battery, and freezes the battery state.
+     */
+    @Override
+    public void unplugBattery(boolean forceUpdate) {
+        mBatteryManagerInternal.unplugBattery(forceUpdate);
+    }
+
+    /**
+     * Unfreezes battery state, returning to current hardware values.
+     */
+    @Override
+    public void resetBattery(boolean forceUpdate) {
+        mBatteryManagerInternal.resetBattery(forceUpdate);
+    }
+
+    /**
+     * Suspend charging even if plugged in.
+     */
+    @Override
+    public void suspendBatteryInput() {
+        mBatteryManagerInternal.suspendBatteryInput();
+    }
 }
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 81c4c86..e79f096 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1416,7 +1416,7 @@
             }
         }
         if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
-            r.requiredPermissions != null && r.requiredPermissions.length > 0) {
+                r.requiredPermissions != null && r.requiredPermissions.length > 0) {
             for (int i = 0; i < r.requiredPermissions.length; i++) {
                 String requiredPermission = r.requiredPermissions[i];
                 try {
@@ -1424,7 +1424,7 @@
                             checkPermission(requiredPermission,
                                     info.activityInfo.applicationInfo.packageName,
                                     UserHandle
-                                            .getUserId(info.activityInfo.applicationInfo.uid));
+                                    .getUserId(info.activityInfo.applicationInfo.uid));
                 } catch (RemoteException e) {
                     perm = PackageManager.PERMISSION_DENIED;
                 }
@@ -1439,36 +1439,18 @@
                     break;
                 }
                 int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
-                if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
-                        && mService.getAppOpsManager().noteOpNoThrow(appOp,
-                        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
-                        null /* default featureId */,
-                        "Broadcast delivered to " + info.activityInfo.name)
-                        != AppOpsManager.MODE_ALLOWED) {
-                    Slog.w(TAG, "Appop Denial: receiving "
-                            + r.intent + " to "
-                            + component.flattenToShortString()
-                            + " requires appop " + AppOpsManager.permissionToOp(
-                            requiredPermission)
-                            + " due to sender " + r.callerPackage
-                            + " (uid " + r.callingUid + ")");
-                    skip = true;
-                    break;
+                if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp) {
+                    if (!noteOpForManifestReceiver(appOp, r, info, component)) {
+                        skip = true;
+                        break;
+                    }
                 }
             }
         }
-        if (!skip && r.appOp != AppOpsManager.OP_NONE
-                && mService.getAppOpsManager().noteOpNoThrow(r.appOp,
-                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
-                null  /* default featureId */, "Broadcast delivered to " + info.activityInfo.name)
-                != AppOpsManager.MODE_ALLOWED) {
-            Slog.w(TAG, "Appop Denial: receiving "
-                    + r.intent + " to "
-                    + component.flattenToShortString()
-                    + " requires appop " + AppOpsManager.opToName(r.appOp)
-                    + " due to sender " + r.callerPackage
-                    + " (uid " + r.callingUid + ")");
-            skip = true;
+        if (!skip && r.appOp != AppOpsManager.OP_NONE) {
+            if (!noteOpForManifestReceiver(r.appOp, r, info, component)) {
+                skip = true;
+            }
         }
         boolean isSingleton = false;
         try {
@@ -1717,6 +1699,40 @@
         mPendingBroadcastRecvIndex = recIdx;
     }
 
+    private boolean noteOpForManifestReceiver(int appOp, BroadcastRecord r, ResolveInfo info,
+            ComponentName component) {
+        if (info.activityInfo.attributionTags == null) {
+            return noteOpForManifestReceiverInner(appOp, r, info, component, null);
+        } else {
+            // Attribution tags provided, noteOp each tag
+            for (String tag : info.activityInfo.attributionTags) {
+                if (!noteOpForManifestReceiverInner(appOp, r, info, component, tag)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    private boolean noteOpForManifestReceiverInner(int appOp, BroadcastRecord r, ResolveInfo info,
+            ComponentName component, String tag) {
+        if (mService.getAppOpsManager().noteOpNoThrow(appOp,
+                    info.activityInfo.applicationInfo.uid,
+                    info.activityInfo.packageName,
+                    tag,
+                    "Broadcast delivered to " + info.activityInfo.name)
+                != AppOpsManager.MODE_ALLOWED) {
+            Slog.w(TAG, "Appop Denial: receiving "
+                    + r.intent + " to "
+                    + component.flattenToShortString()
+                    + " requires appop " + AppOpsManager.opToName(appOp)
+                    + " due to sender " + r.callerPackage
+                    + " (uid " + r.callingUid + ")");
+            return false;
+        }
+        return true;
+    }
+
     private void maybeAddAllowBackgroundActivityStartsToken(ProcessRecord proc, BroadcastRecord r) {
         if (r == null || proc == null || !r.allowBackgroundActivityStarts) {
             return;
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index d03a47a..93f30cc 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -26,12 +26,18 @@
 import android.app.ApplicationErrorReport;
 import android.app.ApplicationExitInfo;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IncrementalStatesInfo;
 import android.content.pm.PackageManagerInternal;
+import android.os.IBinder;
 import android.os.Message;
 import android.os.Process;
+import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.incremental.IIncrementalService;
+import android.os.incremental.IncrementalManager;
+import android.os.incremental.IncrementalMetrics;
 import android.provider.Settings;
 import android.util.EventLog;
 import android.util.Slog;
@@ -294,14 +300,31 @@
         }
 
         // Check if package is still being loaded
-        boolean isPackageLoading = false;
+        boolean isIncremental = false;
+        float loadingProgress = 1;
+        long millisSinceOldestPendingRead = 0;
         final PackageManagerInternal packageManagerInternal = mService.getPackageManagerInternal();
         if (aInfo != null && aInfo.packageName != null) {
             IncrementalStatesInfo incrementalStatesInfo =
                     packageManagerInternal.getIncrementalStatesInfo(
                             aInfo.packageName, mApp.uid, mApp.userId);
             if (incrementalStatesInfo != null) {
-                isPackageLoading = incrementalStatesInfo.isLoading();
+                loadingProgress = incrementalStatesInfo.getProgress();
+            }
+            final String codePath = aInfo.getCodePath();
+            isIncremental = IncrementalManager.isIncrementalPath(codePath);
+            if (isIncremental) {
+                // Report in the main log that the incremental package is still loading
+                Slog.e(TAG, "App crashed on incremental package " + aInfo.packageName
+                        + " which is " + ((int) (loadingProgress * 100)) + "% loaded.");
+                final IBinder incrementalService = ServiceManager.getService(
+                        Context.INCREMENTAL_SERVICE);
+                if (incrementalService != null) {
+                    final IncrementalManager incrementalManager = new IncrementalManager(
+                            IIncrementalService.Stub.asInterface(incrementalService));
+                    IncrementalMetrics metrics = incrementalManager.getMetrics(codePath);
+                    millisSinceOldestPendingRead = metrics.getMillisSinceOldestPendingRead();
+                }
             }
         }
 
@@ -322,10 +345,8 @@
             info.append("Parent: ").append(parentShortComponentName).append("\n");
         }
 
-        if (isPackageLoading) {
-            // Report in the main log that the package is still loading
-            final float loadingProgress = packageManagerInternal.getIncrementalStatesInfo(
-                    aInfo.packageName, mApp.uid, mApp.userId).getProgress();
+        if (isIncremental) {
+            // Report in the main log about the incremental package
             info.append("Package is ").append((int) (loadingProgress * 100)).append("% loaded.\n");
         }
 
@@ -412,7 +433,8 @@
                         ? FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND
                         : FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND,
                 mApp.getProcessClassEnum(),
-                (mApp.info != null) ? mApp.info.packageName : "", isPackageLoading);
+                (mApp.info != null) ? mApp.info.packageName : "",
+                isIncremental, loadingProgress, millisSinceOldestPendingRead);
         final ProcessRecord parentPr = parentProcess != null
                 ? (ProcessRecord) parentProcess.mOwner : null;
         mService.addErrorToDropBox("anr", mApp, mApp.processName, activityShortComponentName,
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 6614e06..1122f7f 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2098,26 +2098,28 @@
         ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter,
                 beginTimeMillis, endTimeMillis, flags);
         Objects.requireNonNull(callback, "callback cannot be null");
-
         ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
-        boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid());
-        boolean isCallerSystem = Binder.getCallingPid() == Process.myPid();
-        boolean isCallerPermissionController;
-        try {
-            isCallerPermissionController = pm.getPackageUid(
-                    mContext.getPackageManager().getPermissionControllerPackageName(), 0)
-                    == Binder.getCallingUid();
-        } catch (PackageManager.NameNotFoundException doesNotHappen) {
-            return;
-        }
+        boolean isSelfRequest = (filter & FILTER_BY_UID) != 0 && uid == Binder.getCallingUid();
+        if (!isSelfRequest) {
+            boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid());
+            boolean isCallerSystem = Binder.getCallingPid() == Process.myPid();
+            boolean isCallerPermissionController;
+            try {
+                isCallerPermissionController = pm.getPackageUid(
+                        mContext.getPackageManager().getPermissionControllerPackageName(), 0)
+                        == Binder.getCallingUid();
+            } catch (PackageManager.NameNotFoundException doesNotHappen) {
+                return;
+            }
 
-        if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController) {
-            mHandler.post(() -> callback.sendResult(new Bundle()));
-            return;
-        }
+            if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController) {
+                mHandler.post(() -> callback.sendResult(new Bundle()));
+                return;
+            }
 
-        mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
-                Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
+            mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
+                    Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
+        }
 
         final String[] opNamesArray = (opNames != null)
                 ? opNames.toArray(new String[opNames.size()]) : null;
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 22d628b..eeed452 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -795,7 +795,7 @@
     private static boolean isApiEnabled() {
         return Binder.getCallingUid() == Process.myUid()
                 || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                PROPERTY_PERMISSIONS_HUB_ENABLED, false);
+                PROPERTY_PERMISSIONS_HUB_ENABLED, true);
     }
 
     private static final class Persistence {
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index e0d1375..6712c54 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -950,34 +950,36 @@
         }
         clipboard.mNotifiedUids.put(uid, true);
 
-        // Retrieve the app label of the source of the clip data
-        CharSequence sourceAppLabel = null;
-        if (clipboard.mPrimaryClipPackage != null) {
-            try {
-                sourceAppLabel = mPm.getApplicationLabel(mPm.getApplicationInfoAsUser(
-                        clipboard.mPrimaryClipPackage, 0, userId));
-            } catch (PackageManager.NameNotFoundException e) {
-                // leave label as null
+        Binder.withCleanCallingIdentity(() -> {
+            // Retrieve the app label of the source of the clip data
+            CharSequence sourceAppLabel = null;
+            if (clipboard.mPrimaryClipPackage != null) {
+                try {
+                    sourceAppLabel = mPm.getApplicationLabel(mPm.getApplicationInfoAsUser(
+                            clipboard.mPrimaryClipPackage, 0, userId));
+                } catch (PackageManager.NameNotFoundException e) {
+                    // leave label as null
+                }
             }
-        }
 
-        try {
-            CharSequence callingAppLabel = mPm.getApplicationLabel(
-                    mPm.getApplicationInfoAsUser(callingPackage, 0, userId));
-            String message;
-            if (sourceAppLabel != null) {
-                message = getContext().getString(
-                        R.string.pasted_from_app, callingAppLabel, sourceAppLabel);
-            } else {
-                message = getContext().getString(R.string.pasted_from_clipboard, callingAppLabel);
+            try {
+                CharSequence callingAppLabel = mPm.getApplicationLabel(
+                        mPm.getApplicationInfoAsUser(callingPackage, 0, userId));
+                String message;
+                if (sourceAppLabel != null) {
+                    message = getContext().getString(
+                            R.string.pasted_from_app, callingAppLabel, sourceAppLabel);
+                } else {
+                    message = getContext().getString(
+                            R.string.pasted_from_clipboard, callingAppLabel);
+                }
+                Slog.i(TAG, message);
+                Toast.makeText(
+                        getContext(), UiThread.get().getLooper(), message, Toast.LENGTH_SHORT)
+                        .show();
+            } catch (PackageManager.NameNotFoundException e) {
+                // do nothing
             }
-            Slog.i(TAG, message);
-            Binder.withCleanCallingIdentity(() ->
-                    Toast.makeText(getContext(), UiThread.get().getLooper(), message,
-                            Toast.LENGTH_SHORT)
-                            .show());
-        } catch (PackageManager.NameNotFoundException e) {
-            // do nothing
-        }
+        });
     }
 }
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 5cf478a..ae9b001 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -28,6 +28,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 
+import com.android.internal.compat.AndroidBuildClassifier;
 import com.android.internal.compat.CompatibilityChangeInfo;
 import com.android.internal.compat.OverrideAllowedState;
 import com.android.server.compat.config.Change;
@@ -55,7 +56,7 @@
      * A change ID to be used only in the CTS test for this SystemApi
      */
     @ChangeId
-    @EnabledSince(targetSdkVersion = 1235) // Needs to be > test APK targetSdkVersion.
+    @EnabledSince(targetSdkVersion = 31) // Needs to be > test APK targetSdkVersion.
     static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id.
 
     /**
@@ -233,7 +234,7 @@
      * @param app Info about the app in question
      * @return {@code true} if the change should be enabled for the package.
      */
-    boolean isEnabled(ApplicationInfo app) {
+    boolean isEnabled(ApplicationInfo app, AndroidBuildClassifier buildClassifier) {
         if (app == null) {
             return defaultValue();
         }
@@ -244,7 +245,13 @@
             return false;
         }
         if (getEnableSinceTargetSdk() != -1) {
-            return app.targetSdkVersion >= getEnableSinceTargetSdk();
+            // If the change is gated by a platform version newer than the one currently installed
+            // on the device, disregard the app's target sdk version.
+            int compareSdk = Math.min(app.targetSdkVersion, buildClassifier.platformTargetSdk());
+            if (compareSdk != app.targetSdkVersion) {
+                compareSdk = app.targetSdkVersion;
+            }
+            return compareSdk >= getEnableSinceTargetSdk();
         }
         return true;
     }
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 2c053b4..ef86f42 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -74,12 +74,14 @@
     private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
 
     private final OverrideValidatorImpl mOverrideValidator;
+    private final AndroidBuildClassifier mAndroidBuildClassifier;
     private Context mContext;
     private File mOverridesFile;
 
     @VisibleForTesting
     CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
         mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this);
+        mAndroidBuildClassifier = androidBuildClassifier;
         mContext = context;
     }
 
@@ -133,7 +135,7 @@
         synchronized (mChanges) {
             for (int i = 0; i < mChanges.size(); ++i) {
                 CompatChange c = mChanges.valueAt(i);
-                if (!c.isEnabled(app)) {
+                if (!c.isEnabled(app, mAndroidBuildClassifier)) {
                     disabled.add(c.getId());
                 }
             }
@@ -175,7 +177,7 @@
                 // we know nothing about this change: default behaviour is enabled.
                 return true;
             }
-            return c.isEnabled(app);
+            return c.isEnabled(app, mAndroidBuildClassifier);
         }
     }
 
@@ -475,7 +477,7 @@
         synchronized (mChanges) {
             for (int i = 0; i < mChanges.size(); ++i) {
                 CompatChange c = mChanges.valueAt(i);
-                if (c.isEnabled(applicationInfo)) {
+                if (c.isEnabled(applicationInfo, mAndroidBuildClassifier)) {
                     enabled.add(c.getId());
                 } else {
                     disabled.add(c.getId());
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
index fe5b4a9..aa66a1a 100644
--- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -22,6 +22,7 @@
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
 import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
+import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD;
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -85,6 +86,9 @@
         if (debuggableBuild) {
             return new OverrideAllowedState(ALLOWED, -1, -1);
         }
+        if (maxTargetSdk >= mAndroidBuildClassifier.platformTargetSdk()) {
+            return new OverrideAllowedState(PLATFORM_TOO_OLD, -1, maxTargetSdk);
+        }
         PackageManager packageManager = mContext.getPackageManager();
         if (packageManager == null) {
             throw new IllegalStateException("No PackageManager!");
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index d17753f..2be39aa 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -66,18 +66,22 @@
     private final Context mContext;
     private final ChangeReporter mChangeReporter;
     private final CompatConfig mCompatConfig;
+    private final AndroidBuildClassifier mBuildClassifier;
 
     public PlatformCompat(Context context) {
         mContext = context;
         mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
-        mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext);
+        mBuildClassifier = new AndroidBuildClassifier();
+        mCompatConfig = CompatConfig.create(mBuildClassifier, mContext);
     }
 
     @VisibleForTesting
-    PlatformCompat(Context context, CompatConfig compatConfig) {
+    PlatformCompat(Context context, CompatConfig compatConfig,
+                   AndroidBuildClassifier buildClassifier) {
         mContext = context;
         mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
         mCompatConfig = compatConfig;
+        mBuildClassifier = buildClassifier;
 
         registerPackageReceiver(context);
     }
@@ -392,7 +396,8 @@
             return false;
         }
         if (change.getEnableSinceTargetSdk() > 0) {
-            return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q;
+            return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q
+                && change.getEnableSinceTargetSdk() <= mBuildClassifier.platformTargetSdk();
         }
         return true;
     }
diff --git a/services/core/java/com/android/server/connectivity/ConnectivityResources.java b/services/core/java/com/android/server/connectivity/ConnectivityResources.java
new file mode 100644
index 0000000..45cf21e
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/ConnectivityResources.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.util.Log;
+
+import com.android.server.ConnectivityService;
+
+import java.util.List;
+
+/**
+ * Utility to obtain the {@link ConnectivityService} {@link Resources}, in the
+ * ServiceConnectivityResources APK.
+ */
+public class ConnectivityResources {
+    private static final String RESOURCES_APK_INTENT =
+            "com.android.server.connectivity.intent.action.SERVICE_CONNECTIVITY_RESOURCES_APK";
+    private static final String RES_PKG_DIR = "/apex/com.android.tethering/";
+
+    @NonNull
+    private final Context mContext;
+
+    @Nullable
+    private Resources mResources = null;
+
+    public ConnectivityResources(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Get the {@link Resources} of the ServiceConnectivityResources APK.
+     */
+    public synchronized Resources get() {
+        if (mResources != null) {
+            return mResources;
+        }
+
+        final List<ResolveInfo> pkgs = mContext.getPackageManager()
+                .queryIntentActivities(new Intent(RESOURCES_APK_INTENT), MATCH_SYSTEM_ONLY);
+        pkgs.removeIf(pkg -> !pkg.activityInfo.applicationInfo.sourceDir.startsWith(RES_PKG_DIR));
+        if (pkgs.size() > 1) {
+            Log.wtf(ConnectivityResources.class.getSimpleName(),
+                    "More than one package found: " + pkgs);
+        }
+        if (pkgs.isEmpty()) {
+            throw new IllegalStateException("No connectivity resource package found");
+        }
+
+        final Context pkgContext;
+        try {
+            pkgContext = mContext.createPackageContext(
+                    pkgs.get(0).activityInfo.applicationInfo.packageName, 0 /* flags */);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new IllegalStateException("Resolved package not found", e);
+        }
+
+        mResources = pkgContext.getResources();
+        return mResources;
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 9411e33..488677a 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -31,14 +31,17 @@
 import static com.android.net.module.util.CollectionUtils.toIntArray;
 
 import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PackageManagerInternal;
 import android.net.INetd;
 import android.net.UidRange;
+import android.net.Uri;
 import android.os.Build;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
@@ -54,7 +57,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.net.module.util.CollectionUtils;
-import com.android.server.LocalServices;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -71,7 +73,7 @@
  *
  * @hide
  */
-public class PermissionMonitor implements PackageManagerInternal.PackageListObserver {
+public class PermissionMonitor {
     private static final String TAG = "PermissionMonitor";
     private static final boolean DBG = true;
     protected static final Boolean SYSTEM = Boolean.TRUE;
@@ -83,6 +85,7 @@
     private final SystemConfigManager mSystemConfigManager;
     private final INetd mNetd;
     private final Dependencies mDeps;
+    private final Context mContext;
 
     @GuardedBy("this")
     private final Set<UserHandle> mUsers = new HashSet<>();
@@ -102,6 +105,25 @@
     @GuardedBy("this")
     private final Set<Integer> mAllApps = new HashSet<>();
 
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+            final Uri packageData = intent.getData();
+            final String packageName =
+                    packageData != null ? packageData.getSchemeSpecificPart() : null;
+
+            if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                onPackageAdded(packageName, uid);
+            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                onPackageRemoved(packageName, uid);
+            } else {
+                Log.wtf(TAG, "received unexpected intent: " + action);
+            }
+        }
+    };
+
     /**
      * Dependencies of PermissionMonitor, for injection in tests.
      */
@@ -127,6 +149,7 @@
         mSystemConfigManager = context.getSystemService(SystemConfigManager.class);
         mNetd = netd;
         mDeps = deps;
+        mContext = context;
     }
 
     // Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -134,12 +157,14 @@
     public synchronized void startMonitoring() {
         log("Monitoring");
 
-        PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
-        if (pmi != null) {
-            pmi.getPackageList(this);
-        } else {
-            loge("failed to get the PackageManagerInternal service");
-        }
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        intentFilter.addDataScheme("package");
+        mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */).registerReceiver(
+                mIntentReceiver, intentFilter, null /* broadcastPermission */,
+                null /* scheduler */);
+
         List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS
                 | MATCH_ANY_USER);
         if (apps == null) {
@@ -347,9 +372,10 @@
      *
      * @hide
      */
-    @Override
     public synchronized void onPackageAdded(@NonNull final String packageName, final int uid) {
-        sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
+        // TODO: Netd is using appId for checking traffic permission. Correct the methods that are
+        //  using appId instead of uid actually
+        sendPackagePermissionsForUid(UserHandle.getAppId(uid), getPermissionForUid(uid));
 
         // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
         // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
@@ -384,9 +410,10 @@
      *
      * @hide
      */
-    @Override
     public synchronized void onPackageRemoved(@NonNull final String packageName, final int uid) {
-        sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
+        // TODO: Netd is using appId for checking traffic permission. Correct the methods that are
+        //  using appId instead of uid actually
+        sendPackagePermissionsForUid(UserHandle.getAppId(uid), getPermissionForUid(uid));
 
         // If the newly-removed package falls within some VPN's uid range, update Netd with it.
         // This needs to happen before the mApps update below, since removeBypassingUids() depends
@@ -432,19 +459,6 @@
         }
     }
 
-    /**
-     * Called when a package is changed.
-     *
-     * @param packageName The name of the changed package.
-     * @param uid The uid of the changed package.
-     *
-     * @hide
-     */
-    @Override
-    public synchronized void onPackageChanged(@NonNull final String packageName, final int uid) {
-        sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
-    }
-
     private static int getNetdPermissionMask(String[] requestedPermissions,
                                              int[] requestedPermissionsFlags) {
         int permissions = 0;
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 027b9af..0e71496 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -791,12 +791,13 @@
     public SyncAdapterType[] getSyncAdapterTypesAsUser(int userId) {
         enforceCrossUserPermission(userId,
                 "no permission to read sync settings for user " + userId);
+        final int callingUid = Binder.getCallingUid();
         // This makes it so that future permission checks will be in the context of this
         // process rather than the caller's process. We will restore this before returning.
         final long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
-            return syncManager.getSyncAdapterTypes(userId);
+            return syncManager.getSyncAdapterTypes(callingUid, userId);
         } finally {
             restoreCallingIdentity(identityToken);
         }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index df87012..ac7e01e 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -89,6 +89,7 @@
 import android.os.UserManager;
 import android.os.WorkSource;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.text.format.TimeMigrationUtils;
 import android.util.EventLog;
 import android.util.Log;
@@ -1257,16 +1258,19 @@
                 syncExemptionFlag, callingUid, callingPid, callingPackage);
     }
 
-    public SyncAdapterType[] getSyncAdapterTypes(int userId) {
+    public SyncAdapterType[] getSyncAdapterTypes(int callingUid, int userId) {
         final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos;
         serviceInfos = mSyncAdapters.getAllServices(userId);
-        SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()];
-        int i = 0;
+        final List<SyncAdapterType> types = new ArrayList<>(serviceInfos.size());
         for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) {
-            types[i] = serviceInfo.type;
-            ++i;
+            final String packageName = serviceInfo.type.getPackageName();
+            if (!TextUtils.isEmpty(packageName) && mPackageManagerInternal.filterAppAccess(
+                    packageName, callingUid, userId)) {
+                continue;
+            }
+            types.add(serviceInfo.type);
         }
-        return types;
+        return types.toArray(new SyncAdapterType[] {});
     }
 
     public String[] getSyncAdapterPackagesForAuthorityAsUser(String authority, int userId) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 174d4b2..96a7416 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1128,7 +1128,7 @@
             recordTopInsetLocked(display);
         }
         addDisplayPowerControllerLocked(display);
-        mDisplayStates.append(displayId, Display.STATE_OFF);
+        mDisplayStates.append(displayId, Display.STATE_UNKNOWN);
         mDisplayBrightnesses.append(displayId, display.getDisplayInfoLocked().brightnessDefault);
 
         DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
@@ -1204,16 +1204,15 @@
         DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
         if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
             final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
-            final int state;
             final int displayId = display.getDisplayIdLocked();
+            final int state = mDisplayStates.get(displayId);
 
-            if (display.isEnabled()) {
-                state = mDisplayStates.get(displayId);
-            } else {
-                state = Display.STATE_OFF;
+            // Only send a request for display state if the display state has already been
+            // initialized by DisplayPowercontroller.
+            if (state != Display.STATE_UNKNOWN) {
+                final float brightness = mDisplayBrightnesses.get(displayId);
+                return device.requestDisplayStateLocked(state, brightness);
             }
-            final float brightness = mDisplayBrightnesses.get(displayId);
-            return device.requestDisplayStateLocked(state, brightness);
         }
         return null;
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 0117326..7b107b85 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -982,7 +982,8 @@
             mWaitingForNegativeProximity = false;
             mIgnoreProximityUntilChanged = false;
         }
-        if (mScreenOffBecauseOfProximity) {
+
+        if (!mLogicalDisplay.isEnabled() || mScreenOffBecauseOfProximity) {
             state = Display.STATE_OFF;
         }
 
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index d88896c..aaec89a 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -682,7 +682,10 @@
                                     || oldState == Display.STATE_ON_SUSPEND) {
                                 setDisplayState(Display.STATE_ON);
                                 currentState = Display.STATE_ON;
-                            } else {
+
+                            // If UNKNOWN, we still want to set the initial display state,
+                            // otherwise, return early.
+                            } else if (oldState != Display.STATE_UNKNOWN) {
                                 return; // old state and new state is off
                             }
                         }
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 06adce8..2b7d207 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -31,6 +31,7 @@
 import android.os.ShellCallback;
 import android.system.ErrnoException;
 import android.text.FontConfig;
+import android.text.TextUtils;
 import android.util.AndroidException;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
@@ -66,6 +67,8 @@
 
     @Override
     public FontConfig getFontConfig() {
+        getContext().enforceCallingPermission(Manifest.permission.UPDATE_FONTS,
+                "UPDATE_FONTS permission required.");
         return getSystemFontConfig();
     }
 
@@ -148,10 +151,24 @@
 
     /* package */ static class OtfFontFileParser implements UpdatableFontDir.FontFileParser {
         @Override
-        public String getPostScriptName(File file) throws IOException {
+        public String getCanonicalFileName(File file) throws IOException {
             ByteBuffer buffer = mmap(file);
             try {
-                return FontFileUtil.getPostScriptName(buffer, 0);
+                String psName = FontFileUtil.getPostScriptName(buffer, 0);
+                int isType1Font = FontFileUtil.isPostScriptType1Font(buffer, 0);
+                int isCollection = FontFileUtil.isCollectionFont(buffer);
+
+                if (TextUtils.isEmpty(psName) || isType1Font == -1 || isCollection == -1) {
+                    return null;
+                }
+
+                String extension;
+                if (isCollection == 1) {
+                    extension = isType1Font == 1 ? ".otc" : ".ttc";
+                } else {
+                    extension = isType1Font == 1 ? ".otf" : ".ttf";
+                }
+                return psName + extension;
             } finally {
                 NioUtils.freeDirectBuffer(buffer);
             }
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index 86dbe86..4f95d27 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -56,14 +56,12 @@
 
     private static final String TAG = "UpdatableFontDir";
     private static final String RANDOM_DIR_PREFIX = "~~";
-    // TODO: Support .otf
-    private static final String ALLOWED_EXTENSION = ".ttf";
 
     private static final String CONFIG_XML_FILE = "/data/fonts/config/config.xml";
 
     /** Interface to mock font file access in tests. */
     interface FontFileParser {
-        String getPostScriptName(File file) throws IOException;
+        String getCanonicalFileName(File file) throws IOException;
 
         long getRevision(File file) throws IOException;
     }
@@ -321,20 +319,20 @@
                         FontManager.RESULT_ERROR_VERIFICATION_FAILURE,
                         "Failed to setup fs-verity.", e);
             }
-            String postScriptName;
+            String canonicalFileName;
             try {
-                postScriptName = mParser.getPostScriptName(tempNewFontFile);
+                canonicalFileName = mParser.getCanonicalFileName(tempNewFontFile);
             } catch (IOException e) {
                 throw new SystemFontException(
                         FontManager.RESULT_ERROR_INVALID_FONT_FILE,
                         "Failed to read PostScript name from font file", e);
             }
-            if (postScriptName == null) {
+            if (canonicalFileName == null) {
                 throw new SystemFontException(
                         FontManager.RESULT_ERROR_INVALID_FONT_NAME,
                         "Failed to read PostScript name from font file");
             }
-            File newFontFile = new File(newDir, postScriptName + ALLOWED_EXTENSION);
+            File newFontFile = new File(newDir, canonicalFileName);
             if (!mFsverityUtil.rename(tempNewFontFile, newFontFile)) {
                 throw new SystemFontException(
                         FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
@@ -380,20 +378,38 @@
         return dir;
     }
 
+    private FontFileInfo lookupFontFileInfo(File file) {
+        String name = file.getName();
+
+        if (!name.endsWith(".ttf") && !name.endsWith(".otf") && !name.endsWith(".ttc")
+                && !name.endsWith(".otc")) {
+            return null;
+        }
+        String key = name.substring(0, name.length() - 4);
+        return mFontFileInfoMap.get(key);
+    }
+
+    private void putFontFileInfo(FontFileInfo info) {
+        String name = info.getFile().getName();
+        // The file name in FontFileInfo is already validated. Thus, just strip last 4 chars.
+        String key = name.substring(0, name.length() - 4);
+        mFontFileInfoMap.put(key, info);
+    }
+
     /**
      * Add the given {@link FontFileInfo} to {@link #mFontFileInfoMap} if its font revision is
      * higher than the currently used font file (either in {@link #mFontFileInfoMap} or {@link
      * #mPreinstalledFontDirs}).
      */
     private boolean addFileToMapIfNewer(FontFileInfo fontFileInfo, boolean deleteOldFile) {
-        String name = fontFileInfo.getFile().getName();
-        FontFileInfo existingInfo = mFontFileInfoMap.get(name);
+        FontFileInfo existingInfo = lookupFontFileInfo(fontFileInfo.getFile());
         final boolean shouldAddToMap;
         if (existingInfo == null) {
             // We got a new updatable font. We need to check if it's newer than preinstalled fonts.
             // Note that getPreinstalledFontRevision() returns -1 if there is no preinstalled font
             // with 'name'.
-            shouldAddToMap = getPreinstalledFontRevision(name) < fontFileInfo.getRevision();
+            long preInstalledRev = getPreinstalledFontRevision(fontFileInfo.getFile().getName());
+            shouldAddToMap = preInstalledRev < fontFileInfo.getRevision();
         } else {
             shouldAddToMap = existingInfo.getRevision() < fontFileInfo.getRevision();
         }
@@ -401,7 +417,7 @@
             if (deleteOldFile && existingInfo != null) {
                 FileUtils.deleteContentsAndDir(existingInfo.getRandomizedFontDir());
             }
-            mFontFileInfoMap.put(name, fontFileInfo);
+            putFontFileInfo(fontFileInfo);
         } else {
             if (deleteOldFile) {
                 FileUtils.deleteContentsAndDir(fontFileInfo.getRandomizedFontDir());
@@ -464,15 +480,18 @@
      */
     private boolean validateFontFileName(File file) {
         String fileName = file.getName();
-        String postScriptName = getPostScriptName(file);
-        return (postScriptName + ALLOWED_EXTENSION).equals(fileName);
+        String canonicalFileName = getCanonicalFileName(file);
+        if (canonicalFileName == null) {
+            return false;
+        }
+        return canonicalFileName.equals(fileName);
     }
 
     /** Returns the PostScript name of the given font file, or null. */
     @Nullable
-    private String getPostScriptName(File file) {
+    private String getCanonicalFileName(File file) {
         try {
-            return mParser.getPostScriptName(file);
+            return mParser.getCanonicalFileName(file);
         } catch (IOException e) {
             Slog.e(TAG, "Failed to read font file", e);
             return null;
@@ -514,7 +533,7 @@
         List<FontConfig.Font> fontList = fontFamily.getFontList();
         for (int i = 0; i < fontList.size(); i++) {
             FontConfig.Font font = fontList.get(i);
-            FontFileInfo info = mFontFileInfoMap.get(font.getFile().getName());
+            FontFileInfo info = lookupFontFileInfo(font.getFile());
             if (info == null) {
                 return null;
             }
@@ -537,8 +556,9 @@
 
     Map<String, File> getFontFileMap() {
         Map<String, File> map = new ArrayMap<>();
-        for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
-            map.put(entry.getKey(), entry.getValue().getFile());
+        for (int i = 0; i < mFontFileInfoMap.size(); ++i) {
+            File file = mFontFileInfoMap.valueAt(i).getFile();
+            map.put(file.getName(), file);
         }
         return map;
     }
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
index c23e2e6..947ee24 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
@@ -97,11 +97,16 @@
 
     @Override
     public boolean start() {
-      // Wake-up on <Set Stream Path> was not mandatory before CEC 2.0.
-      // The message is re-sent at the end of the action for devices that don't support 2.0.
-      sendSetStreamPath();
-        int targetPowerStatus = localDevice().mService.getHdmiCecNetwork()
-                .getCecDeviceInfo(getTargetAddress()).getDevicePowerStatus();
+        // Wake-up on <Set Stream Path> was not mandatory before CEC 2.0.
+        // The message is re-sent at the end of the action for devices that don't support 2.0.
+        sendSetStreamPath();
+        int targetPowerStatus = HdmiControlManager.POWER_STATUS_UNKNOWN;
+        HdmiDeviceInfo targetDevice = localDevice().mService.getHdmiCecNetwork().getCecDeviceInfo(
+                getTargetAddress());
+        if (targetDevice != null) {
+            targetPowerStatus = targetDevice.getDevicePowerStatus();
+        }
+
         if (!mIsCec20 || targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
             queryDevicePowerStatus();
         } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index 624af30..6fbb26c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -35,33 +35,19 @@
 import android.os.UserHandle;
 import android.provider.Settings.Global;
 import android.util.ArrayMap;
-import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ConcurrentUtils;
-import com.android.server.hdmi.cec.config.CecSettings;
-import com.android.server.hdmi.cec.config.Setting;
-import com.android.server.hdmi.cec.config.Value;
-import com.android.server.hdmi.cec.config.XmlParser;
 
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map.Entry;
-import java.util.Set;
 import java.util.concurrent.Executor;
 
-import javax.xml.datatype.DatatypeConfigurationException;
-
 /**
  * The {@link HdmiCecConfig} class is used for getting information about
  * available HDMI CEC settings.
@@ -74,6 +60,10 @@
     private static final String SHARED_PREFS_DIR = "shared_prefs";
     private static final String SHARED_PREFS_NAME = "cec_config.xml";
 
+    private static final int STORAGE_SYSPROPS = 0;
+    private static final int STORAGE_GLOBAL_SETTINGS = 1;
+    private static final int STORAGE_SHARED_PREFS = 2;
+
     @IntDef({
         STORAGE_SYSPROPS,
         STORAGE_GLOBAL_SETTINGS,
@@ -81,10 +71,6 @@
     })
     private @interface Storage {}
 
-    private static final int STORAGE_SYSPROPS = 0;
-    private static final int STORAGE_GLOBAL_SETTINGS = 1;
-    private static final int STORAGE_SHARED_PREFS = 2;
-
     private static final String VALUE_TYPE_STRING = "string";
     private static final String VALUE_TYPE_INT = "int";
 
@@ -96,8 +82,6 @@
 
     @NonNull private final Context mContext;
     @NonNull private final StorageAdapter mStorageAdapter;
-    @Nullable private final CecSettings mSystemConfig;
-    @Nullable private final CecSettings mVendorOverride;
 
     private final Object mLock = new Object();
 
@@ -107,6 +91,18 @@
 
     private SettingsObserver mSettingsObserver;
 
+    private LinkedHashMap<String, Setting> mSettings = new LinkedHashMap<>();
+
+    /**
+     * Exception thrown when the CEC Configuration setup verification fails.
+     * This usually means a settings lacks default value or storage/storage key.
+     */
+    public static class VerificationException extends RuntimeException {
+        public VerificationException(String message) {
+            super(message);
+        }
+    }
+
     /**
      * Listener used to get notifications when value of a setting changes.
      */
@@ -202,91 +198,297 @@
         }
     }
 
+    private class Value {
+        private final String mStringValue;
+        private final Integer mIntValue;
+
+        Value(@NonNull String value) {
+            mStringValue = value;
+            mIntValue = null;
+        }
+
+        Value(@NonNull Integer value) {
+            mStringValue = null;
+            mIntValue = value;
+        }
+
+        String getStringValue() {
+            return mStringValue;
+        }
+
+        Integer getIntValue() {
+            return mIntValue;
+        }
+    }
+
+    private class Setting {
+        @NonNull private final Context mContext;
+        @NonNull private final @CecSettingName String mName;
+        private final boolean mUserConfigurable;
+
+        private Value mDefaultValue = null;
+        private List<Value> mAllowedValues = new ArrayList<>();
+
+        Setting(@NonNull Context context,
+                @NonNull @CecSettingName String name,
+                int userConfResId) {
+            mContext = context;
+            mName = name;
+            mUserConfigurable = mContext.getResources().getBoolean(userConfResId);
+        }
+
+        public @CecSettingName String getName() {
+            return mName;
+        }
+
+        public @ValueType String getValueType() {
+            return getDefaultValue().getStringValue() != null
+                    ? VALUE_TYPE_STRING
+                    : VALUE_TYPE_INT;
+        }
+
+        public Value getDefaultValue() {
+            if (mDefaultValue == null) {
+                throw new VerificationException("Invalid CEC setup for '"
+                    + this.getName() + "' setting. "
+                    + "Setting has no default value.");
+            }
+            return mDefaultValue;
+        }
+
+        public boolean getUserConfigurable() {
+            return mUserConfigurable;
+        }
+
+        private void registerValue(@NonNull Value value,
+                                   int allowedResId, int defaultResId) {
+            if (mContext.getResources().getBoolean(allowedResId)) {
+                mAllowedValues.add(value);
+                if (mContext.getResources().getBoolean(defaultResId)) {
+                    if (mDefaultValue != null) {
+                        throw new VerificationException("Invalid CEC setup for '"
+                            + this.getName() + "' setting. "
+                            + "Setting already has a default value.");
+                    }
+                    mDefaultValue = value;
+                }
+            }
+        }
+
+        public void registerValue(@NonNull String value, int allowedResId,
+                                  int defaultResId) {
+            registerValue(new Value(value), allowedResId, defaultResId);
+        }
+
+        public void registerValue(int value, int allowedResId,
+                                  int defaultResId) {
+            registerValue(new Value(value), allowedResId, defaultResId);
+        }
+
+
+        public List<Value> getAllowedValues() {
+            return mAllowedValues;
+        }
+    }
+
     @VisibleForTesting
     HdmiCecConfig(@NonNull Context context,
-                  @NonNull StorageAdapter storageAdapter,
-                  @Nullable CecSettings systemConfig,
-                  @Nullable CecSettings vendorOverride) {
+                  @NonNull StorageAdapter storageAdapter) {
         mContext = context;
         mStorageAdapter = storageAdapter;
-        mSystemConfig = systemConfig;
-        mVendorOverride = vendorOverride;
-        if (mSystemConfig == null) {
-            Slog.i(TAG, "CEC system configuration XML missing.");
-        }
-        if (mVendorOverride == null) {
-            Slog.i(TAG, "CEC OEM configuration override XML missing.");
-        }
+
+        Setting hdmiCecEnabled = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+                R.bool.config_cecHdmiCecEnabled_userConfigurable);
+        hdmiCecEnabled.registerValue(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED,
+                R.bool.config_cecHdmiCecControlEnabled_allowed,
+                R.bool.config_cecHdmiCecControlEnabled_default);
+        hdmiCecEnabled.registerValue(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED,
+                R.bool.config_cecHdmiCecControlDisabled_allowed,
+                R.bool.config_cecHdmiCecControlDisabled_default);
+
+        Setting hdmiCecVersion = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+                R.bool.config_cecHdmiCecVersion_userConfigurable);
+        hdmiCecVersion.registerValue(HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
+                R.bool.config_cecHdmiCecVersion14b_allowed,
+                R.bool.config_cecHdmiCecVersion14b_default);
+        hdmiCecVersion.registerValue(HdmiControlManager.HDMI_CEC_VERSION_2_0,
+                R.bool.config_cecHdmiCecVersion20_allowed,
+                R.bool.config_cecHdmiCecVersion20_default);
+
+        Setting powerControlMode = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+                R.bool.config_cecSendStandbyOnSleep_userConfigurable);
+        powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV,
+                R.bool.config_cecPowerControlModeTv_allowed,
+                R.bool.config_cecPowerControlModeTv_default);
+        powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST,
+                R.bool.config_cecPowerControlModeBroadcast_allowed,
+                R.bool.config_cecPowerControlModeBroadcast_default);
+        powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_NONE,
+                R.bool.config_cecPowerControlModeNone_allowed,
+                R.bool.config_cecPowerControlModeNone_default);
+
+        Setting powerStateChangeOnActiveSourceLost = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                R.bool.config_cecPowerStateChangeOnActiveSourceLost_userConfigurable);
+        powerStateChangeOnActiveSourceLost.registerValue(
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE,
+                R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_allowed,
+                R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_default);
+        powerStateChangeOnActiveSourceLost.registerValue(
+                HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW,
+                R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed,
+                R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default);
+
+        Setting systemAudioModeMuting = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+                R.bool.config_cecSystemAudioModeMuting_userConfigurable);
+        systemAudioModeMuting.registerValue(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_ENABLED,
+                R.bool.config_cecSystemAudioModeMutingEnabled_allowed,
+                R.bool.config_cecSystemAudioModeMutingEnabled_default);
+        systemAudioModeMuting.registerValue(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED,
+                R.bool.config_cecSystemAudioModeMutingDisabled_allowed,
+                R.bool.config_cecSystemAudioModeMutingDisabled_default);
+
+        Setting volumeControlMode = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                R.bool.config_cecVolumeControlMode_userConfigurable);
+        volumeControlMode.registerValue(HdmiControlManager.VOLUME_CONTROL_ENABLED,
+                R.bool.config_cecVolumeControlModeEnabled_allowed,
+                R.bool.config_cecVolumeControlModeEnabled_default);
+        volumeControlMode.registerValue(HdmiControlManager.VOLUME_CONTROL_DISABLED,
+                R.bool.config_cecVolumeControlModeDisabled_allowed,
+                R.bool.config_cecVolumeControlModeDisabled_default);
+
+        Setting tvWakeOnOneTouchPlay = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+                R.bool.config_cecTvWakeOnOneTouchPlay_userConfigurable);
+        tvWakeOnOneTouchPlay.registerValue(HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED,
+                R.bool.config_cecTvWakeOnOneTouchPlayEnabled_allowed,
+                R.bool.config_cecTvWakeOnOneTouchPlayEnabled_default);
+        tvWakeOnOneTouchPlay.registerValue(HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_DISABLED,
+                R.bool.config_cecTvWakeOnOneTouchPlayDisabled_allowed,
+                R.bool.config_cecTvWakeOnOneTouchPlayDisabled_default);
+
+        Setting tvSendStandbyOnSleep = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+                R.bool.config_cecTvSendStandbyOnSleep_userConfigurable);
+        tvSendStandbyOnSleep.registerValue(HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_ENABLED,
+                R.bool.config_cecTvSendStandbyOnSleepEnabled_allowed,
+                R.bool.config_cecTvSendStandbyOnSleepEnabled_default);
+        tvSendStandbyOnSleep.registerValue(HdmiControlManager.TV_SEND_STANDBY_ON_SLEEP_DISABLED,
+                R.bool.config_cecTvSendStandbyOnSleepDisabled_allowed,
+                R.bool.config_cecTvSendStandbyOnSleepDisabled_default);
+
+        Setting rcProfileTv = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
+                R.bool.config_cecRcProfileTv_userConfigurable);
+        rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_NONE,
+                R.bool.config_cecRcProfileTvNone_allowed,
+                R.bool.config_cecRcProfileTvNone_default);
+        rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_ONE,
+                R.bool.config_cecRcProfileTvOne_allowed,
+                R.bool.config_cecRcProfileTvOne_default);
+        rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_TWO,
+                R.bool.config_cecRcProfileTvTwo_allowed,
+                R.bool.config_cecRcProfileTvTwo_default);
+        rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_THREE,
+                R.bool.config_cecRcProfileTvThree_allowed,
+                R.bool.config_cecRcProfileTvThree_default);
+        rcProfileTv.registerValue(HdmiControlManager.RC_PROFILE_TV_FOUR,
+                R.bool.config_cecRcProfileTvFour_allowed,
+                R.bool.config_cecRcProfileTvFour_default);
+
+        Setting rcProfileSourceRootMenu = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
+                R.bool.config_cecRcProfileSourceRootMenu_userConfigurable);
+        rcProfileSourceRootMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_ROOT_MENU_HANDLED,
+                R.bool.config_cecRcProfileSourceRootMenuHandled_allowed,
+                R.bool.config_cecRcProfileSourceRootMenuHandled_default);
+        rcProfileSourceRootMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_ROOT_MENU_NOT_HANDLED,
+                R.bool.config_cecRcProfileSourceRootMenuNotHandled_allowed,
+                R.bool.config_cecRcProfileSourceRootMenuNotHandled_default);
+
+        Setting rcProfileSourceSetupMenu = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
+                R.bool.config_cecRcProfileSourceSetupMenu_userConfigurable);
+        rcProfileSourceSetupMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_SETUP_MENU_HANDLED,
+                R.bool.config_cecRcProfileSourceSetupMenuHandled_allowed,
+                R.bool.config_cecRcProfileSourceSetupMenuHandled_default);
+        rcProfileSourceSetupMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_SETUP_MENU_NOT_HANDLED,
+                R.bool.config_cecRcProfileSourceSetupMenuNotHandled_allowed,
+                R.bool.config_cecRcProfileSourceSetupMenuNotHandled_default);
+
+        Setting rcProfileSourceContentsMenu = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
+                R.bool.config_cecRcProfileSourceContentsMenu_userConfigurable);
+        rcProfileSourceContentsMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_CONTENTS_MENU_HANDLED,
+                R.bool.config_cecRcProfileSourceContentsMenuHandled_allowed,
+                R.bool.config_cecRcProfileSourceContentsMenuHandled_default);
+        rcProfileSourceContentsMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_CONTENTS_MENU_NOT_HANDLED,
+                R.bool.config_cecRcProfileSourceContentsMenuNotHandled_allowed,
+                R.bool.config_cecRcProfileSourceContentsMenuNotHandled_default);
+
+        Setting rcProfileSourceTopMenu = registerSetting(
+                HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+                R.bool.config_cecRcProfileSourceTopMenu_userConfigurable);
+        rcProfileSourceTopMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_TOP_MENU_HANDLED,
+                R.bool.config_cecRcProfileSourceTopMenuHandled_allowed,
+                R.bool.config_cecRcProfileSourceTopMenuHandled_default);
+        rcProfileSourceTopMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_TOP_MENU_NOT_HANDLED,
+                R.bool.config_cecRcProfileSourceTopMenuNotHandled_allowed,
+                R.bool.config_cecRcProfileSourceTopMenuNotHandled_default);
+
+        Setting rcProfileSourceMediaContextSensitiveMenu = registerSetting(
+                HdmiControlManager
+                    .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU,
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable);
+        rcProfileSourceMediaContextSensitiveMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_HANDLED,
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed,
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default);
+        rcProfileSourceMediaContextSensitiveMenu.registerValue(
+                HdmiControlManager.RC_PROFILE_SOURCE_MEDIA_CONTEXT_SENSITIVE_MENU_NOT_HANDLED,
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed,
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default);
+
+        verifySettings();
     }
 
     HdmiCecConfig(@NonNull Context context) {
-        this(context, new StorageAdapter(context),
-             readSettingsFromFile(Environment.buildPath(Environment.getRootDirectory(),
-                                                        ETC_DIR, CONFIG_FILE)),
-             readSettingsFromFile(Environment.buildPath(Environment.getVendorDirectory(),
-                                                        ETC_DIR, CONFIG_FILE)));
+        this(context, new StorageAdapter(context));
     }
 
-    @Nullable
-    private static CecSettings readSettingsFromFile(@NonNull File file) {
-        if (!file.exists()) {
-            return null;
-        }
-        if (!file.isFile()) {
-            Slog.e(TAG, "CEC configuration is not a file: " + file + ", skipping.");
-            return null;
-        }
-        try (InputStream in = new BufferedInputStream(new FileInputStream(file))) {
-            return XmlParser.read(in);
-        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
-            Slog.e(TAG, "Encountered an error while reading/parsing CEC config file: " + file, e);
-        }
-        return null;
+    private Setting registerSetting(@NonNull @CecSettingName String name,
+                               int userConfResId) {
+        Setting setting = new Setting(mContext, name, userConfResId);
+        mSettings.put(name, setting);
+        return setting;
     }
 
-    @NonNull
-    @VisibleForTesting
-    static HdmiCecConfig createFromStrings(@NonNull Context context,
-                                           @NonNull StorageAdapter storageAdapter,
-                                           @Nullable String productConfigXml,
-                                           @Nullable String vendorOverrideXml) {
-        CecSettings productConfig = null;
-        CecSettings vendorOverride = null;
-        try {
-            if (productConfigXml != null) {
-                productConfig = XmlParser.read(
-                        new ByteArrayInputStream(productConfigXml.getBytes()));
-            }
-            if (vendorOverrideXml != null) {
-                vendorOverride = XmlParser.read(
-                        new ByteArrayInputStream(vendorOverrideXml.getBytes()));
-            }
-        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
-            Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e);
+    private void verifySettings() {
+        for (Setting setting: mSettings.values()) {
+            // This will throw an exception when a setting
+            // doesn't have a default value assigned.
+            setting.getDefaultValue();
+            getStorage(setting);
+            getStorageKey(setting);
         }
-        return new HdmiCecConfig(context, storageAdapter, productConfig, vendorOverride);
     }
 
     @Nullable
     private Setting getSetting(@NonNull String name) {
-        if (mSystemConfig == null) {
-            return null;
-        }
-        if (mVendorOverride != null) {
-            // First read from the vendor override.
-            for (Setting setting : mVendorOverride.getSetting()) {
-                if (setting.getName().equals(name)) {
-                    return setting;
-                }
-            }
-        }
-        // If not found, try the system config.
-        for (Setting setting : mSystemConfig.getSetting()) {
-            if (setting.getName().equals(name)) {
-                return setting;
-            }
-        }
-        return null;
+        return mSettings.containsKey(name) ? mSettings.get(name) : null;
     }
 
     @Storage
@@ -322,7 +524,7 @@
                     .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU:
                 return STORAGE_SHARED_PREFS;
             default:
-                throw new RuntimeException("Invalid CEC setting '" + setting.getName()
+                throw new VerificationException("Invalid CEC setting '" + setting.getName()
                         + "' storage.");
         }
     }
@@ -359,7 +561,7 @@
                     .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU:
                 return setting.getName();
             default:
-                throw new RuntimeException("Invalid CEC setting '" + setting.getName()
+                throw new VerificationException("Invalid CEC setting '" + setting.getName()
                     + "' storage key.");
         }
     }
@@ -396,10 +598,6 @@
         }
     }
 
-    private int getIntValue(@NonNull Value value) {
-        return Integer.decode(value.getIntValue());
-    }
-
     private void notifyGlobalSettingChanged(String setting) {
         switch (setting) {
             case Global.HDMI_CONTROL_ENABLED:
@@ -533,41 +731,20 @@
      * Returns a list of all settings based on the XML metadata.
      */
     public @CecSettingName List<String> getAllSettings() {
-        if (mSystemConfig == null) {
-            return new ArrayList<String>();
-        }
-        List<String> allSettings = new ArrayList<String>();
-        for (Setting setting : mSystemConfig.getSetting()) {
-            allSettings.add(setting.getName());
-        }
-        return allSettings;
+        return new ArrayList<>(mSettings.keySet());
     }
 
     /**
      * Returns a list of user-modifiable settings based on the XML metadata.
      */
     public @CecSettingName List<String> getUserSettings() {
-        if (mSystemConfig == null) {
-            return new ArrayList<String>();
-        }
-        Set<String> userSettings = new HashSet<String>();
-        // First read from the system config.
-        for (Setting setting : mSystemConfig.getSetting()) {
+        List<String> settings = new ArrayList<>();
+        for (Setting setting: mSettings.values()) {
             if (setting.getUserConfigurable()) {
-                userSettings.add(setting.getName());
+                settings.add(setting.getName());
             }
         }
-        if (mVendorOverride != null) {
-            // Next either add or remove based on the vendor override.
-            for (Setting setting : mVendorOverride.getSetting()) {
-                if (setting.getUserConfigurable()) {
-                    userSettings.add(setting.getName());
-                } else {
-                    userSettings.remove(setting.getName());
-                }
-            }
-        }
-        return new ArrayList(userSettings);
+        return settings;
     }
 
     /**
@@ -607,7 +784,7 @@
                     + "' is not a string-type setting.");
         }
         List<String> allowedValues = new ArrayList<String>();
-        for (Value allowedValue : setting.getAllowedValues().getValue()) {
+        for (Value allowedValue : setting.getAllowedValues()) {
             allowedValues.add(allowedValue.getStringValue());
         }
         return allowedValues;
@@ -626,8 +803,8 @@
                     + "' is not a string-type setting.");
         }
         List<Integer> allowedValues = new ArrayList<Integer>();
-        for (Value allowedValue : setting.getAllowedValues().getValue()) {
-            allowedValues.add(getIntValue(allowedValue));
+        for (Value allowedValue : setting.getAllowedValues()) {
+            allowedValues.add(allowedValue.getIntValue());
         }
         return allowedValues;
     }
@@ -659,7 +836,7 @@
             throw new IllegalArgumentException("Setting '" + name
                     + "' is not a string-type setting.");
         }
-        return getIntValue(getSetting(name).getDefaultValue());
+        return getSetting(name).getDefaultValue().getIntValue();
     }
 
     /**
@@ -691,7 +868,7 @@
                     + "' is not a int-type setting.");
         }
         HdmiLogger.debug("Getting CEC setting value '" + name + "'.");
-        String defaultValue = Integer.toString(getIntValue(setting.getDefaultValue()));
+        String defaultValue = Integer.toString(setting.getDefaultValue().getIntValue());
         String value = retrieveValue(setting, defaultValue);
         return Integer.parseInt(value);
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index d8914b3..bdc4e66 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -500,7 +500,8 @@
 
         HdmiDeviceInfo cecDeviceInfo = mService.getHdmiCecNetwork().getCecDeviceInfo(address);
         // If no non-default display name is available for the device, request the devices OSD name.
-        if (cecDeviceInfo.getDisplayName().equals(HdmiUtils.getDefaultDeviceName(address))) {
+        if (cecDeviceInfo != null && cecDeviceInfo.getDisplayName().equals(
+                HdmiUtils.getDefaultDeviceName(address))) {
             mService.sendCecCommand(
                     HdmiCecMessageBuilder.buildGiveOsdNameCommand(mAddress, address));
         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 7235a92..90d6433 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -30,6 +30,7 @@
 import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_DIGITAL;
 import static android.hardware.hdmi.HdmiControlManager.TIMER_RECORDING_TYPE_EXTERNAL;
 
+import android.annotation.Nullable;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPortInfo;
@@ -1147,6 +1148,7 @@
                 && getAvrDeviceInfo() != null;
     }
 
+    @Nullable
     @ServiceThreadOnly
     HdmiDeviceInfo getAvrDeviceInfo() {
         assertRunOnServiceThread();
@@ -1157,6 +1159,7 @@
         return getSafeAvrDeviceInfo() != null;
     }
 
+    @Nullable
     HdmiDeviceInfo getSafeAvrDeviceInfo() {
         return mService.getHdmiCecNetwork().getSafeCecDeviceInfo(Constants.ADDR_AUDIO_SYSTEM);
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
index b748ae0..7ceaa95 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -185,6 +185,12 @@
         mLocalDevices.clear();
     }
 
+    /**
+     * Get the device info of a local device or a device in the CEC network by a device id.
+     * @param id id of the device to get
+     * @return the device with the given id, or {@code null}
+     */
+    @Nullable
     public HdmiDeviceInfo getDeviceInfo(int id) {
         return mDeviceInfos.get(id);
     }
@@ -717,6 +723,7 @@
      * @return {@link HdmiDeviceInfo} matched with the given {@code logicalAddress}.
      * Returns null if no logical address matched
      */
+    @Nullable
     HdmiDeviceInfo getSafeCecDeviceInfo(int logicalAddress) {
         for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
             if (info.isCecDevice() && info.getLogicalAddress() == logicalAddress) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 115cafed..e6e2f96 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -988,6 +988,7 @@
         return mCecController.getVendorId();
     }
 
+    @Nullable
     @ServiceThreadOnly
     HdmiDeviceInfo getDeviceInfo(int logicalAddress) {
         assertRunOnServiceThread();
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 73ecbe0..9d2db94 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -16,6 +16,7 @@
 package com.android.server.hdmi;
 
 import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.util.Slog;
@@ -62,8 +63,7 @@
             Slog.e(TAG, "Wrong arguments");
             return null;
         }
-        return new OneTouchPlayAction(source, targetAddress,
-                callback);
+        return new OneTouchPlayAction(source, targetAddress, callback);
     }
 
     private OneTouchPlayAction(HdmiCecLocalDevice localDevice, int targetAddress,
@@ -71,8 +71,8 @@
         this(localDevice, targetAddress, callback,
                 localDevice.getDeviceInfo().getCecVersion()
                         >= HdmiControlManager.HDMI_CEC_VERSION_2_0
-                        && localDevice.mService.getHdmiCecNetwork().getCecDeviceInfo(
-                        targetAddress).getCecVersion() >= HdmiControlManager.HDMI_CEC_VERSION_2_0);
+                        && getTargetCecVersion(localDevice, targetAddress)
+                        >= HdmiControlManager.HDMI_CEC_VERSION_2_0);
     }
 
     @VisibleForTesting
@@ -88,9 +88,9 @@
         // Because only source device can create this action, it's safe to cast.
         mSource = source();
         sendCommand(HdmiCecMessageBuilder.buildTextViewOn(getSourceAddress(), mTargetAddress));
-        boolean targetOnBefore = localDevice().mService.getHdmiCecNetwork()
-                .getCecDeviceInfo(mTargetAddress).getDevicePowerStatus()
-                == HdmiControlManager.POWER_STATUS_ON;
+
+        boolean targetOnBefore = getTargetDevicePowerStatus(mSource, mTargetAddress,
+                HdmiControlManager.POWER_STATUS_UNKNOWN) == HdmiControlManager.POWER_STATUS_ON;
         broadcastActiveSource();
         // If the device is not an audio system itself, request the connected audio system to
         // turn on.
@@ -98,8 +98,8 @@
             sendCommand(HdmiCecMessageBuilder.buildSystemAudioModeRequest(getSourceAddress(),
                     Constants.ADDR_AUDIO_SYSTEM, getSourcePath(), true));
         }
-        int targetPowerStatus = localDevice().mService.getHdmiCecNetwork()
-                .getCecDeviceInfo(mTargetAddress).getDevicePowerStatus();
+        int targetPowerStatus = getTargetDevicePowerStatus(mSource, mTargetAddress,
+                HdmiControlManager.POWER_STATUS_UNKNOWN);
         if (!mIsCec20 || targetPowerStatus == HdmiControlManager.POWER_STATUS_UNKNOWN) {
             queryDevicePowerStatus();
         } else if (targetPowerStatus == HdmiControlManager.POWER_STATUS_ON) {
@@ -179,4 +179,23 @@
         return sendStandbyOnSleep.equals(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
     }
 
+    private static int getTargetCecVersion(HdmiCecLocalDevice localDevice,
+            int targetLogicalAddress) {
+        HdmiDeviceInfo targetDevice = localDevice.mService.getHdmiCecNetwork().getCecDeviceInfo(
+                targetLogicalAddress);
+        if (targetDevice != null) {
+            return targetDevice.getCecVersion();
+        }
+        return HdmiControlManager.HDMI_CEC_VERSION_1_4_B;
+    }
+
+    private static int getTargetDevicePowerStatus(HdmiCecLocalDevice localDevice,
+            int targetLogicalAddress, int defaultPowerStatus) {
+        HdmiDeviceInfo targetDevice = localDevice.mService.getHdmiCecNetwork().getCecDeviceInfo(
+                targetLogicalAddress);
+        if (targetDevice != null) {
+            return targetDevice.getDevicePowerStatus();
+        }
+        return defaultPowerStatus;
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/cec_config.xml b/services/core/java/com/android/server/hdmi/cec_config.xml
deleted file mode 100644
index 191e725..0000000
--- a/services/core/java/com/android/server/hdmi/cec_config.xml
+++ /dev/null
@@ -1,133 +0,0 @@
-<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
-<cec-settings>
-  <setting name="hdmi_cec_enabled"
-           value-type="int"
-           user-configurable="true">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="1" />
-  </setting>
-  <setting name="hdmi_cec_version"
-           value-type="int"
-           user-configurable="true">
-    <allowed-values>
-      <value int-value="0x05" />
-      <value int-value="0x06" />
-    </allowed-values>
-    <default-value int-value="0x05" />
-  </setting>
-  <setting name="send_standby_on_sleep"
-           value-type="string"
-           user-configurable="true">
-    <allowed-values>
-      <value string-value="to_tv" />
-      <value string-value="broadcast" />
-      <value string-value="none" />
-    </allowed-values>
-    <default-value string-value="to_tv" />
-  </setting>
-  <setting name="power_state_change_on_active_source_lost"
-           value-type="string"
-           user-configurable="true">
-    <allowed-values>
-      <value string-value="none" />
-      <value string-value="standby_now" />
-    </allowed-values>
-    <default-value string-value="none" />
-  </setting>
-  <setting name="system_audio_mode_muting"
-           value-type="int"
-           user-configurable="true">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="1" />
-  </setting>
-  <setting name="volume_control_enabled"
-      value-type="int"
-      user-configurable="true">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="1" />
-  </setting>
-  <setting name="tv_wake_on_one_touch_play"
-      value-type="int"
-      user-configurable="true">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="1" />
-  </setting>
-  <setting name="tv_send_standby_on_sleep"
-      value-type="int"
-      user-configurable="true">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="1" />
-  </setting>
-  <setting name="rc_profile_tv"
-      value-type="int"
-      user-configurable="false">
-    <allowed-values>
-      <value int-value="0x0" />
-      <value int-value="0x2" />
-      <value int-value="0x6" />
-      <value int-value="0xA" />
-      <value int-value="0xE" />
-    </allowed-values>
-    <default-value int-value="0x0" />
-  </setting>
-  <setting name="rc_profile_source_handles_root_menu"
-      value-type="int"
-      user-configurable="false">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="1" />
-  </setting>
-  <setting name="rc_profile_source_handles_setup_menu"
-      value-type="int"
-      user-configurable="false">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="1" />
-  </setting>
-  <setting name="rc_profile_source_handles_contents_menu"
-      value-type="int"
-      user-configurable="false">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="0" />
-  </setting>
-  <setting name="rc_profile_source_handles_top_menu"
-      value-type="int"
-      user-configurable="false">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="0" />
-  </setting>
-  <setting name="rc_profile_source_handles_media_context_sensitive_menu"
-      value-type="int"
-      user-configurable="false">
-    <allowed-values>
-      <value int-value="0" />
-      <value int-value="1" />
-    </allowed-values>
-    <default-value int-value="0" />
-  </setting>
-</cec-settings>
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 7dc9a0b..cbe6e69 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -304,7 +304,7 @@
             int displayId, InputApplicationHandle application);
     private static native void nativeSetFocusedDisplay(long ptr, int displayId);
     private static native boolean nativeTransferTouchFocus(long ptr,
-            IBinder fromChannelToken, IBinder toChannelToken);
+            IBinder fromChannelToken, IBinder toChannelToken, boolean isDragDrop);
     private static native void nativeSetPointerSpeed(long ptr, int speed);
     private static native void nativeSetShowTouches(long ptr, boolean enabled);
     private static native void nativeSetInteractive(long ptr, boolean interactive);
@@ -1727,12 +1727,14 @@
      * @param fromChannel The channel of a window that currently has touch focus.
      * @param toChannel The channel of the window that should receive touch focus in
      * place of the first.
+     * @param isDragDrop True if transfer touch focus for drag and drop.
      * @return True if the transfer was successful.  False if the window with the
      * specified channel did not actually have touch focus at the time of the request.
      */
     public boolean transferTouchFocus(@NonNull InputChannel fromChannel,
-            @NonNull InputChannel toChannel) {
-        return nativeTransferTouchFocus(mPtr, fromChannel.getToken(), toChannel.getToken());
+            @NonNull InputChannel toChannel, boolean isDragDrop) {
+        return nativeTransferTouchFocus(mPtr, fromChannel.getToken(), toChannel.getToken(),
+                isDragDrop);
     }
 
     /**
@@ -1752,7 +1754,8 @@
             @NonNull IBinder toChannelToken) {
         Objects.nonNull(fromChannelToken);
         Objects.nonNull(toChannelToken);
-        return nativeTransferTouchFocus(mPtr, fromChannelToken, toChannelToken);
+        return nativeTransferTouchFocus(mPtr, fromChannelToken, toChannelToken,
+                false /* isDragDrop */);
     }
 
     @Override // Binder call
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a89cb55..672ed3d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -5838,7 +5838,22 @@
     @BinderThread
     @ShellCommandResult
     private int handleShellCommandTraceInputMethod(@NonNull ShellCommand shellCommand) {
-        int result = ImeTracing.getInstance().onShellCommand(shellCommand);
+        final String cmd = shellCommand.getNextArgRequired();
+        final PrintWriter pw = shellCommand.getOutPrintWriter();
+        switch (cmd) {
+            case "start":
+                ImeTracing.getInstance().getInstance().startTrace(pw);
+                break;
+            case "stop":
+                ImeTracing.getInstance().stopTrace(pw);
+                break;
+            default:
+                pw.println("Unknown command: " + cmd);
+                pw.println("Input method trace options:");
+                pw.println("  start: Start tracing");
+                pw.println("  stop: Stop tracing");
+                return ShellCommandResult.FAILURE;
+        }
         boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled();
         ArrayMap<IBinder, ClientState> clients;
         synchronized (mMethodMap) {
@@ -5854,7 +5869,7 @@
                 }
             }
         }
-        return result;
+        return ShellCommandResult.SUCCESS;
     }
 
     /**
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index 5a90fa7..7f47805 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -370,6 +370,7 @@
                 LocationStatsEnums.USAGE_ENDED,
                 LocationStatsEnums.API_REQUEST_GEOFENCE,
                 registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
                 null,
                 /* LocationRequest= */ null,
                 /* hasListener= */ false,
@@ -383,6 +384,7 @@
                 LocationStatsEnums.USAGE_ENDED,
                 LocationStatsEnums.API_REQUEST_GEOFENCE,
                 registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
                 null,
                 /* LocationRequest= */ null,
                 /* hasListener= */ false,
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index b3119d7..8460d67 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -189,6 +189,7 @@
                 LocationStatsEnums.USAGE_STARTED,
                 LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER,
                 registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
                 null,
                 null,
                 true,
@@ -202,6 +203,7 @@
                 LocationStatsEnums.USAGE_ENDED,
                 LocationStatsEnums.API_ADD_GNSS_MEASUREMENTS_LISTENER,
                 registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
                 null,
                 null,
                 true,
diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
index 1eb1618..936283d 100644
--- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
@@ -83,6 +83,7 @@
                 LocationStatsEnums.USAGE_STARTED,
                 LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
                 registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
                 null,
                 null,
                 true,
@@ -96,6 +97,7 @@
                 LocationStatsEnums.USAGE_ENDED,
                 LocationStatsEnums.API_REGISTER_GNSS_STATUS_CALLBACK,
                 registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
                 null,
                 null,
                 true,
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index f275663..1eef0de 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -27,6 +27,7 @@
 import android.location.GnssNavigationMessage;
 import android.location.GnssStatus;
 import android.location.Location;
+import android.os.Binder;
 import android.os.SystemClock;
 import android.util.Log;
 
@@ -921,6 +922,7 @@
 
     @NativeEntryPoint
     void reportGnssServiceDied() {
+        // Not necessary to clear (and restore) binder identity since it runs on another thread.
         Log.e(TAG, "gnss hal died - restarting shortly...");
 
         // move to another thread just in case there is some awkward gnss thread dependency with
@@ -940,96 +942,111 @@
 
     @NativeEntryPoint
     void reportLocation(boolean hasLatLong, Location location) {
-        if (hasLatLong && !mHasFirstFix) {
-            mHasFirstFix = true;
+        Binder.withCleanCallingIdentity(() -> {
+            if (hasLatLong && !mHasFirstFix) {
+                mHasFirstFix = true;
 
-            // notify status listeners
-            int ttff = (int) (SystemClock.elapsedRealtime() - mStartRealtimeMs);
-            for (int i = 0; i < mStatusCallbacks.length; i++) {
-                mStatusCallbacks[i].onReportFirstFix(ttff);
+                // notify status listeners
+                int ttff = (int) (SystemClock.elapsedRealtime() - mStartRealtimeMs);
+                for (int i = 0; i < mStatusCallbacks.length; i++) {
+                    mStatusCallbacks[i].onReportFirstFix(ttff);
+                }
             }
-        }
 
-        if (location.hasSpeed()) {
-            boolean exceeded = location.getSpeed() > ITAR_SPEED_LIMIT_METERS_PER_SECOND;
-            if (!mItarSpeedLimitExceeded && exceeded) {
-                Log.w(TAG, "speed nearing ITAR threshold - blocking further GNSS output");
-            } else if (mItarSpeedLimitExceeded && !exceeded) {
-                Log.w(TAG, "speed leaving ITAR threshold - allowing further GNSS output");
+            if (location.hasSpeed()) {
+                boolean exceeded = location.getSpeed() > ITAR_SPEED_LIMIT_METERS_PER_SECOND;
+                if (!mItarSpeedLimitExceeded && exceeded) {
+                    Log.w(TAG, "speed nearing ITAR threshold - blocking further GNSS output");
+                } else if (mItarSpeedLimitExceeded && !exceeded) {
+                    Log.w(TAG, "speed leaving ITAR threshold - allowing further GNSS output");
+                }
+                mItarSpeedLimitExceeded = exceeded;
             }
-            mItarSpeedLimitExceeded = exceeded;
-        }
 
-        if (mItarSpeedLimitExceeded) {
-            return;
-        }
+            if (mItarSpeedLimitExceeded) {
+                return;
+            }
 
-        for (int i = 0; i < mLocationCallbacks.length; i++) {
-            mLocationCallbacks[i].onReportLocation(hasLatLong, location);
-        }
+            for (int i = 0; i < mLocationCallbacks.length; i++) {
+                mLocationCallbacks[i].onReportLocation(hasLatLong, location);
+            }
+        });
     }
 
     @NativeEntryPoint
     void reportStatus(@StatusCallbacks.GnssStatusValue int gnssStatus) {
-        for (int i = 0; i < mStatusCallbacks.length; i++) {
-            mStatusCallbacks[i].onReportStatus(gnssStatus);
-        }
+        Binder.withCleanCallingIdentity(() -> {
+            for (int i = 0; i < mStatusCallbacks.length; i++) {
+                mStatusCallbacks[i].onReportStatus(gnssStatus);
+            }
+        });
     }
 
     @NativeEntryPoint
     void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0DbHzs,
             float[] elevations, float[] azimuths, float[] carrierFrequencies,
             float[] basebandCn0DbHzs) {
-        GnssStatus gnssStatus = GnssStatus.wrap(svCount, svidWithFlags, cn0DbHzs, elevations,
-                azimuths, carrierFrequencies, basebandCn0DbHzs);
-        for (int i = 0; i < mSvStatusCallbacks.length; i++) {
-            mSvStatusCallbacks[i].onReportSvStatus(gnssStatus);
-        }
+        Binder.withCleanCallingIdentity(() -> {
+            GnssStatus gnssStatus = GnssStatus.wrap(svCount, svidWithFlags, cn0DbHzs, elevations,
+                    azimuths, carrierFrequencies, basebandCn0DbHzs);
+            for (int i = 0; i < mSvStatusCallbacks.length; i++) {
+                mSvStatusCallbacks[i].onReportSvStatus(gnssStatus);
+            }
+        });
     }
 
     @NativeEntryPoint
     void reportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
-        mAGpsCallbacks.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr);
+        Binder.withCleanCallingIdentity(
+                () -> mAGpsCallbacks.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr));
     }
 
     @NativeEntryPoint
     void reportNmea(long timestamp) {
-        if (mItarSpeedLimitExceeded) {
-            return;
-        }
+        Binder.withCleanCallingIdentity(() -> {
+            if (mItarSpeedLimitExceeded) {
+                return;
+            }
 
-        for (int i = 0; i < mNmeaCallbacks.length; i++) {
-            mNmeaCallbacks[i].onReportNmea(timestamp);
-        }
+            for (int i = 0; i < mNmeaCallbacks.length; i++) {
+                mNmeaCallbacks[i].onReportNmea(timestamp);
+            }
+        });
     }
 
     @NativeEntryPoint
     void reportMeasurementData(GnssMeasurementsEvent event) {
-        if (mItarSpeedLimitExceeded) {
-            return;
-        }
+        Binder.withCleanCallingIdentity(() -> {
+            if (mItarSpeedLimitExceeded) {
+                return;
+            }
 
-        for (int i = 0; i < mMeasurementCallbacks.length; i++) {
-            mMeasurementCallbacks[i].onReportMeasurements(event);
-        }
+            for (int i = 0; i < mMeasurementCallbacks.length; i++) {
+                mMeasurementCallbacks[i].onReportMeasurements(event);
+            }
+        });
     }
 
     @NativeEntryPoint
     void reportAntennaInfo(List<GnssAntennaInfo> antennaInfos) {
-        for (int i = 0; i < mAntennaInfoCallbacks.length; i++) {
-            mAntennaInfoCallbacks[i].onReportAntennaInfo(antennaInfos);
-        }
+        Binder.withCleanCallingIdentity(() -> {
+            for (int i = 0; i < mAntennaInfoCallbacks.length; i++) {
+                mAntennaInfoCallbacks[i].onReportAntennaInfo(antennaInfos);
+            }
+        });
     }
 
     @NativeEntryPoint
     void reportNavigationMessage(GnssNavigationMessage event) {
-        if (mItarSpeedLimitExceeded) {
-            return;
-        }
+        Binder.withCleanCallingIdentity(() -> {
+            if (mItarSpeedLimitExceeded) {
+                return;
+            }
 
-        for (int i = 0; i < mNavigationMessageCallbacks.length; i++) {
-            mNavigationMessageCallbacks[i].onReportNavigationMessage(event);
-        }
+            for (int i = 0; i < mNavigationMessageCallbacks.length; i++) {
+                mNavigationMessageCallbacks[i].onReportNavigationMessage(event);
+            }
+        });
     }
 
     @NativeEntryPoint
@@ -1061,15 +1078,17 @@
 
     private void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
             GnssCapabilities newCapabilities) {
-        if (newCapabilities.equals(oldCapabilities)) {
-            return;
-        }
+        Binder.withCleanCallingIdentity(() -> {
+            if (newCapabilities.equals(oldCapabilities)) {
+                return;
+            }
 
-        Log.i(TAG, "gnss capabilities changed to " + newCapabilities);
+            Log.i(TAG, "gnss capabilities changed to " + newCapabilities);
 
-        for (int i = 0; i < mBaseCallbacks.length; i++) {
-            mBaseCallbacks[i].onCapabilitiesChanged(oldCapabilities, newCapabilities);
-        }
+            for (int i = 0; i < mBaseCallbacks.length; i++) {
+                mBaseCallbacks[i].onCapabilitiesChanged(oldCapabilities, newCapabilities);
+            }
+        });
     }
 
     @NativeEntryPoint
@@ -1089,88 +1108,103 @@
 
     @NativeEntryPoint
     void reportLocationBatch(Location[] locations) {
-        for (int i = 0; i < mLocationCallbacks.length; i++) {
-            mLocationCallbacks[i].onReportLocations(locations);
-        }
+        Binder.withCleanCallingIdentity(() -> {
+            for (int i = 0; i < mLocationCallbacks.length; i++) {
+                mLocationCallbacks[i].onReportLocations(locations);
+            }
+        });
     }
 
     @NativeEntryPoint
     void psdsDownloadRequest(int psdsType) {
-        mPsdsCallbacks.onRequestPsdsDownload(psdsType);
+        Binder.withCleanCallingIdentity(() -> mPsdsCallbacks.onRequestPsdsDownload(psdsType));
     }
 
     @NativeEntryPoint
     void reportGeofenceTransition(int geofenceId, Location location, int transition,
             long transitionTimestamp) {
-        mGeofenceCallbacks.onReportGeofenceTransition(geofenceId, location, transition,
-                transitionTimestamp);
+        Binder.withCleanCallingIdentity(
+                () -> mGeofenceCallbacks.onReportGeofenceTransition(geofenceId, location,
+                        transition, transitionTimestamp));
     }
 
     @NativeEntryPoint
     void reportGeofenceStatus(int status, Location location) {
-        mGeofenceCallbacks.onReportGeofenceStatus(status, location);
+        Binder.withCleanCallingIdentity(
+                () -> mGeofenceCallbacks.onReportGeofenceStatus(status, location));
     }
 
     @NativeEntryPoint
     void reportGeofenceAddStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
-        mGeofenceCallbacks.onReportGeofenceAddStatus(geofenceId, status);
+        Binder.withCleanCallingIdentity(
+                () -> mGeofenceCallbacks.onReportGeofenceAddStatus(geofenceId, status));
     }
 
     @NativeEntryPoint
     void reportGeofenceRemoveStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
-        mGeofenceCallbacks.onReportGeofenceRemoveStatus(geofenceId, status);
+        Binder.withCleanCallingIdentity(
+                () -> mGeofenceCallbacks.onReportGeofenceRemoveStatus(geofenceId, status));
     }
 
     @NativeEntryPoint
     void reportGeofencePauseStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
-        mGeofenceCallbacks.onReportGeofencePauseStatus(geofenceId, status);
+        Binder.withCleanCallingIdentity(
+                () -> mGeofenceCallbacks.onReportGeofencePauseStatus(geofenceId, status));
     }
 
     @NativeEntryPoint
     void reportGeofenceResumeStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
-        mGeofenceCallbacks.onReportGeofenceResumeStatus(geofenceId, status);
+        Binder.withCleanCallingIdentity(
+                () -> mGeofenceCallbacks.onReportGeofenceResumeStatus(geofenceId, status));
     }
 
     @NativeEntryPoint
     void reportNiNotification(int notificationId, int niType, int notifyFlags,
             int timeout, int defaultResponse, String requestorId, String text,
             int requestorIdEncoding, int textEncoding) {
-        mNotificationCallbacks.onReportNiNotification(notificationId, niType, notifyFlags, timeout,
-                    defaultResponse, requestorId, text, requestorIdEncoding, textEncoding);
+        Binder.withCleanCallingIdentity(
+                () -> mNotificationCallbacks.onReportNiNotification(notificationId, niType,
+                        notifyFlags, timeout, defaultResponse, requestorId, text,
+                        requestorIdEncoding, textEncoding));
     }
 
     @NativeEntryPoint
     void requestSetID(int flags) {
-        mAGpsCallbacks.onRequestSetID(flags);
+        Binder.withCleanCallingIdentity(() -> mAGpsCallbacks.onRequestSetID(flags));
     }
 
     @NativeEntryPoint
     void requestLocation(boolean independentFromGnss, boolean isUserEmergency) {
-        mLocationRequestCallbacks.onRequestLocation(independentFromGnss, isUserEmergency);
+        Binder.withCleanCallingIdentity(
+                () -> mLocationRequestCallbacks.onRequestLocation(independentFromGnss,
+                        isUserEmergency));
     }
 
     @NativeEntryPoint
     void requestUtcTime() {
-        mTimeCallbacks.onRequestUtcTime();
+        Binder.withCleanCallingIdentity(() -> mTimeCallbacks.onRequestUtcTime());
     }
 
     @NativeEntryPoint
     void requestRefLocation() {
-        mLocationRequestCallbacks.onRequestRefLocation();
+        Binder.withCleanCallingIdentity(
+                () -> mLocationRequestCallbacks.onRequestRefLocation());
     }
 
     @NativeEntryPoint
     void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
             String otherProtocolStackName, byte requestor, String requestorId,
             byte responseType, boolean inEmergencyMode, boolean isCachedLocation) {
-        mNotificationCallbacks.onReportNfwNotification(proxyAppPackageName, protocolStack,
-                    otherProtocolStackName, requestor, requestorId, responseType, inEmergencyMode,
-                    isCachedLocation);
+        Binder.withCleanCallingIdentity(
+                () -> mNotificationCallbacks.onReportNfwNotification(proxyAppPackageName,
+                        protocolStack, otherProtocolStackName, requestor, requestorId, responseType,
+                        inEmergencyMode, isCachedLocation));
     }
 
     @NativeEntryPoint
     boolean isInEmergencySession() {
-        return mEmergencyHelper.isInEmergency(mConfiguration.getEsExtensionSec());
+        return Binder.withCleanCallingIdentity(
+                () -> mEmergencyHelper.isInEmergency(mConfiguration.getEsExtensionSec()));
     }
 
     /**
diff --git a/services/core/java/com/android/server/location/injector/LocationUsageLogger.java b/services/core/java/com/android/server/location/injector/LocationUsageLogger.java
index 244a8e0..af21bcb 100644
--- a/services/core/java/com/android/server/location/injector/LocationUsageLogger.java
+++ b/services/core/java/com/android/server/location/injector/LocationUsageLogger.java
@@ -49,9 +49,9 @@
      * Log a location API usage event.
      */
     public void logLocationApiUsage(int usageType, int apiInUse,
-            String packageName, String provider, LocationRequest locationRequest,
-            boolean hasListener, boolean hasIntent,
-            Geofence geofence, boolean foreground) {
+            String packageName, String attributionTag, String provider,
+            LocationRequest locationRequest, boolean hasListener,
+            boolean hasIntent, Geofence geofence, boolean foreground) {
         try {
             if (hitApiUsageLogCap()) {
                 return;
@@ -84,7 +84,8 @@
                     isGeofenceNull
                         ? LocationStatsEnums.RADIUS_UNKNOWN
                         : bucketizeRadius(geofence.getRadius()),
-                    categorizeActivityImportance(foreground));
+                    categorizeActivityImportance(foreground),
+                    attributionTag);
         } catch (Exception e) {
             // Swallow exceptions to avoid crashing LMS.
             Log.w(TAG, "Failed to log API usage to statsd.", e);
@@ -114,7 +115,8 @@
                             /* isListenerNull= */ true,
                             /* isIntentNull= */ true),
                     /* bucketizedRadius= */ 0,
-                    LocationStatsEnums.IMPORTANCE_UNKNOWN);
+                    LocationStatsEnums.IMPORTANCE_UNKNOWN,
+                    /* attribution_tag */ null);
         } catch (Exception e) {
             Log.w(TAG, "Failed to log API usage to statsd.", e);
         }
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 2aa6f28..dc8b1d0 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -1854,6 +1854,7 @@
                 LocationStatsEnums.USAGE_STARTED,
                 LocationStatsEnums.API_REQUEST_LOCATION_UPDATES,
                 registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
                 mName,
                 registration.getRequest(),
                 key instanceof PendingIntent,
@@ -1882,6 +1883,7 @@
                 LocationStatsEnums.USAGE_ENDED,
                 LocationStatsEnums.API_REQUEST_LOCATION_UPDATES,
                 registration.getIdentity().getPackageName(),
+                registration.getIdentity().getAttributionTag(),
                 mName,
                 registration.getRequest(),
                 key instanceof PendingIntent,
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 1acbabd..ca8202f 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -105,6 +105,12 @@
     private final SparseSetArray<Integer> mQueriesViaComponent = new SparseSetArray<>();
 
     /**
+     * A mapping from the set of App IDs that query other App IDs via library name to the
+     * list of packages that they can see.
+     */
+    private final SparseSetArray<Integer> mQueryableViaUsesLibrary = new SparseSetArray<>();
+
+    /**
      * Executor for running reasonably short background tasks such as building the initial
      * visibility cache.
      */
@@ -239,6 +245,7 @@
         Snapshots.copy(mImplicitlyQueryable, orig.mImplicitlyQueryable);
         Snapshots.copy(mQueriesViaPackage, orig.mQueriesViaPackage);
         Snapshots.copy(mQueriesViaComponent, orig.mQueriesViaComponent);
+        Snapshots.copy(mQueryableViaUsesLibrary, orig.mQueryableViaUsesLibrary);
         mQueriesViaComponentRequireRecompute = orig.mQueriesViaComponentRequireRecompute;
         mForceQueryable.addAll(orig.mForceQueryable);
         mForceQueryableByDevicePackageNames = orig.mForceQueryableByDevicePackageNames;
@@ -508,6 +515,22 @@
         return false;
     }
 
+    private static boolean canQueryViaUsesLibrary(AndroidPackage querying,
+            AndroidPackage potentialTarget) {
+        if (potentialTarget.getLibraryNames().isEmpty()) {
+            return false;
+        }
+        final List<String> libNames = potentialTarget.getLibraryNames();
+        for (int i = 0, size = libNames.size(); i < size; i++) {
+            final String libName = libNames.get(i);
+            if (querying.getUsesLibraries().contains(libName)
+                    || querying.getUsesOptionalLibraries().contains(libName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private static boolean matchesProviders(
             Set<String> queriesAuthorities, AndroidPackage potentialTarget) {
         for (int p = ArrayUtils.size(potentialTarget.getProviders()) - 1; p >= 0; p--) {
@@ -707,6 +730,9 @@
                         || canQueryAsInstaller(existingSetting, newPkg)) {
                     mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
                 }
+                if (canQueryViaUsesLibrary(existingPkg, newPkg)) {
+                    mQueryableViaUsesLibrary.add(existingSetting.appId, newPkgSetting.appId);
+                }
             }
             // now we'll evaluate our new package's ability to see existing packages
             if (!mForceQueryable.contains(existingSetting.appId)) {
@@ -718,6 +744,9 @@
                         || canQueryAsInstaller(newPkgSetting, existingPkg)) {
                     mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
                 }
+                if (canQueryViaUsesLibrary(newPkg, existingPkg)) {
+                    mQueryableViaUsesLibrary.add(newPkgSetting.appId, existingSetting.appId);
+                }
             }
             // if either package instruments the other, mark both as visible to one another
             if (newPkgSetting.pkg != null && existingSetting.pkg != null
@@ -1035,6 +1064,10 @@
             for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) {
                 mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.appId);
             }
+            mQueryableViaUsesLibrary.remove(setting.appId);
+            for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) {
+                mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i), setting.appId);
+            }
 
             mForceQueryable.remove(setting.appId);
 
@@ -1315,6 +1348,18 @@
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             }
 
+            try {
+                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueryableViaUsesLibrary");
+                if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) {
+                    if (DEBUG_LOGGING) {
+                        log(callingSetting, targetPkgSetting, "queryable for library users");
+                    }
+                    return false;
+                }
+            } finally {
+                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+            }
+
             return true;
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -1394,6 +1439,8 @@
                     filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId),
                     mImplicitlyQueryable, "      ", expandPackages);
         }
+        pw.println("  queryable via uses-library:");
+        dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, "    ", expandPackages);
     }
 
     private static void dumpQueriesMap(PrintWriter pw, @Nullable Integer filteringId,
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
index b34611b..29322e2 100644
--- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -180,6 +180,8 @@
             mId = id;
             mListener = listener;
             mDataLoader = null;
+
+            callListener(IDataLoaderStatusListener.DATA_LOADER_BINDING);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 58e2aa2..bafe987 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1011,8 +1011,9 @@
                         "DataLoader installation of APEX modules is not allowed.");
             }
 
-            if (this.params.dataLoaderParams.getComponentName().getPackageName()
-                    == SYSTEM_DATA_LOADER_PACKAGE && mContext.checkCallingOrSelfPermission(
+            boolean systemDataLoader = SYSTEM_DATA_LOADER_PACKAGE.equals(
+                    this.params.dataLoaderParams.getComponentName().getPackageName());
+            if (systemDataLoader && mContext.checkCallingOrSelfPermission(
                     Manifest.permission.USE_SYSTEM_DATA_LOADERS)
                     != PackageManager.PERMISSION_GRANTED) {
                 throw new SecurityException("You need the "
@@ -3060,7 +3061,7 @@
         // Also stage .dm.fsv_sig. .dm may be required to install with fs-verity signature on
         // supported on older devices.
         maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile,
-                VerityUtils.isFsVeritySupported() && DexMetadataHelper.isFsVerityRequired());
+                DexMetadataHelper.isFsVerityRequired());
     }
 
     private void storeBytesToInstallationFile(final String localPath, final String absolutePath,
@@ -3653,6 +3654,7 @@
             @Override
             public void onStatusChanged(int dataLoaderId, int status) {
                 switch (status) {
+                    case IDataLoaderStatusListener.DATA_LOADER_BINDING:
                     case IDataLoaderStatusListener.DATA_LOADER_STOPPED:
                     case IDataLoaderStatusListener.DATA_LOADER_DESTROYED:
                         return;
@@ -3763,8 +3765,8 @@
             healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
             healthCheckParams.unhealthyMonitoringMs = INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
 
-            final boolean systemDataLoader =
-                    params.getComponentName().getPackageName() == SYSTEM_DATA_LOADER_PACKAGE;
+            final boolean systemDataLoader = SYSTEM_DATA_LOADER_PACKAGE.equals(
+                    params.getComponentName().getPackageName());
 
             final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() {
                 @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ff87f1c..dfe72b2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -410,6 +410,7 @@
 import com.android.server.utils.WatchedArrayMap;
 import com.android.server.utils.WatchedLongSparseArray;
 import com.android.server.utils.WatchedSparseBooleanArray;
+import com.android.server.utils.WatchedSparseIntArray;
 import com.android.server.utils.Watcher;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
@@ -892,7 +893,7 @@
     // that created the isolated process.
     @Watched
     @GuardedBy("mLock")
-    final SparseIntArray mIsolatedOwners = new SparseIntArray();
+    final WatchedSparseIntArray mIsolatedOwners = new WatchedSparseIntArray();
 
     /**
      * Tracks new system packages [received in an OTA] that we expect to
@@ -1795,7 +1796,7 @@
         public static final int SNAPPED = 2;
 
         public final Settings settings;
-        public final SparseIntArray isolatedOwners;
+        public final WatchedSparseIntArray isolatedOwners;
         public final WatchedArrayMap<String, AndroidPackage> packages;
         public final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibs;
         public final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> staticLibs;
@@ -1814,7 +1815,7 @@
         Snapshot(int type) {
             if (type == Snapshot.SNAPPED) {
                 settings = mSettings.snapshot();
-                isolatedOwners = mIsolatedOwners.clone();
+                isolatedOwners = mIsolatedOwners.snapshot();
                 packages = mPackages.snapshot();
                 sharedLibs = mSharedLibraries.snapshot();
                 staticLibs = mStaticLibsByDeclaringPackage.snapshot();
@@ -2016,7 +2017,7 @@
         // Cached attributes.  The names in this class are the same as the
         // names in PackageManagerService; see that class for documentation.
         private final Settings mSettings;
-        private final SparseIntArray mIsolatedOwners;
+        private final WatchedSparseIntArray mIsolatedOwners;
         private final WatchedArrayMap<String, AndroidPackage> mPackages;
         private final WatchedArrayMap<ComponentName, ParsedInstrumentation>
                 mInstrumentation;
@@ -3551,7 +3552,7 @@
         public String getInstantAppPackageName(int callingUid) {
             // If the caller is an isolated app use the owner's uid for the lookup.
             if (Process.isIsolated(callingUid)) {
-                callingUid = mIsolatedOwners.get(callingUid);
+                callingUid = getIsolatedOwner(callingUid);
             }
             final int appId = UserHandle.getAppId(callingUid);
             final Object obj = mSettings.getSettingLPr(appId);
@@ -3563,6 +3564,19 @@
             return null;
         }
 
+        /**
+         * Finds the owner for the provided isolated UID. Throws IllegalStateException if no such
+         * isolated UID is found.
+         */
+        private int getIsolatedOwner(int isolatedUid) {
+            final int ownerUid = mIsolatedOwners.get(isolatedUid, -1);
+            if (ownerUid == -1) {
+                throw new IllegalStateException(
+                        "No owner UID found for isolated UID " + isolatedUid);
+            }
+            return ownerUid;
+        }
+
         public String resolveExternalPackageNameLPr(AndroidPackage pkg) {
             if (pkg.getStaticSharedLibName() != null) {
                 return pkg.getManifestPackageName();
@@ -3929,7 +3943,7 @@
         public boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
                 int callingUid) {
             if (Process.isIsolated(callingUid)) {
-                callingUid = mIsolatedOwners.get(callingUid);
+                callingUid = getIsolatedOwner(callingUid);
             }
             final PackageSetting ps = mSettings.getPackageLPr(packageName);
             final boolean returnAllowed =
@@ -4083,7 +4097,7 @@
                 @Nullable ComponentName component, @ComponentType int componentType, int userId) {
             // if we're in an isolated process, get the real calling UID
             if (Process.isIsolated(callingUid)) {
-                callingUid = mIsolatedOwners.get(callingUid);
+                callingUid = getIsolatedOwner(callingUid);
             }
             final String instantAppPkgName = getInstantAppPackageName(callingUid);
             final boolean callerIsInstantApp = instantAppPkgName != null;
@@ -6164,6 +6178,7 @@
         mAppsFilter.registerObserver(mWatcher);
         mInstantAppRegistry.registerObserver(mWatcher);
         mSettings.registerObserver(mWatcher);
+        mIsolatedOwners.registerObserver(mWatcher);
         // If neither "build" attribute is true then this may be a mockito test, and verification
         // can fail as a false positive.
         Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild));
@@ -27803,13 +27818,23 @@
     }
 
     private static String getDefaultTimeouts() {
-        return DeviceConfig.getString(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
-                PROPERTY_INCFS_DEFAULT_TIMEOUTS, "");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return DeviceConfig.getString(NAMESPACE_PACKAGE_MANAGER_SERVICE,
+                    PROPERTY_INCFS_DEFAULT_TIMEOUTS, "");
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     private static String getKnownDigestersList() {
-        return DeviceConfig.getString(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
-                PROPERTY_KNOWN_DIGESTERS_LIST, "");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return DeviceConfig.getString(NAMESPACE_PACKAGE_MANAGER_SERVICE,
+                    PROPERTY_KNOWN_DIGESTERS_LIST, "");
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
index 0c2b4c5..b4bcde7 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java
@@ -80,14 +80,14 @@
      * been preserved for migration purposes, but is otherwise ignored. Corresponds to
      * {@link PackageManager#INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS}.
      */
-    int APPROVAL_LEVEL_LEGACY_ALWAYS = 1;
+    int APPROVAL_LEVEL_LEGACY_ALWAYS = 2;
 
     /**
      * The app has been chosen by the user through
      * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)},
      * indicating an explicit choice to use this app to open an unverified domain.
      */
-    int APPROVAL_LEVEL_SELECTION = 2;
+    int APPROVAL_LEVEL_SELECTION = 3;
 
     /**
      * The app is approved through the digital asset link statement being hosted at the domain
@@ -95,7 +95,7 @@
      * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)} by
      * the domain verification agent on device.
      */
-    int APPROVAL_LEVEL_VERIFIED = 3;
+    int APPROVAL_LEVEL_VERIFIED = 4;
 
     /**
      * The app has been installed as an instant app, which grants it total authority on the domains
@@ -105,7 +105,7 @@
      * The user is still able to disable instant app link handling through
      * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String, boolean)}.
      */
-    int APPROVAL_LEVEL_INSTANT_APP = 4;
+    int APPROVAL_LEVEL_INSTANT_APP = 5;
 
     /**
      * Defines the possible values for {@link #approvalLevelForDomain(PackageSetting, Intent, int)}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 047e3b3..ffea6a7 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -345,6 +345,9 @@
      */
     private final Object mLock = new Object();
 
+    /** List of {@link ScreenOnListener}s which do not belong to the default display. */
+    private final SparseArray<ScreenOnListener> mScreenOnListeners = new SparseArray<>();
+
     Context mContext;
     IWindowManager mWindowManager;
     WindowManagerFuncs mWindowManagerFuncs;
@@ -434,8 +437,25 @@
     volatile boolean mBeganFromNonInteractive;
     volatile boolean mEndCallKeyHandled;
     volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
-    volatile boolean mGoingToSleep;
-    volatile boolean mRequestedOrGoingToSleep;
+
+    /**
+     * {@code true} if the device is entering a low-power state; {@code false otherwise}.
+     *
+     * <p>This differs from {@link #mRequestedOrSleepingDefaultDisplay} which tracks the power state
+     * of the {@link #mDefaultDisplay default display} versus the power state of the entire device.
+     */
+    volatile boolean mDeviceGoingToSleep;
+
+    /**
+     * {@code true} if the {@link #mDefaultDisplay default display} is entering or was requested to
+     * enter a low-power state; {@code false otherwise}.
+     *
+     * <p>This differs from {@link #mDeviceGoingToSleep} which tracks the power state of the entire
+     * device versus the power state of the {@link #mDefaultDisplay default display}.
+     */
+    // TODO(b/178103325): Track sleep/requested sleep for every display.
+    volatile boolean mRequestedOrSleepingDefaultDisplay;
+
     volatile boolean mRecentsVisible;
     volatile boolean mNavBarVirtualKeyHapticFeedbackEnabled = true;
     volatile boolean mPictureInPictureVisible;
@@ -917,13 +937,14 @@
                 case SHORT_PRESS_POWER_NOTHING:
                     break;
                 case SHORT_PRESS_POWER_GO_TO_SLEEP:
-                    goToSleepFromPowerButton(eventTime, 0);
+                    sleepDefaultDisplayFromPowerButton(eventTime, 0);
                     break;
                 case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
-                    goToSleepFromPowerButton(eventTime, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+                    sleepDefaultDisplayFromPowerButton(eventTime,
+                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                     break;
                 case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
-                    if (goToSleepFromPowerButton(eventTime,
+                    if (sleepDefaultDisplayFromPowerButton(eventTime,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE)) {
                         launchHomeFromHotKey(DEFAULT_DISPLAY);
                     }
@@ -951,11 +972,12 @@
     }
 
     /**
-     * Sends the device to sleep as a result of a power button press.
+     * Sends the default display to sleep as a result of a power button press.
      *
-     * @return True if the was device was sent to sleep, false if sleep was suppressed.
+     * @return {@code true} if the device was sent to sleep, {@code false} if the device did not
+     * sleep.
      */
-    private boolean goToSleepFromPowerButton(long eventTime, int flags) {
+    private boolean sleepDefaultDisplayFromPowerButton(long eventTime, int flags) {
         // Before we actually go to sleep, we check the last wakeup reason.
         // If the device very recently woke up from a gesture (like user lifting their device)
         // then ignore the sleep instruction. This is because users have developed
@@ -975,12 +997,12 @@
             }
         }
 
-        goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
+        sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, flags);
         return true;
     }
 
-    private void goToSleep(long eventTime, int reason, int flags) {
-        mRequestedOrGoingToSleep = true;
+    private void sleepDefaultDisplay(long eventTime, int reason, int flags) {
+        mRequestedOrSleepingDefaultDisplay = true;
         mPowerManager.goToSleep(eventTime, reason, flags);
     }
 
@@ -1017,7 +1039,8 @@
                             Settings.Global.THEATER_MODE_ON, 1);
 
                     if (mGoToSleepOnButtonPressTheaterMode && interactive) {
-                        goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
+                        sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
+                                0);
                     }
                 }
                 break;
@@ -1126,7 +1149,7 @@
             case SHORT_PRESS_SLEEP_GO_TO_SLEEP:
             case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME:
                 Slog.i(TAG, "sleepRelease() calling goToSleep(GO_TO_SLEEP_REASON_SLEEP_BUTTON)");
-                goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
+                sleepDefaultDisplay(eventTime, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
                 break;
         }
     }
@@ -3511,7 +3534,7 @@
                             }
                             if ((mEndcallBehavior
                                     & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
-                                goToSleep(event.getEventTime(),
+                                sleepDefaultDisplay(event.getEventTime(),
                                         PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                                 isWakeKey = false;
                             }
@@ -3538,10 +3561,12 @@
                 // Any activity on the power button stops the accessibility shortcut
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = false; // wake-up will be handled separately
+                final boolean isDefaultDisplayOn = Display.isOnState(mDefaultDisplay.getState());
+                final boolean interactiveAndOn = interactive && isDefaultDisplayOn;
                 if (down) {
-                    interceptPowerKeyDown(event, interactive);
+                    interceptPowerKeyDown(event, interactiveAndOn);
                 } else {
-                    interceptPowerKeyUp(event, interactive, canceled);
+                    interceptPowerKeyUp(event, interactiveAndOn, canceled);
                 }
                 break;
             }
@@ -3746,7 +3771,7 @@
         final MutableBoolean outLaunched = new MutableBoolean(false);
         final boolean gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event,
                 interactive, outLaunched);
-        if (outLaunched.value && mRequestedOrGoingToSleep) {
+        if (outLaunched.value && mRequestedOrSleepingDefaultDisplay) {
             mCameraGestureTriggeredDuringGoingToSleep = true;
         }
         return gesturedServiceIntercepted;
@@ -4088,8 +4113,8 @@
                                     pmSleepReason)) + ")");
         }
 
-        mGoingToSleep = true;
-        mRequestedOrGoingToSleep = true;
+        mDeviceGoingToSleep = true;
+        mRequestedOrSleepingDefaultDisplay = true;
 
         if (mKeyguardDelegate != null) {
             mKeyguardDelegate.onStartedGoingToSleep(pmSleepReason);
@@ -4108,8 +4133,8 @@
         }
         MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000);
 
-        mGoingToSleep = false;
-        mRequestedOrGoingToSleep = false;
+        mDeviceGoingToSleep = false;
+        mRequestedOrSleepingDefaultDisplay = false;
         mDefaultDisplayPolicy.setAwake(false);
 
         // We must get this work done here because the power manager will drop
@@ -4253,21 +4278,25 @@
     // Called on the DisplayManager's DisplayPowerController thread.
     @Override
     public void screenTurnedOff(int displayId) {
-        if (displayId != DEFAULT_DISPLAY) {
-            return;
-        }
+        if (DEBUG_WAKEUP) Slog.i(TAG, "Display" + displayId + " turned off...");
 
-        if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turned off...");
-
-        updateScreenOffSleepToken(true);
-        mDefaultDisplayPolicy.screenTurnedOff();
-        synchronized (mLock) {
-            if (mKeyguardDelegate != null) {
-                mKeyguardDelegate.onScreenTurnedOff();
+        if (displayId == DEFAULT_DISPLAY) {
+            updateScreenOffSleepToken(true);
+            mRequestedOrSleepingDefaultDisplay = false;
+            mDefaultDisplayPolicy.screenTurnedOff();
+            synchronized (mLock) {
+                if (mKeyguardDelegate != null) {
+                    mKeyguardDelegate.onScreenTurnedOff();
+                }
+            }
+            mDefaultDisplayRotation.updateOrientationListener();
+            reportScreenStateToVrManager(false);
+            if (mCameraGestureTriggeredDuringGoingToSleep) {
+                wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromPowerKey,
+                        PowerManager.WAKE_REASON_CAMERA_LAUNCH,
+                        "com.android.systemui:CAMERA_GESTURE_PREVENT_LOCK");
             }
         }
-        mDefaultDisplayRotation.updateOrientationListener();
-        reportScreenStateToVrManager(false);
     }
 
     private long getKeyguardDrawnTimeout() {
@@ -4280,27 +4309,28 @@
     // Called on the DisplayManager's DisplayPowerController thread.
     @Override
     public void screenTurningOn(int displayId, final ScreenOnListener screenOnListener) {
-        if (displayId != DEFAULT_DISPLAY) {
-            return;
-        }
+        if (DEBUG_WAKEUP) Slog.i(TAG, "Display " + displayId + " turning on...");
 
-        if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on...");
+        if (displayId == DEFAULT_DISPLAY) {
+            Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn",
+                    0 /* cookie */);
+            updateScreenOffSleepToken(false);
+            mDefaultDisplayPolicy.screenTurnedOn(screenOnListener);
 
-        Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn", 0 /* cookie */);
-        updateScreenOffSleepToken(false);
-        mDefaultDisplayPolicy.screenTurnedOn(screenOnListener);
-
-        synchronized (mLock) {
-            if (mKeyguardDelegate != null && mKeyguardDelegate.hasKeyguard()) {
-                mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
-                mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT,
-                        getKeyguardDrawnTimeout());
-                mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);
-            } else {
-                if (DEBUG_WAKEUP) Slog.d(TAG,
-                        "null mKeyguardDelegate: setting mKeyguardDrawComplete.");
-                mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
+            synchronized (mLock) {
+                if (mKeyguardDelegate != null && mKeyguardDelegate.hasKeyguard()) {
+                    mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
+                    mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT,
+                            getKeyguardDrawnTimeout());
+                    mKeyguardDelegate.onScreenTurningOn(mKeyguardDrawnCallback);
+                } else {
+                    if (DEBUG_WAKEUP) Slog.d(TAG,
+                            "null mKeyguardDelegate: setting mKeyguardDrawComplete.");
+                    mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
+                }
             }
+        } else {
+            mScreenOnListeners.put(displayId, screenOnListener);
         }
     }
 
@@ -4321,11 +4351,12 @@
 
     @Override
     public void screenTurningOff(int displayId, ScreenOffListener screenOffListener) {
+        mWindowManagerFuncs.screenTurningOff(displayId, screenOffListener);
         if (displayId != DEFAULT_DISPLAY) {
             return;
         }
 
-        mWindowManagerFuncs.screenTurningOff(screenOffListener);
+        mRequestedOrSleepingDefaultDisplay = true;
         synchronized (mLock) {
             if (mKeyguardDelegate != null) {
                 mKeyguardDelegate.onScreenTurningOff();
@@ -4380,6 +4411,14 @@
             listener.onScreenOn();
         }
 
+        for (int i = mScreenOnListeners.size() - 1; i >= 0; i--) {
+            final ScreenOnListener screenOnListener = mScreenOnListeners.valueAt(i);
+            if (screenOnListener != null) {
+                screenOnListener.onScreenOn();
+            }
+        }
+        mScreenOnListeners.clear();
+
         if (enableScreen) {
             try {
                 mWindowManager.enableScreenIfNeeded();
@@ -4410,7 +4449,7 @@
 
     @Override
     public boolean okToAnimate() {
-        return mDefaultDisplayPolicy.isAwake() && !mGoingToSleep;
+        return mDefaultDisplayPolicy.isAwake() && !mDeviceGoingToSleep;
     }
 
     /** {@inheritDoc} */
@@ -4777,7 +4816,7 @@
                     mWindowManagerFuncs.lockDeviceNow();
                     break;
                 case LID_BEHAVIOR_SLEEP:
-                    goToSleep(SystemClock.uptimeMillis(),
+                    sleepDefaultDisplay(SystemClock.uptimeMillis(),
                             PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                     break;
diff --git a/services/core/java/com/android/server/policy/SplashScreenSurface.java b/services/core/java/com/android/server/policy/SplashScreenSurface.java
index b9202c3..72933a0 100644
--- a/services/core/java/com/android/server/policy/SplashScreenSurface.java
+++ b/services/core/java/com/android/server/policy/SplashScreenSurface.java
@@ -45,7 +45,7 @@
     }
 
     @Override
-    public void remove() {
+    public void remove(boolean animate) {
         if (DEBUG_SPLASH_SCREEN) Slog.v(TAG, "Removing splash screen window for " + mAppToken + ": "
                         + this + " Callers=" + Debug.getCallers(4));
 
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index b5a9aca..0735977b 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -238,8 +238,9 @@
         /**
          * Removes the starting window surface. Do not hold the window manager lock when calling
          * this method!
+         * @param animate Whether need to play the default exit animation for starting window.
          */
-        void remove();
+        void remove(boolean animate);
     }
 
     /**
@@ -303,9 +304,10 @@
         /**
          * Notifies the window manager that screen is being turned off.
          *
+         * @param displayId the ID of the display which is turning off
          * @param listener callback to call when display can be turned off
          */
-        void screenTurningOff(ScreenOffListener listener);
+        void screenTurningOff(int displayId, ScreenOffListener listener);
 
         /**
          * Convert the lid state to a human readable format.
diff --git a/services/core/java/com/android/server/power/PreRebootLogger.java b/services/core/java/com/android/server/power/PreRebootLogger.java
index 2e4b054..c9e81ed 100644
--- a/services/core/java/com/android/server/power/PreRebootLogger.java
+++ b/services/core/java/com/android/server/power/PreRebootLogger.java
@@ -19,7 +19,6 @@
 import android.annotation.DurationMillisLong;
 import android.annotation.NonNull;
 import android.content.Context;
-import android.os.Binder;
 import android.os.Environment;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
@@ -147,7 +146,6 @@
             return;
         }
 
-        final long token = Binder.clearCallingIdentity();
         try {
             final File dumpFile = new File(dumpDir, serviceName);
             final ParcelFileDescriptor fd = ParcelFileDescriptor.open(dumpFile,
@@ -156,8 +154,6 @@
             binder.dump(fd.getFileDescriptor(), ArrayUtils.emptyArray(String.class));
         } catch (FileNotFoundException | RemoteException e) {
             Slog.e(TAG, String.format("Failed to dump %s service before reboot", serviceName), e);
-        } finally {
-            Binder.restoreCallingIdentity(token);
         }
     }
 }
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 3a08ddc..fc62f5b 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -24,6 +24,7 @@
 import android.content.rollback.PackageRollbackInfo;
 import android.content.rollback.PackageRollbackInfo.RestoreInfo;
 import android.content.rollback.RollbackInfo;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -227,6 +228,15 @@
                 packageSessionIds, extensionVersions);
     }
 
+    private static boolean isLinkPossible(File oldFile, File newFile) {
+        try {
+            return Os.stat(oldFile.getAbsolutePath()).st_dev
+                    == Os.stat(newFile.getAbsolutePath()).st_dev;
+        } catch (ErrnoException ignore) {
+            return false;
+        }
+    }
+
     /**
      * Creates a backup copy of an apk or apex for a package.
      * For packages containing splits, this method should be called for each
@@ -239,16 +249,29 @@
         targetDir.mkdirs();
         File targetFile = new File(targetDir, sourceFile.getName());
 
-        try {
-            // Create a hard link to avoid copy
-            // TODO(b/168562373)
-            // Linking between non-encrypted and encrypted is not supported and we have
-            // encrypted /data/rollback and non-encrypted /data/apex/active. For now this works
-            // because we happen to store encrypted files under /data/apex/active which is no
-            // longer the case when compressed apex rolls out. We have to handle this case in
-            // order not to fall back to copy.
-            Os.link(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath());
-        } catch (ErrnoException ignore) {
+        boolean fallbackToCopy = !isLinkPossible(sourceFile, targetFile);
+        if (!fallbackToCopy) {
+            try {
+                // Create a hard link to avoid copy
+                // TODO(b/168562373)
+                // Linking between non-encrypted and encrypted is not supported and we have
+                // encrypted /data/rollback and non-encrypted /data/apex/active. For now this works
+                // because we happen to store encrypted files under /data/apex/active which is no
+                // longer the case when compressed apex rolls out. We have to handle this case in
+                // order not to fall back to copy.
+                Os.link(sourceFile.getAbsolutePath(), targetFile.getAbsolutePath());
+            } catch (ErrnoException e) {
+                boolean isRollbackTest =
+                        SystemProperties.getBoolean("persist.rollback.is_test", false);
+                if (isRollbackTest) {
+                    throw new IOException(e);
+                } else {
+                    fallbackToCopy = true;
+                }
+            }
+        }
+
+        if (fallbackToCopy) {
             // Fall back to copy if hardlink can't be created
             Files.copy(sourceFile.toPath(), targetFile.toPath());
         }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 7523671..970420a 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -160,4 +160,10 @@
      * Handles a logging command from the WM shell command.
      */
     void handleWindowManagerLoggingCommand(String[] args, ParcelFileDescriptor outFd);
+
+    /**
+     * @see com.android.internal.statusbar.IStatusBar#setNavigationBarLumaSamplingEnabled(int,
+     * boolean)
+     */
+    void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable);
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 546e420..302a23f 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -588,6 +588,15 @@
                 } catch (RemoteException ex) { }
             }
         }
+
+        @Override
+        public void setNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+            if (mBar != null) {
+                try {
+                    mBar.setNavigationBarLumaSamplingEnabled(displayId, enable);
+                } catch (RemoteException ex) { }
+            }
+        }
     };
 
     private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
diff --git a/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
new file mode 100644
index 0000000..c8c828f
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import static libcore.io.IoUtils.closeQuietly;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A class that provides time zone detector state information for metrics.
+ *
+ * <p>
+ * Regarding time zone ID ordinals:
+ * <p>
+ * We don't want to leak user location information by reporting time zone IDs. Instead, time zone
+ * IDs are consistently identified within a given instance of this class by a numeric ID. This
+ * allows comparison of IDs without revealing what those IDs are.
+ */
+public final class MetricsTimeZoneDetectorState {
+
+    @IntDef(prefix = "DETECTION_MODE_",
+            value = { DETECTION_MODE_MANUAL, DETECTION_MODE_GEO, DETECTION_MODE_TELEPHONY})
+    @interface DetectionMode {};
+
+    @DetectionMode
+    public static final int DETECTION_MODE_MANUAL = 0;
+    @DetectionMode
+    public static final int DETECTION_MODE_GEO = 1;
+    @DetectionMode
+    public static final int DETECTION_MODE_TELEPHONY = 2;
+
+    @NonNull
+    private final ConfigurationInternal mConfigurationInternal;
+    @NonNull
+    private final int mDeviceTimeZoneIdOrdinal;
+    @Nullable
+    private final MetricsTimeZoneSuggestion mLatestManualSuggestion;
+    @Nullable
+    private final MetricsTimeZoneSuggestion mLatestTelephonySuggestion;
+    @Nullable
+    private final MetricsTimeZoneSuggestion mLatestGeolocationSuggestion;
+
+    private MetricsTimeZoneDetectorState(
+            @NonNull ConfigurationInternal configurationInternal,
+            int deviceTimeZoneIdOrdinal,
+            @Nullable MetricsTimeZoneSuggestion latestManualSuggestion,
+            @Nullable MetricsTimeZoneSuggestion latestTelephonySuggestion,
+            @Nullable MetricsTimeZoneSuggestion latestGeolocationSuggestion) {
+        mConfigurationInternal = Objects.requireNonNull(configurationInternal);
+        mDeviceTimeZoneIdOrdinal = deviceTimeZoneIdOrdinal;
+        mLatestManualSuggestion = latestManualSuggestion;
+        mLatestTelephonySuggestion = latestTelephonySuggestion;
+        mLatestGeolocationSuggestion = latestGeolocationSuggestion;
+    }
+
+    /**
+     * Creates {@link MetricsTimeZoneDetectorState} from the supplied parameters, using the {@link
+     * OrdinalGenerator} to generate time zone ID ordinals.
+     */
+    public static MetricsTimeZoneDetectorState create(
+            @NonNull OrdinalGenerator<String> tzIdOrdinalGenerator,
+            @NonNull ConfigurationInternal configurationInternal,
+            @NonNull String deviceTimeZoneId,
+            @Nullable ManualTimeZoneSuggestion latestManualSuggestion,
+            @Nullable TelephonyTimeZoneSuggestion latestTelephonySuggestion,
+            @Nullable GeolocationTimeZoneSuggestion latestGeolocationSuggestion) {
+
+        // TODO(b/172934905) Add logic to canonicalize the time zone IDs to Android's preferred IDs
+        //  so that the ordinals will match even when the ID is not identical, just equivalent.
+        int deviceTimeZoneIdOrdinal =
+                tzIdOrdinalGenerator.ordinal(Objects.requireNonNull(deviceTimeZoneId));
+        MetricsTimeZoneSuggestion latestObfuscatedManualSuggestion =
+                createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestManualSuggestion);
+        MetricsTimeZoneSuggestion latestObfuscatedTelephonySuggestion =
+                createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestTelephonySuggestion);
+        MetricsTimeZoneSuggestion latestObfuscatedGeolocationSuggestion =
+                createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestGeolocationSuggestion);
+
+        return new MetricsTimeZoneDetectorState(
+                configurationInternal, deviceTimeZoneIdOrdinal, latestObfuscatedManualSuggestion,
+                latestObfuscatedTelephonySuggestion, latestObfuscatedGeolocationSuggestion);
+    }
+
+    /** Returns true if the device supports telephony time zone detection. */
+    public boolean isTelephonyDetectionSupported() {
+        return mConfigurationInternal.isTelephonyDetectionSupported();
+    }
+
+    /** Returns true if the device supports geolocation time zone detection. */
+    public boolean isGeoDetectionSupported() {
+        return mConfigurationInternal.isGeoDetectionSupported();
+    }
+
+    /** Returns true if user's location can be used generally. */
+    public boolean isUserLocationEnabled() {
+        return mConfigurationInternal.isLocationEnabled();
+    }
+
+    /** Returns the value of the geolocation time zone detection enabled setting. */
+    public boolean getGeoDetectionEnabledSetting() {
+        return mConfigurationInternal.getGeoDetectionEnabledSetting();
+    }
+
+    /** Returns the value of the auto time zone detection enabled setting. */
+    public boolean getAutoDetectionEnabledSetting() {
+        return mConfigurationInternal.getAutoDetectionEnabledSetting();
+    }
+
+    /**
+     * Returns the detection mode the device is currently using, which can be influenced by various
+     * things besides the user's setting.
+     */
+    @DetectionMode
+    public int getDetectionMode() {
+        if (!mConfigurationInternal.getAutoDetectionEnabledBehavior()) {
+            return DETECTION_MODE_MANUAL;
+        } else if (mConfigurationInternal.getGeoDetectionEnabledBehavior()) {
+            return DETECTION_MODE_GEO;
+        } else {
+            return DETECTION_MODE_TELEPHONY;
+        }
+    }
+
+    /**
+     * Returns the ordinal for the device's currently set time zone ID.
+     * See {@link MetricsTimeZoneDetectorState} for information about ordinals.
+     */
+    @NonNull
+    public int getDeviceTimeZoneIdOrdinal() {
+        return mDeviceTimeZoneIdOrdinal;
+    }
+
+    /**
+     * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last manual
+     * suggestion received.
+     */
+    @Nullable
+    public byte[] getLatestManualSuggestionProtoBytes() {
+        return suggestionProtoBytes(mLatestManualSuggestion);
+    }
+
+    /**
+     * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last, best
+     * telephony suggestion received.
+     */
+    @Nullable
+    public byte[] getLatestTelephonySuggestionProtoBytes() {
+        return suggestionProtoBytes(mLatestTelephonySuggestion);
+    }
+
+    /**
+     * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last geolocation
+     * suggestion received.
+     */
+    @Nullable
+    public byte[] getLatestGeolocationSuggestionProtoBytes() {
+        return suggestionProtoBytes(mLatestGeolocationSuggestion);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        MetricsTimeZoneDetectorState that = (MetricsTimeZoneDetectorState) o;
+        return mDeviceTimeZoneIdOrdinal == that.mDeviceTimeZoneIdOrdinal
+                && mConfigurationInternal.equals(that.mConfigurationInternal)
+                && Objects.equals(mLatestManualSuggestion, that.mLatestManualSuggestion)
+                && Objects.equals(mLatestTelephonySuggestion, that.mLatestTelephonySuggestion)
+                && Objects.equals(mLatestGeolocationSuggestion, that.mLatestGeolocationSuggestion);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mConfigurationInternal, mDeviceTimeZoneIdOrdinal,
+                mLatestManualSuggestion, mLatestTelephonySuggestion, mLatestGeolocationSuggestion);
+    }
+
+    @Override
+    public String toString() {
+        return "MetricsTimeZoneDetectorState{"
+                + "mConfigurationInternal=" + mConfigurationInternal
+                + ", mDeviceTimeZoneIdOrdinal=" + mDeviceTimeZoneIdOrdinal
+                + ", mLatestManualSuggestion=" + mLatestManualSuggestion
+                + ", mLatestTelephonySuggestion=" + mLatestTelephonySuggestion
+                + ", mLatestGeolocationSuggestion=" + mLatestGeolocationSuggestion
+                + '}';
+    }
+
+    private static byte[] suggestionProtoBytes(
+            @Nullable MetricsTimeZoneSuggestion suggestion) {
+        if (suggestion == null) {
+            return null;
+        }
+        return suggestion.toBytes();
+    }
+
+    @Nullable
+    private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
+            @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
+            @NonNull ManualTimeZoneSuggestion manualSuggestion) {
+        if (manualSuggestion == null) {
+            return null;
+        }
+
+        int zoneIdOrdinal = zoneIdOrdinalGenerator.ordinal(manualSuggestion.getZoneId());
+        return MetricsTimeZoneSuggestion.createCertain(
+                new int[] { zoneIdOrdinal });
+    }
+
+    @Nullable
+    private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
+            @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
+            @NonNull TelephonyTimeZoneSuggestion telephonySuggestion) {
+        if (telephonySuggestion == null) {
+            return null;
+        }
+        if (telephonySuggestion.getZoneId() == null) {
+            return MetricsTimeZoneSuggestion.createUncertain();
+        }
+        int zoneIdOrdinal = zoneIdOrdinalGenerator.ordinal(telephonySuggestion.getZoneId());
+        return MetricsTimeZoneSuggestion.createCertain(new int[] { zoneIdOrdinal });
+    }
+
+    @Nullable
+    private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion(
+            @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator,
+            @Nullable GeolocationTimeZoneSuggestion geolocationSuggestion) {
+        if (geolocationSuggestion == null) {
+            return null;
+        }
+
+        List<String> zoneIds = geolocationSuggestion.getZoneIds();
+        if (zoneIds == null) {
+            return MetricsTimeZoneSuggestion.createUncertain();
+        }
+        return MetricsTimeZoneSuggestion.createCertain(zoneIdOrdinalGenerator.ordinals(zoneIds));
+    }
+
+    /**
+     * A Java class that closely matches the android.app.time.MetricsTimeZoneSuggestion
+     * proto definition.
+     */
+    private static final class MetricsTimeZoneSuggestion {
+        @Nullable
+        private final int[] mZoneIdOrdinals;
+
+        MetricsTimeZoneSuggestion(@Nullable int[] zoneIdOrdinals) {
+            mZoneIdOrdinals = zoneIdOrdinals;
+        }
+
+        @NonNull
+        static MetricsTimeZoneSuggestion createUncertain() {
+            return new MetricsTimeZoneSuggestion(null);
+        }
+
+        public static MetricsTimeZoneSuggestion createCertain(
+                @NonNull int[] zoneIdOrdinals) {
+            return new MetricsTimeZoneSuggestion(zoneIdOrdinals);
+        }
+
+        boolean isCertain() {
+            return mZoneIdOrdinals != null;
+        }
+
+        @Nullable
+        int[] getZoneIdOrdinals() {
+            return mZoneIdOrdinals;
+        }
+
+        byte[] toBytes() {
+            // We don't get access to the atoms.proto definition for nested proto fields, so we use
+            // an identically specified proto.
+            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+            ProtoOutputStream protoOutputStream = new ProtoOutputStream(byteArrayOutputStream);
+            int typeProtoValue = isCertain()
+                    ? android.app.time.MetricsTimeZoneSuggestion.CERTAIN
+                    : android.app.time.MetricsTimeZoneSuggestion.UNCERTAIN;
+            protoOutputStream.write(android.app.time.MetricsTimeZoneSuggestion.TYPE,
+                    typeProtoValue);
+            if (isCertain()) {
+                for (int zoneIdOrdinal : getZoneIdOrdinals()) {
+                    protoOutputStream.write(
+                            android.app.time.MetricsTimeZoneSuggestion.TIME_ZONE_ORDINALS,
+                            zoneIdOrdinal);
+                }
+            }
+            protoOutputStream.flush();
+            closeQuietly(byteArrayOutputStream);
+            return byteArrayOutputStream.toByteArray();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            MetricsTimeZoneSuggestion that = (MetricsTimeZoneSuggestion) o;
+            return Arrays.equals(mZoneIdOrdinals, that.mZoneIdOrdinals);
+        }
+
+        @Override
+        public int hashCode() {
+            return Arrays.hashCode(mZoneIdOrdinals);
+        }
+
+        @Override
+        public String toString() {
+            return "MetricsTimeZoneSuggestion{"
+                    + "mZoneIdOrdinals=" + Arrays.toString(mZoneIdOrdinals)
+                    + '}';
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java b/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java
new file mode 100644
index 0000000..a448773
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.timezonedetector;
+
+import android.util.ArraySet;
+
+import java.util.List;
+
+/**
+ * A helper class that turns a set of objects into ordinal values, i.e. each object is offered
+ * up via {@link #ordinal(Object)} or similar method, and a number will be returned. If the
+ * object has been seen before by the instance then the same number will be returned. Intended
+ * for situations where it is useful to know if values from some finite set are the same or
+ * different, but the value is either large or may reveal PII. This class relies on {@link
+ * Object#equals(Object)} and {@link Object#hashCode()}.
+ */
+class OrdinalGenerator<T> {
+    private final ArraySet<T> mKnownIds = new ArraySet<>();
+
+    int ordinal(T object) {
+        int ordinal = mKnownIds.indexOf(object);
+        if (ordinal < 0) {
+            ordinal = mKnownIds.size();
+            mKnownIds.add(object);
+        }
+        return ordinal;
+    }
+
+    int[] ordinals(List<T> objects) {
+        int[] ordinals = new int[objects.size()];
+        for (int i = 0; i < ordinals.length; i++) {
+            ordinals[i] = ordinal(objects.get(i));
+        }
+        return ordinals;
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
index cd220b1..d429b87 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -50,4 +50,8 @@
      * available, and so on. This method may be implemented asynchronously.
      */
     void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion);
+
+    /** Generates a state snapshot for metrics. */
+    @NonNull
+    MetricsTimeZoneDetectorState generateMetricsState();
 }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
index 7ba20ee..4e78f5a 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
@@ -90,4 +90,10 @@
         mHandler.post(
                 () -> mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(timeZoneSuggestion));
     }
+
+    @Override
+    @NonNull
+    public MetricsTimeZoneDetectorState generateMetricsState() {
+        return mTimeZoneDetectorStrategy.generateMetricsState();
+    }
 }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index 8266f12..e3f31b6 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -66,9 +66,10 @@
  * <p>Threading:
  *
  * <p>Suggestion calls with a void return type may be handed off to a separate thread and handled
- * asynchronously. Synchronous calls like {@link #getCurrentUserConfigurationInternal()}, and debug
- * calls like {@link #dump(IndentingPrintWriter, String[])}, may be called on a different thread
- * concurrently with other operations.
+ * asynchronously. Synchronous calls like {@link #getCurrentUserConfigurationInternal()},
+ * {@link #generateMetricsState()} and debug calls like {@link
+ * #dump(IndentingPrintWriter, String[])}, may be called on a different thread concurrently with
+ * other operations.
  *
  * @hide
  */
@@ -123,4 +124,8 @@
      * suggestion.
      */
     void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion suggestion);
+
+    /** Generates a state snapshot for metrics. */
+    @NonNull
+    MetricsTimeZoneDetectorState generateMetricsState();
 }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index 2e96a10..5d34dd7 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -371,6 +371,28 @@
         }
     }
 
+    @Override
+    @NonNull
+    public synchronized MetricsTimeZoneDetectorState generateMetricsState() {
+        int currentUserId = mEnvironment.getCurrentUserId();
+        // Just capture one telephony suggestion: the one that would be used right now if telephony
+        // detection is in use.
+        QualifiedTelephonyTimeZoneSuggestion bestQualifiedTelephonySuggestion =
+                findBestTelephonySuggestion();
+        TelephonyTimeZoneSuggestion telephonySuggestion =
+                bestQualifiedTelephonySuggestion == null
+                        ? null : bestQualifiedTelephonySuggestion.suggestion;
+        // A new generator is created each time: we don't want / require consistency.
+        OrdinalGenerator<String> tzIdOrdinalGenerator = new OrdinalGenerator<>();
+        return MetricsTimeZoneDetectorState.create(
+                tzIdOrdinalGenerator,
+                getConfigurationInternal(currentUserId),
+                mEnvironment.getDeviceTimeZone(),
+                getLatestManualSuggestion(),
+                telephonySuggestion,
+                getLatestGeolocationSuggestion());
+    }
+
     private static int scoreTelephonySuggestion(@NonNull TelephonyTimeZoneSuggestion suggestion) {
         int score;
         if (suggestion.getZoneId() == null) {
diff --git a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
index 417a636..4fa920e 100644
--- a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
@@ -42,10 +42,12 @@
     @NonNull private final LocationTimeZoneProviderProxy mProxy;
 
     BinderLocationTimeZoneProvider(
+            @NonNull ProviderMetricsLogger providerMetricsLogger,
             @NonNull ThreadingDomain threadingDomain,
             @NonNull String providerName,
             @NonNull LocationTimeZoneProviderProxy proxy) {
-        super(threadingDomain, providerName, new ZoneInfoDbTimeZoneIdValidator());
+        super(providerMetricsLogger, threadingDomain, providerName,
+                new ZoneInfoDbTimeZoneIdValidator());
         mProxy = Objects.requireNonNull(proxy);
     }
 
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index 5883821..ca4a640 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -45,6 +45,7 @@
 import com.android.server.SystemService;
 import com.android.server.timezonedetector.ServiceConfigAccessor;
 import com.android.server.timezonedetector.TimeZoneDetectorInternal;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderMetricsLogger;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -297,7 +298,9 @@
                     R.string.config_primaryLocationTimeZoneProviderPackageName
             );
         }
-        return new BinderLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy);
+        ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(0);
+        return new BinderLocationTimeZoneProvider(
+                providerMetricsLogger, mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy);
     }
 
     @NonNull
@@ -317,7 +320,9 @@
                     R.string.config_secondaryLocationTimeZoneProviderPackageName
             );
         }
-        return new BinderLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy);
+        ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(1);
+        return new BinderLocationTimeZoneProvider(
+                providerMetricsLogger, mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy);
     }
 
     /** Used for bug triage and in tests to simulate provider events. */
diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
index b97c838..cc815dc6 100644
--- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
@@ -98,6 +98,14 @@
     }
 
     /**
+     * Listener interface used to log provider events for metrics.
+     */
+    interface ProviderMetricsLogger {
+        /** Logs that a provider changed state. */
+        void onProviderStateChanged(@ProviderStateEnum int stateEnum);
+    }
+
+    /**
      * Information about the provider's current state.
      */
     static class ProviderState {
@@ -349,6 +357,7 @@
         }
     }
 
+    @NonNull private final ProviderMetricsLogger mProviderMetricsLogger;
     @NonNull final ThreadingDomain mThreadingDomain;
     @NonNull final Object mSharedLock;
     @NonNull final String mProviderName;
@@ -380,10 +389,12 @@
     @NonNull private TimeZoneIdValidator mTimeZoneIdValidator;
 
     /** Creates the instance. */
-    LocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain,
+    LocationTimeZoneProvider(@NonNull ProviderMetricsLogger providerMetricsLogger,
+            @NonNull ThreadingDomain threadingDomain,
             @NonNull String providerName,
             @NonNull TimeZoneIdValidator timeZoneIdValidator) {
         mThreadingDomain = Objects.requireNonNull(threadingDomain);
+        mProviderMetricsLogger = Objects.requireNonNull(providerMetricsLogger);
         mInitializationTimeoutQueue = threadingDomain.createSingleRunnableQueue();
         mSharedLock = threadingDomain.getLockObject();
         mProviderName = Objects.requireNonNull(providerName);
@@ -485,6 +496,7 @@
             mCurrentState.set(newState);
             onSetCurrentState(newState);
             if (!Objects.equals(newState, oldState)) {
+                mProviderMetricsLogger.onProviderStateChanged(newState.stateEnum);
                 if (mStateChangeRecording) {
                     mRecordedStates.add(newState);
                 }
diff --git a/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java b/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java
new file mode 100644
index 0000000..dfff6f2
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector.location;
+
+import android.annotation.IntRange;
+
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderMetricsLogger;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
+
+/**
+ * The real implementation of {@link ProviderMetricsLogger} which logs using
+ * {@link FrameworkStatsLog}.
+ */
+public class RealProviderMetricsLogger implements ProviderMetricsLogger {
+
+    @IntRange(from = 0, to = 1)
+    private final int mProviderIndex;
+
+    public RealProviderMetricsLogger(@IntRange(from = 0, to = 1) int providerIndex) {
+        mProviderIndex = providerIndex;
+    }
+
+    @Override
+    public void onProviderStateChanged(@ProviderStateEnum int stateEnum) {
+        // TODO(b/172934905): Implement once the atom has landed.
+    }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index aeeabe2..db3d7ad 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -137,6 +137,7 @@
 import static com.android.server.wm.ActivityRecordProto.DEFER_HIDING_CLIENT;
 import static com.android.server.wm.ActivityRecordProto.FILLS_PARENT;
 import static com.android.server.wm.ActivityRecordProto.FRONT_OF_TASK;
+import static com.android.server.wm.ActivityRecordProto.IN_SIZE_COMPAT_MODE;
 import static com.android.server.wm.ActivityRecordProto.IS_ANIMATING;
 import static com.android.server.wm.ActivityRecordProto.IS_WAITING_FOR_TRANSITION_START;
 import static com.android.server.wm.ActivityRecordProto.LAST_ALL_DRAWN;
@@ -2114,7 +2115,7 @@
                     }
                 }
                 if (abort) {
-                    surface.remove();
+                    surface.remove(false /* prepareAnimation */);
                 }
             } else {
                 ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Surface returned was null: %s",
@@ -2128,7 +2129,8 @@
     private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
             boolean allowTaskSnapshot, boolean activityCreated,
             TaskSnapshot snapshot) {
-        if (newTask || !processRunning || (taskSwitch && !activityCreated)) {
+        if ((newTask || !processRunning || (taskSwitch && !activityCreated))
+                && !isActivityTypeHome()) {
             return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
         } else if (taskSwitch && allowTaskSnapshot) {
             if (isSnapshotCompatible(snapshot)) {
@@ -2179,7 +2181,6 @@
                         + ActivityRecord.this + " state " + mTransferringSplashScreenState);
                 if (isTransferringSplashScreen()) {
                     mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
-                    // TODO show default exit splash screen animation
                     removeStartingWindow();
                 }
             }
@@ -2196,6 +2197,9 @@
     }
 
     private boolean transferSplashScreenIfNeeded() {
+        if (!mWmService.mStartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
+            return false;
+        }
         if (!mHandleExitSplashScreen || mStartingSurface == null || mStartingWindow == null
                 || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH) {
             return false;
@@ -2265,10 +2269,14 @@
         }
         // no matter what, remove the starting window.
         mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
-        removeStartingWindow();
+        removeStartingWindowAnimation(false /* prepareAnimation */);
     }
 
     void removeStartingWindow() {
+        removeStartingWindowAnimation(true /* prepareAnimation */);
+    }
+
+    void removeStartingWindowAnimation(boolean prepareAnimation) {
         if (transferSplashScreenIfNeeded()) {
             return;
         }
@@ -2313,7 +2321,7 @@
         mWmService.mAnimationHandler.post(() -> {
             ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Removing startingView=%s", surface);
             try {
-                surface.remove();
+                surface.remove(prepareAnimation);
             } catch (Exception e) {
                 Slog.w(TAG_WM, "Exception when removing starting window", e);
             }
@@ -6190,7 +6198,7 @@
             // Remove orphaned starting window.
             if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this);
             mStartingWindowState = STARTING_WINDOW_REMOVED;
-            removeStartingWindow();
+            removeStartingWindowAnimation(false /* prepareAnimation */);
         }
         if (isState(INITIALIZING) && !shouldBeVisible(
                 true /* behindFullscreenActivity */, true /* ignoringKeyguard */)) {
@@ -8255,6 +8263,7 @@
             proto.write(PROC_ID, app.getPid());
         }
         proto.write(PIP_AUTO_ENTER_ENABLED, pictureInPictureArgs.isAutoEnterEnabled());
+        proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode());
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/BlurController.java b/services/core/java/com/android/server/wm/BlurController.java
index 13295e8..128d452 100644
--- a/services/core/java/com/android/server/wm/BlurController.java
+++ b/services/core/java/com/android/server/wm/BlurController.java
@@ -22,12 +22,17 @@
 import android.os.RemoteException;
 import android.view.ICrossWindowBlurEnabledListener;
 
+import com.android.internal.annotations.GuardedBy;
+
 final class BlurController {
 
     private final RemoteCallbackList<ICrossWindowBlurEnabledListener>
             mBlurEnabledListeners = new RemoteCallbackList<>();
     private final Object mLock = new Object();
+    @GuardedBy("mLock")
     boolean mBlurEnabled;
+    @GuardedBy("mLock")
+    boolean mBlurForceDisabled;
 
     BlurController() {
         mBlurEnabled = CROSS_WINDOW_BLUR_SUPPORTED;
@@ -46,19 +51,24 @@
         mBlurEnabledListeners.unregister(listener);
     }
 
-    private void updateBlurEnabled() {
-        // TODO: add other factors disabling blurs
-        final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED;
+    void setForceCrossWindowBlurDisabled(boolean disable) {
         synchronized (mLock) {
-            if (mBlurEnabled == newEnabled) {
-                return;
-            }
-            mBlurEnabled = newEnabled;
-            notifyBlurEnabledChanged(newEnabled);
+            mBlurForceDisabled = disable;
+            updateBlurEnabledLocked();
         }
+
     }
 
-    private void notifyBlurEnabledChanged(boolean enabled) {
+    private void updateBlurEnabledLocked() {
+        final boolean newEnabled = CROSS_WINDOW_BLUR_SUPPORTED && !mBlurForceDisabled;
+        if (mBlurEnabled == newEnabled) {
+            return;
+        }
+        mBlurEnabled = newEnabled;
+        notifyBlurEnabledChangedLocked(newEnabled);
+    }
+
+    private void notifyBlurEnabledChangedLocked(boolean enabled) {
         int i = mBlurEnabledListeners.beginBroadcast();
         while (i > 0) {
             i--;
@@ -71,6 +81,4 @@
         }
         mBlurEnabledListeners.finishBroadcast();
     }
-
-
 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1d45c6e..426e631 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -131,7 +131,6 @@
 import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE;
 import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT;
 import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
-import static com.android.server.wm.WindowManagerService.SEAMLESS_ROTATION_TIMEOUT_DURATION;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_ASSIGN_LAYERS;
@@ -1811,11 +1810,6 @@
             w.mReportOrientationChanged = true;
         }, true /* traverseTopToBottom */);
 
-        if (rotateSeamlessly) {
-            mWmService.mH.sendNewMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT,
-                    this, SEAMLESS_ROTATION_TIMEOUT_DURATION);
-        }
-
         for (int i = mWmService.mRotationWatchers.size() - 1; i >= 0; i--) {
             final WindowManagerService.RotationWatcher rotationWatcher
                     = mWmService.mRotationWatchers.get(i);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 32152ec..01f0359 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1410,9 +1410,9 @@
             boolean localClient) {
         final InsetsState state =
                 mDisplayContent.getInsetsPolicy().getInsetsForWindowMetrics(attrs);
-        final boolean inSizeCompatMode = WindowState.inSizeCompatMode(attrs, windowToken);
-        outInsetsState.set(state, inSizeCompatMode || localClient);
-        if (inSizeCompatMode) {
+        final boolean hasCompatScale = WindowState.hasCompatScale(attrs, windowToken);
+        outInsetsState.set(state, hasCompatScale || localClient);
+        if (hasCompatScale) {
             final float compatScale = windowToken != null
                     ? windowToken.getSizeCompatScale()
                     : mDisplayContent.mCompatibleScreenScale;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 63cb38a..5df1355 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -567,7 +567,7 @@
         }
         mDisplayContent.forAllWindows(w -> {
             if (w.mSeamlesslyRotated) {
-                w.finishSeamlessRotation(false /* timeout */);
+                w.cancelSeamlessRotation();
                 w.mSeamlesslyRotated = false;
             }
         }, true /* traverseTopToBottom */);
@@ -670,24 +670,6 @@
         }
     }
 
-    void onSeamlessRotationTimeout() {
-        final boolean[] isLayoutNeeded = { false };
-
-        mDisplayContent.forAllWindows(w -> {
-            if (!w.mSeamlesslyRotated) {
-                return;
-            }
-            isLayoutNeeded[0] = true;
-            w.setDisplayLayoutNeeded();
-            w.finishSeamlessRotation(true /* timeout */);
-            markForSeamlessRotation(w, false /* seamlesslyRotated */);
-        }, true /* traverseTopToBottom */);
-
-        if (isLayoutNeeded[0]) {
-            mService.mWindowPlacerLocked.performSurfacePlacement();
-        }
-    }
-
     /**
      * Returns the animation to run for a rotation transition based on the top fullscreen windows
      * {@link android.view.WindowManager.LayoutParams#rotationAnimation} and whether it is currently
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 627af91..1120a07 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -182,8 +182,6 @@
                     if (SHOW_LIGHT_TRANSACTIONS) {
                         Slog.i(TAG_WM, "<<< CLOSE TRANSACTION performDrag");
                     }
-
-                    mDragState.notifyLocationLocked(touchX, touchY);
                 } finally {
                     if (surface != null) {
                         surface.release();
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 0b3c065..08d5e80 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -515,50 +515,6 @@
         mTransaction.setPosition(mSurfaceControl, x - mThumbOffsetX, y - mThumbOffsetY).apply();
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "DRAG %s: pos=(%d,%d)", mSurfaceControl,
                 (int) (x - mThumbOffsetX), (int) (y - mThumbOffsetY));
-
-        notifyLocationLocked(x, y);
-    }
-
-    void notifyLocationLocked(float x, float y) {
-        // Tell the affected window
-        WindowState touchedWin = mDisplayContent.getTouchableWinAtPointLocked(x, y);
-        if (touchedWin != null && !isWindowNotified(touchedWin)) {
-            // The drag point is over a window which was not notified about a drag start.
-            // Pretend it's over empty space.
-            touchedWin = null;
-        }
-
-        try {
-            final int myPid = Process.myPid();
-
-            // have we dragged over a new window?
-            if ((touchedWin != mTargetWindow) && (mTargetWindow != null)) {
-                if (DEBUG_DRAG) {
-                    Slog.d(TAG_WM, "sending DRAG_EXITED to " + mTargetWindow);
-                }
-                // force DRAG_EXITED_EVENT if appropriate
-                DragEvent evt = obtainDragEvent(mTargetWindow, DragEvent.ACTION_DRAG_EXITED,
-                        0, 0, 0, 0, null, null, null, null, null, false);
-                mTargetWindow.mClient.dispatchDragEvent(evt);
-                if (myPid != mTargetWindow.mSession.mPid) {
-                    evt.recycle();
-                }
-            }
-            if (touchedWin != null) {
-                if (false && DEBUG_DRAG) {
-                    Slog.d(TAG_WM, "sending DRAG_LOCATION to " + touchedWin);
-                }
-                DragEvent evt = obtainDragEvent(touchedWin, DragEvent.ACTION_DRAG_LOCATION,
-                        x, y, mThumbOffsetX, mThumbOffsetY, null, null, null, null, null, false);
-                touchedWin.mClient.dispatchDragEvent(evt);
-                if (myPid != touchedWin.mSession.mPid) {
-                    evt.recycle();
-                }
-            }
-        } catch (RemoteException e) {
-            Slog.w(TAG_WM, "can't send drag notification to windows");
-        }
-        mTargetWindow = touchedWin;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/FixedRotationAnimationController.java b/services/core/java/com/android/server/wm/FixedRotationAnimationController.java
index a1e3ac7..aa73170 100644
--- a/services/core/java/com/android/server/wm/FixedRotationAnimationController.java
+++ b/services/core/java/com/android/server/wm/FixedRotationAnimationController.java
@@ -37,10 +37,15 @@
         super(displayContent);
         final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
         mStatusBar = displayPolicy.getStatusBar();
-        // Do not animate movable navigation bar (e.g. non-gesture mode).
+
+        final RecentsAnimationController controller =
+                displayContent.mWmService.getRecentsAnimationController();
+        final boolean navBarControlledByRecents =
+                controller != null && controller.isNavigationBarAttachedToApp();
+        // Do not animate movable navigation bar (e.g. non-gesture mode) or when the navigation bar
+        // is currently controlled by recents animation.
         mNavigationBar = !displayPolicy.navigationBarCanMove()
-                ? displayPolicy.getNavigationBar()
-                : null;
+                && !navBarControlledByRecents ? displayPolicy.getNavigationBar() : null;
     }
 
     /** Applies show animation on the previously hidden window tokens. */
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index c6c7fe0..35e5491 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -30,7 +30,6 @@
 import static com.android.server.wm.InsetsSourceProviderProto.CONTROL_TARGET;
 import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL;
 import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL_TARGET;
-import static com.android.server.wm.InsetsSourceProviderProto.FINISH_SEAMLESS_ROTATE_FRAME_NUMBER;
 import static com.android.server.wm.InsetsSourceProviderProto.FRAME;
 import static com.android.server.wm.InsetsSourceProviderProto.IME_OVERRIDDEN_FRAME;
 import static com.android.server.wm.InsetsSourceProviderProto.IS_LEASH_READY_FOR_DISPATCHING;
@@ -59,6 +58,7 @@
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 
 import java.io.PrintWriter;
+import java.util.function.Consumer;
 
 /**
  * Controller for a specific inset source on the server. It's called provider as it provides the
@@ -84,6 +84,16 @@
     private final Rect mImeOverrideFrame = new Rect();
     private boolean mIsLeashReadyForDispatching;
 
+    private final Consumer<Transaction> mSetLeashPositionConsumer = t -> {
+        if (mControl != null) {
+            final SurfaceControl leash = mControl.getLeash();
+            if (leash != null) {
+                final Point position = mControl.getSurfacePosition();
+                t.setPosition(leash, position.x, position.y);
+            }
+        }
+    };
+
     /** The visibility override from the current controlling window. */
     private boolean mClientVisible;
 
@@ -93,7 +103,6 @@
     private boolean mServerVisible;
 
     private boolean mSeamlessRotating;
-    private long mFinishSeamlessRotateFrameNumber = -1;
 
     private final boolean mControllable;
 
@@ -150,7 +159,6 @@
             // TODO: Ideally, we should wait for the animation to finish so previous window can
             // animate-out as new one animates-in.
             mWin.cancelAnimation();
-            mWin.mPendingPositionChanged = null;
             mWin.mProvidedInsetsSources.remove(mSource.getType());
         }
         ProtoLog.d(WM_DEBUG_IME, "InsetsSource setWin %s", win);
@@ -249,31 +257,16 @@
         if (mControl != null) {
             final Point position = getWindowFrameSurfacePosition();
             if (mControl.setSurfacePosition(position.x, position.y) && mControlTarget != null) {
-                if (!mWin.getWindowFrames().didFrameSizeChange()) {
-                    updateLeashPosition(-1 /* frameNumber */);
-                } else if (mWin.mInRelayout) {
-                    updateLeashPosition(mWin.getFrameNumber());
+                if (mWin.getWindowFrames().didFrameSizeChange()) {
+                    mWin.applyWithNextDraw(mSetLeashPositionConsumer);
                 } else {
-                    mWin.mPendingPositionChanged = this;
+                    mSetLeashPositionConsumer.accept(mWin.getPendingTransaction());
                 }
                 mStateController.notifyControlChanged(mControlTarget);
             }
         }
     }
 
-    void updateLeashPosition(long frameNumber) {
-        if (mControl == null) {
-            return;
-        }
-        final SurfaceControl leash = mControl.getLeash();
-        if (leash != null) {
-            final Transaction t = mDisplayContent.getPendingTransaction();
-            final Point position = mControl.getSurfacePosition();
-            t.setPosition(leash, position.x, position.y);
-            deferTransactionUntil(t, leash, frameNumber);
-        }
-    }
-
     private Point getWindowFrameSurfacePosition() {
         final Rect frame = mWin.getFrame();
         final Point position = new Point();
@@ -281,14 +274,6 @@
         return position;
     }
 
-    private void deferTransactionUntil(Transaction t, SurfaceControl leash, long frameNumber) {
-        if (frameNumber >= 0) {
-            final SurfaceControl barrier = mWin.getClientViewRootSurface();
-            t.deferTransactionUntil(mWin.getSurfaceControl(), barrier, frameNumber);
-            t.deferTransactionUntil(leash, barrier, frameNumber);
-        }
-    }
-
     /**
      * @see InsetsStateController#onControlFakeTargetChanged(int, InsetsControlTarget)
      */
@@ -342,15 +327,6 @@
         mIsLeashReadyForDispatching = false;
 
         final SurfaceControl leash = mAdapter.mCapturedLeash;
-        final long frameNumber = mFinishSeamlessRotateFrameNumber;
-        mFinishSeamlessRotateFrameNumber = -1;
-        if (mWin.mHasSurface && leash != null) {
-            // We just finished the seamless rotation. We don't want to change the position or the
-            // window crop of the surface controls (including the leash) until the client finishes
-            // drawing the new frame of the new orientation. Although we cannot defer the reparent
-            // operation, it is fine, because reparent won't cause any visual effect.
-            deferTransactionUntil(t, leash, frameNumber);
-        }
         mControlTarget = target;
         updateVisibility();
         mControl = new InsetsSourceControl(mSource.getType(), leash, surfacePosition);
@@ -359,19 +335,14 @@
     }
 
     void startSeamlessRotation() {
-        if (!mSeamlessRotating) {
-            mSeamlessRotating = true;
-
-            // This will revoke the leash and clear the control target.
-            mWin.cancelAnimation();
-        }
+      if (!mSeamlessRotating) {
+          mSeamlessRotating = true;
+          mWin.cancelAnimation();
+      }
     }
 
-    void finishSeamlessRotation(boolean timeout) {
-        if (mSeamlessRotating) {
-            mSeamlessRotating = false;
-            mFinishSeamlessRotateFrameNumber = timeout ? -1 : mWin.getFrameNumber();
-        }
+    void finishSeamlessRotation() {
+        mSeamlessRotating = false;
     }
 
     boolean updateClientVisibility(InsetsControlTarget caller) {
@@ -529,7 +500,6 @@
         proto.write(CLIENT_VISIBLE, mClientVisible);
         proto.write(SERVER_VISIBLE, mServerVisible);
         proto.write(SEAMLESS_ROTATING, mSeamlessRotating);
-        proto.write(FINISH_SEAMLESS_ROTATE_FRAME_NUMBER, mFinishSeamlessRotateFrameNumber);
         proto.write(CONTROLLABLE, mControllable);
         proto.end(token);
     }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e02cce4..914e456 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -17,7 +17,9 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
@@ -104,7 +106,8 @@
     public @interface ReorderMode {}
 
     private final WindowManagerService mService;
-    private final StatusBarManagerInternal mStatusBar;
+    @VisibleForTesting
+    final StatusBarManagerInternal mStatusBar;
     private IRecentsAnimationRunner mRunner;
     private final RecentsAnimationCallbacks mCallbacks;
     private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
@@ -149,6 +152,7 @@
 
     @VisibleForTesting
     boolean mShouldAttachNavBarToAppDuringTransition;
+    private boolean mNavigationBarAttachedToApp;
 
     /**
      * Animates the screenshot of task that used to be controlled by RecentsAnimation.
@@ -369,7 +373,17 @@
         }
 
         @Override
-        public void detachNavigationBarFromApp() {}
+        public void detachNavigationBarFromApp(boolean moveHomeToTop) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                synchronized (mService.getWindowManagerLock()) {
+                    restoreNavigationBarFromApp(moveHomeToTop);
+                    mService.mWindowPlacerLocked.requestTraversal();
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     };
 
     /**
@@ -440,9 +454,7 @@
             return;
         }
 
-        if (mShouldAttachNavBarToAppDuringTransition) {
-            attachNavBarToApp();
-        }
+        attachNavigationBarToApp();
 
         // Adjust the wallpaper visibility for the showing target activity
         ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
@@ -577,32 +589,52 @@
         }
     }
 
-    @VisibleForTesting
-    WindowToken getNavigationBarWindowToken() {
-        WindowState navBar = mDisplayContent.getDisplayPolicy().getNavigationBar();
-        if (navBar != null) {
-            return navBar.mToken;
-        }
-        return null;
+    boolean isNavigationBarAttachedToApp() {
+        return mNavigationBarAttachedToApp;
     }
 
-    private void attachNavBarToApp() {
+    @VisibleForTesting
+    WindowState getNavigationBarWindow() {
+        return mDisplayContent.getDisplayPolicy().getNavigationBar();
+    }
+
+    private void attachNavigationBarToApp() {
+        if (!mShouldAttachNavBarToAppDuringTransition
+                // Skip the case where the nav bar is controlled by fixed rotation.
+                || mDisplayContent.getFixedRotationAnimationController() != null) {
+            return;
+        }
         ActivityRecord topActivity = null;
+        boolean shouldTranslateNavBar = false;
+        final boolean isDisplayLandscape =
+                mDisplayContent.getConfiguration().orientation == ORIENTATION_LANDSCAPE;
         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
             final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
             final Task task = adapter.mTask;
-            if (!task.isHomeOrRecentsRootTask()) {
-                topActivity = task.getTopVisibleActivity();
-                break;
+            final boolean isSplitScreenSecondary =
+                    task.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+            if (task.isHomeOrRecentsRootTask()
+                    // TODO(b/178449492): Will need to update for the new split screen mode once
+                    // it's ready.
+                    // Skip if the task is the secondary split screen and in landscape.
+                    || (isSplitScreenSecondary && isDisplayLandscape)) {
+                continue;
             }
-        }
-        final WindowToken navToken = getNavigationBarWindowToken();
-        if (topActivity == null || navToken == null) {
-            return;
+            shouldTranslateNavBar = isSplitScreenSecondary;
+            topActivity = task.getTopVisibleActivity();
+            break;
         }
 
-        final SurfaceControl.Transaction t = navToken.getPendingTransaction();
-        final SurfaceControl navSurfaceControl = navToken.getSurfaceControl();
+        final WindowState navWindow = getNavigationBarWindow();
+        if (topActivity == null || navWindow == null || navWindow.mToken == null) {
+            return;
+        }
+        mNavigationBarAttachedToApp = true;
+        final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction();
+        final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl();
+        if (shouldTranslateNavBar) {
+            navWindow.setSurfaceTranslationY(-topActivity.getBounds().top);
+        }
         t.reparent(navSurfaceControl, topActivity.getSurfaceControl());
         t.show(navSurfaceControl);
 
@@ -613,16 +645,32 @@
             // Place the nav bar on top of anything else in the top activity.
             t.setLayer(navSurfaceControl, Integer.MAX_VALUE);
         }
+        if (mStatusBar != null) {
+            mStatusBar.setNavigationBarLumaSamplingEnabled(mDisplayId, false);
+        }
     }
 
-    private void restoreNavBarFromApp(boolean animate) {
-        // Reparent the SurfaceControl of nav bar token back.
-        final WindowToken navToken = getNavigationBarWindowToken();
-        final SurfaceControl.Transaction t = mDisplayContent.getPendingTransaction();
-        if (navToken != null) {
-            final WindowContainer parent = navToken.getParent();
-            t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
+    private void restoreNavigationBarFromApp(boolean animate) {
+        if (!mNavigationBarAttachedToApp) {
+            return;
         }
+        if (mStatusBar != null) {
+            mStatusBar.setNavigationBarLumaSamplingEnabled(mDisplayId, true);
+        }
+
+        final WindowState navWindow = getNavigationBarWindow();
+        if (navWindow == null) {
+            return;
+        }
+        navWindow.setSurfaceTranslationY(0);
+
+        if (navWindow.mToken == null) {
+            return;
+        }
+        final SurfaceControl.Transaction t = mDisplayContent.getPendingTransaction();
+        final WindowContainer parent = navWindow.mToken.getParent();
+        // Reparent the SurfaceControl of nav bar token back.
+        t.reparent(navWindow.mToken.getSurfaceControl(), parent.getSurfaceControl());
 
         if (animate) {
             // Run fade-in animation to show navigation bar back to bottom of the display.
@@ -852,9 +900,7 @@
             removeWallpaperAnimation(wallpaperAdapter);
         }
 
-        if (mShouldAttachNavBarToAppDuringTransition) {
-            restoreNavBarFromApp(reorderMode == REORDER_MOVE_TO_TOP);
-        }
+        restoreNavigationBarFromApp(reorderMode == REORDER_MOVE_TO_TOP);
 
         // Clear any pending failsafe runnables
         mService.mH.removeCallbacks(mFailsafeRunnable);
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 3d305e4..1e8b8a5 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -35,9 +35,6 @@
  * Helper class for seamless rotation.
  *
  * Works by transforming the {@link WindowState} back into the old display rotation.
- *
- * Uses {@link Transaction#deferTransactionUntil(SurfaceControl, IBinder, long)} instead of
- * latching on the buffer size to allow for seamless 180 degree rotations.
  */
 public class SeamlessRotator {
 
@@ -103,22 +100,7 @@
      * Removing the transform and the result of the {@link WindowState} layout are both tied to the
      * {@link WindowState} next frame, such that they apply at the same time the client draws the
      * window in the new orientation.
-     *
-     * In the case of a rotation timeout, we want to remove the transform immediately and not defer
-     * it.
      */
-    public void finish(WindowState win, boolean timeout) {
-        final Transaction t = win.getPendingTransaction();
-        finish(t, win);
-        if (win.mWinAnimator.mSurfaceController != null && !timeout) {
-            t.deferTransactionUntil(win.mSurfaceControl,
-                    win.getClientViewRootSurface(), win.getFrameNumber());
-            t.deferTransactionUntil(win.mWinAnimator.mSurfaceController.mSurfaceControl,
-                    win.getClientViewRootSurface(), win.getFrameNumber());
-        }
-    }
-
-    /** Removes the transform and restore to the original last position. */
     void finish(Transaction t, WindowContainer win) {
         mTransform.reset();
         t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index ef4a40f..6c46135 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -40,9 +40,8 @@
     private static final String TAG = TAG_WITH_CLASS_NAME
             ? StartingSurfaceController.class.getSimpleName() : TAG_WM;
     /** Set to {@code true} to enable shell starting surface drawer. */
-    private static final boolean DEBUG_ENABLE_SHELL_DRAWER =
-            SystemProperties.getBoolean("persist.debug.shell_starting_surface", false);
-
+    static final boolean DEBUG_ENABLE_SHELL_DRAWER =
+            SystemProperties.getBoolean("persist.debug.shell_starting_surface", true);
     private final WindowManagerService mService;
 
     public StartingSurfaceController(WindowManagerService wm) {
@@ -139,8 +138,9 @@
         }
 
         @Override
-        public void remove() {
-            mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask);
+        public void remove(boolean animate) {
+            mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask,
+                    animate);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 04a254c..a4b4726 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4135,6 +4135,8 @@
         final StartingWindowInfo info = new StartingWindowInfo();
         info.taskInfo = getTaskInfo();
 
+        info.isKeyguardOccluded =
+            mAtmService.mKeyguardController.isDisplayOccluded(DEFAULT_DISPLAY);
         final ActivityRecord topActivity = getTopMostActivity();
         if (topActivity != null) {
             info.startingWindowTypeParameter =
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index fc6db61..5d22f8f 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -32,6 +32,7 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ParceledListSlice;
+import android.graphics.Rect;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -131,10 +132,28 @@
             });
         }
 
-        void removeStartingWindow(Task task) {
+        void removeStartingWindow(Task task, boolean prepareAnimation) {
             mDeferTaskOrgCallbacksConsumer.accept(() -> {
+                SurfaceControl firstWindowLeash = null;
+                Rect mainFrame = null;
+                // TODO enable shift up animation once we fix flicker test
+//                final boolean playShiftUpAnimation = !task.inMultiWindowMode();
+//                if (prepareAnimation && playShiftUpAnimation) {
+//                    final ActivityRecord topActivity = task.topActivityWithStartingWindow();
+//                    if (topActivity != null) {
+//                        final WindowState mainWindow =
+//                                topActivity.findMainWindow(false/* includeStartingApp */);
+//                        if (mainWindow != null) {
+                // TODO create proper leash instead of the copied SC
+//                            firstWindowLeash = new SurfaceControl(mainWindow.getSurfaceControl(),
+//                                    "TaskOrganizerController.removeStartingWindow");
+//                            mainFrame = mainWindow.getRelativeFrame();
+//                        }
+//                    }
+//                }
                 try {
-                    mTaskOrganizer.removeStartingWindow(task.mTaskId);
+                    mTaskOrganizer.removeStartingWindow(task.mTaskId, firstWindowLeash, mainFrame,
+                            prepareAnimation);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Exception sending onStartTaskFinished callback", e);
                 }
@@ -249,8 +268,8 @@
             mOrganizer.addStartingWindow(t, appToken, launchTheme);
         }
 
-        void removeStartingWindow(Task t) {
-            mOrganizer.removeStartingWindow(t);
+        void removeStartingWindow(Task t, boolean prepareAnimation) {
+            mOrganizer.removeStartingWindow(t, prepareAnimation);
         }
 
         void copySplashScreenView(Task t) {
@@ -495,14 +514,14 @@
         return true;
     }
 
-    void removeStartingWindow(Task task) {
+    void removeStartingWindow(Task task, boolean prepareAnimation) {
         final Task rootTask = task.getRootTask();
         if (rootTask == null || rootTask.mTaskOrganizer == null) {
             return;
         }
         final TaskOrganizerState state =
                 mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
-        state.removeStartingWindow(task);
+        state.removeStartingWindow(task, prepareAnimation);
     }
 
     boolean copySplashScreenView(Task task) {
@@ -865,6 +884,7 @@
             mPendingTaskEvents.remove(pending);
         }
         mPendingTaskEvents.add(pending);
+        mService.mWindowManager.mWindowPlacerLocked.requestTraversal();
         return true;
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 9d35c25..525420e 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -188,7 +188,8 @@
             transferFocusFromWin = displayContent.mCurrentFocus;
         }
         if (!mInputManager.transferTouchFocus(
-                transferFocusFromWin.mInputChannel, mTaskPositioner.mClientChannel)) {
+                transferFocusFromWin.mInputChannel, mTaskPositioner.mClientChannel,
+                false /* isDragDrop */)) {
             Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
             cleanUpTaskPositioner();
             return false;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index b810de9..8915eba 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -37,6 +37,7 @@
 import android.util.ArraySet;
 import android.util.Pair;
 import android.util.Slog;
+import android.view.Display;
 import android.view.InsetsState;
 import android.view.SurfaceControl;
 import android.view.ThreadedRenderer;
@@ -624,7 +625,7 @@
     /**
      * Called when screen is being turned off.
      */
-    void screenTurningOff(ScreenOffListener listener) {
+    void screenTurningOff(int displayId, ScreenOffListener listener) {
         if (shouldDisableSnapshots()) {
             listener.onScreenOff();
             return;
@@ -635,7 +636,7 @@
             try {
                 synchronized (mService.mGlobalLock) {
                     mTmpTasks.clear();
-                    mService.mRoot.forAllTasks(task -> {
+                    mService.mRoot.getDisplayContent(displayId).forAllTasks(task -> {
                         // Since RecentsAnimation will handle task snapshot while switching apps
                         // with the best capture timing (e.g. IME window capture), No need
                         // additional task capture while task is controlled by RecentsAnimation.
@@ -645,7 +646,7 @@
                     });
                     // Allow taking snapshot of home when turning screen off to reduce the delay of
                     // waking from secure lock to home.
-                    final boolean allowSnapshotHome =
+                    final boolean allowSnapshotHome = displayId == Display.DEFAULT_DISPLAY &&
                             mService.mPolicy.isKeyguardSecure(mService.mCurrentUserId);
                     snapshotTasks(mTmpTasks, allowSnapshotHome);
                 }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 07610ab..79a6bd7 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -281,13 +281,14 @@
     }
 
     @Override
-    public void remove() {
+    public void remove(boolean animate) {
         synchronized (mService.mGlobalLock) {
             final long now = SystemClock.uptimeMillis();
             if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS
                     // Show the latest content as soon as possible for unlocking to home.
                     && mActivityType != ACTIVITY_TYPE_HOME) {
-                mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
+                mHandler.postAtTime(() -> remove(false /* prepareAnimation */),
+                        mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
                 ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
                         "Defer removing snapshot surface in %dms", (now - mShownTime));
 
@@ -517,7 +518,7 @@
                 // The orientation of the screen is changing. We better remove the snapshot ASAP as
                 // we are going to wait on the new window in any case to unfreeze the screen, and
                 // the starting window is not needed anymore.
-                sHandler.post(mOuter::remove);
+                sHandler.post(() -> mOuter.remove(false /* prepareAnimation */));
             }
             if (reportDraw) {
                 sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget();
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index 9245f8c..ffd6d21 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -113,7 +113,7 @@
     }
 
     /**
-     * @return true if the width or height has changed since last reported to the client.
+     * @return true if the width or height has changed since last updating resizing window.
      */
     boolean didFrameSizeChange() {
         return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height());
@@ -135,6 +135,13 @@
     }
 
     /**
+     * @return true if the width or height has changed since last reported to the client.
+     */
+    boolean isFrameSizeChangeReported() {
+        return mFrameSizeChanged || didFrameSizeChange();
+    }
+
+    /**
      * Resets the size changed flags so they're all set to false again. This should be called
      * after the frames are reported to client.
      */
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 7450782..53ebfb2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -221,7 +221,8 @@
                 DragState state, Display display, InputManagerService service,
                 InputChannel source) {
             state.register(display);
-            return service.transferTouchFocus(source, state.getInputChannel());
+            return service.transferTouchFocus(source, state.getInputChannel(),
+                    true /* isDragDrop */);
         }
 
         /**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 3ddb6fc..b95674e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -366,9 +366,6 @@
     /** Amount of time (in milliseconds) to delay before declaring a window freeze timeout. */
     static final int WINDOW_FREEZE_TIMEOUT_DURATION = 2000;
 
-    /** Amount of time (in milliseconds) to delay before declaring a seamless rotation timeout. */
-    static final int SEAMLESS_ROTATION_TIMEOUT_DURATION = 2000;
-
     /** Amount of time (in milliseconds) to delay before declaring a window replacement timeout. */
     static final int WINDOW_REPLACEMENT_TIMEOUT_DURATION = 2000;
 
@@ -2236,16 +2233,9 @@
             win.setFrameNumber(frameNumber);
 
             final DisplayContent dc = win.getDisplayContent();
-            if (!dc.mWaitingForConfig) {
-                win.finishSeamlessRotation(false /* timeout */);
-            }
-
-            if (win.mPendingPositionChanged != null) {
-                win.mPendingPositionChanged.updateLeashPosition(frameNumber);
-                win.mPendingPositionChanged = null;
-            }
 
             if (mUseBLASTSync && win.useBLASTSync() && viewVisibility != View.GONE) {
+                win.prepareDrawHandlers();
                 result |= RELAYOUT_RES_BLAST_SYNC;
             }
 
@@ -2990,8 +2980,8 @@
     }
 
     @Override
-    public void screenTurningOff(ScreenOffListener listener) {
-        mTaskSnapshotController.screenTurningOff(listener);
+    public void screenTurningOff(int displayId, ScreenOffListener listener) {
+        mTaskSnapshotController.screenTurningOff(displayId, listener);
     }
 
     @Override
@@ -5086,7 +5076,6 @@
 
         public static final int UPDATE_ANIMATION_SCALE = 51;
         public static final int WINDOW_HIDE_TIMEOUT = 52;
-        public static final int SEAMLESS_ROTATION_TIMEOUT = 54;
         public static final int RESTORE_POINTER_ICON = 55;
         public static final int SET_HAS_OVERLAY_UI = 58;
         public static final int ANIMATION_FAILSAFE = 60;
@@ -5369,13 +5358,6 @@
                     }
                     break;
                 }
-                case SEAMLESS_ROTATION_TIMEOUT: {
-                    final DisplayContent displayContent = (DisplayContent) msg.obj;
-                    synchronized (mGlobalLock) {
-                        displayContent.getDisplayRotation().onSeamlessRotationTimeout();
-                    }
-                    break;
-                }
                 case SET_HAS_OVERLAY_UI: {
                     mAmInternal.setHasOverlayUi(msg.arg1, msg.arg2 == 1);
                     break;
@@ -5693,6 +5675,11 @@
         mBlurController.unregisterCrossWindowBlurEnabledListener(listener);
     }
 
+    @Override
+    public void setForceCrossWindowBlurDisabled(boolean disable) {
+        mBlurController.setForceCrossWindowBlurDisabled(disable);
+    }
+
     // -------------------------------------------------------------
     // Internals
     // -------------------------------------------------------------
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d9b879f..7ebc1cc 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -168,8 +168,8 @@
 import static com.android.server.wm.WindowStateProto.FORCE_SEAMLESS_ROTATION;
 import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS;
 import static com.android.server.wm.WindowStateProto.GLOBAL_SCALE;
+import static com.android.server.wm.WindowStateProto.HAS_COMPAT_SCALE;
 import static com.android.server.wm.WindowStateProto.HAS_SURFACE;
-import static com.android.server.wm.WindowStateProto.IN_SIZE_COMPAT_MODE;
 import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN;
 import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
 import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
@@ -261,6 +261,7 @@
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /** A window in the window manager. */
@@ -725,8 +726,6 @@
      */
     private InsetsState mFrozenInsetsState;
 
-    @Nullable InsetsSourceProvider mPendingPositionChanged;
-
     private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
     private KeyInterceptionInfo mKeyInterceptionInfo;
 
@@ -749,6 +748,42 @@
     private final WindowProcessController mWpcForDisplayAreaConfigChanges;
 
     /**
+     * We split the draw handlers in to a "pending" and "ready" list, in order to solve
+     * sequencing problems. Think of it this way, let's say I update a windows orientation
+     * (in configuration), and then I call applyWithNextDraw. What I'm hoping for is to
+     * apply with the draw that contains the orientation change. However, since the client
+     * can call finishDrawing at any time, it could be about to call a previous call to
+     * finishDrawing (or maybe its already called it, we just haven't handled it). Since this
+     * frame was already completed it had no time to include the orientation change we made.
+     * To solve this problem we accumulate draw handlers in mPendingDrawHandlers, and then force
+     * the client to call relayout. Only the frame post relayout will contain the configuration
+     * change since the window has to relayout), and so in relayout we drain mPendingDrawHandlers
+     * into mReadyDrawHandlers. Finally once we get to finishDrawing we know everything in
+     * mReadyDrawHandlers corresponds to state which was observed by the client and we can
+     * invoke the consumers.
+     */
+    private final List<Consumer<SurfaceControl.Transaction>> mPendingDrawHandlers
+        = new ArrayList<>();
+    private final List<Consumer<SurfaceControl.Transaction>> mReadyDrawHandlers
+        = new ArrayList<>();
+
+    private final Consumer<SurfaceControl.Transaction> mSeamlessRotationFinishedConsumer = t -> {
+        finishSeamlessRotation(t);
+        updateSurfacePosition(t);
+    };
+
+    private final Consumer<SurfaceControl.Transaction> mSetSurfacePositionConsumer = t -> {
+        if (mSurfaceControl != null && mSurfaceControl.isValid()) {
+            t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
+        }
+    };
+
+    /**
+     * @see #setSurfaceTranslationY(int)
+     */
+    private int mSurfaceTranslationY;
+
+    /**
      * Returns the visibility of the given {@link InternalInsetsType type} requested by the client.
      *
      * @param type the given {@link InternalInsetsType type}.
@@ -831,19 +866,27 @@
             mPendingSeamlessRotate.unrotate(transaction, this);
             getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
                     true /* seamlesslyRotated */);
+            applyWithNextDraw(mSeamlessRotationFinishedConsumer);
         }
     }
 
-    void finishSeamlessRotation(boolean timeout) {
-        if (mPendingSeamlessRotate != null) {
-            mPendingSeamlessRotate.finish(this, timeout);
-            mFinishSeamlessRotateFrameNumber = getFrameNumber();
-            mPendingSeamlessRotate = null;
-            getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
-                    false /* seamlesslyRotated */);
-            if (mControllableInsetProvider != null) {
-                mControllableInsetProvider.finishSeamlessRotation(timeout);
-            }
+    void cancelSeamlessRotation() {
+        finishSeamlessRotation(getPendingTransaction());
+    }
+
+    void finishSeamlessRotation(SurfaceControl.Transaction t) {
+        if (mPendingSeamlessRotate == null) {
+            return;
+        }
+
+        mPendingSeamlessRotate.finish(t, this);
+        mFinishSeamlessRotateFrameNumber = getFrameNumber();
+        mPendingSeamlessRotate = null;
+
+        getDisplayContent().getDisplayRotation().markForSeamlessRotation(this,
+            false /* seamlesslyRotated */);
+        if (mControllableInsetProvider != null) {
+            mControllableInsetProvider.finishSeamlessRotation();
         }
     }
 
@@ -1050,18 +1093,18 @@
      * scaling override set.
      * @see CompatModePackages#getCompatScale
      * @see android.content.res.CompatibilityInfo#supportsScreen
-     * @see ActivityRecord#inSizeCompatMode()
+     * @see ActivityRecord#hasSizeCompatBounds()
      */
-    boolean inSizeCompatMode() {
-        return mOverrideScale != 1f || inSizeCompatMode(mAttrs, mActivityRecord);
+    boolean hasCompatScale() {
+        return mOverrideScale != 1f || hasCompatScale(mAttrs, mActivityRecord);
     }
 
     /**
      * @return {@code true} if the application runs in size compatibility mode.
      * @see android.content.res.CompatibilityInfo#supportsScreen
-     * @see ActivityRecord#inSizeCompatMode()
+     * @see ActivityRecord#hasSizeCompatBounds()
      */
-    static boolean inSizeCompatMode(WindowManager.LayoutParams attrs, WindowToken windowToken) {
+    static boolean hasCompatScale(WindowManager.LayoutParams attrs, WindowToken windowToken) {
         return (attrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0
                 || (windowToken != null && windowToken.hasSizeCompatBounds()
                 // Exclude starting window because it is not displayed by the application.
@@ -1266,7 +1309,7 @@
         windowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
 
         windowFrames.mCompatFrame.set(windowFrames.mFrame);
-        if (inSizeCompatMode()) {
+        if (hasCompatScale()) {
             // Also the scaled frame that we report to the app needs to be
             // adjusted to be in its coordinate space.
             windowFrames.mCompatFrame.scale(mInvGlobalScale);
@@ -1538,7 +1581,7 @@
      */
     InsetsState getCompatInsetsState() {
         InsetsState state = getInsetsState();
-        if (inSizeCompatMode()) {
+        if (hasCompatScale()) {
             state = new InsetsState(state, true);
             state.scale(mInvGlobalScale);
         }
@@ -1676,7 +1719,7 @@
     }
 
     void prelayout() {
-        if (inSizeCompatMode()) {
+        if (hasCompatScale()) {
             if (mOverrideScale != 1f) {
                 mGlobalScale = mToken.hasSizeCompatBounds()
                         ? mToken.getSizeCompatScale() * mOverrideScale
@@ -2090,6 +2133,8 @@
                 : getTask().getWindowConfiguration().hasMovementAnimations();
         if (mToken.okToAnimate()
                 && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
+                && !mWindowFrames.didFrameSizeChange()
+                && !surfaceInsetsChanging()
                 && !isDragResizing()
                 && hasMovementAnimation
                 && !mWinAnimator.mLastHidden
@@ -3590,7 +3635,7 @@
     void fillClientWindowFrames(ClientWindowFrames outFrames) {
         outFrames.frame.set(mWindowFrames.mCompatFrame);
         outFrames.displayFrame.set(mWindowFrames.mDisplayFrame);
-        if (mInvGlobalScale != 1.0f && inSizeCompatMode()) {
+        if (mInvGlobalScale != 1.0f && hasCompatScale()) {
             outFrames.displayFrame.scale(mInvGlobalScale);
         }
 
@@ -3990,7 +4035,7 @@
         proto.write(PENDING_SEAMLESS_ROTATION, mPendingSeamlessRotate != null);
         proto.write(FINISHED_SEAMLESS_ROTATION_FRAME, mFinishSeamlessRotateFrameNumber);
         proto.write(FORCE_SEAMLESS_ROTATION, mForceSeamlesslyRotate);
-        proto.write(IN_SIZE_COMPAT_MODE, inSizeCompatMode());
+        proto.write(HAS_COMPAT_SCALE, hasCompatScale());
         proto.write(GLOBAL_SCALE, mGlobalScale);
         proto.end(token);
     }
@@ -4092,7 +4137,7 @@
         pw.println(prefix + "mHasSurface=" + mHasSurface
                 + " isReadyForDisplay()=" + isReadyForDisplay()
                 + " mWindowRemovalAllowed=" + mWindowRemovalAllowed);
-        if (inSizeCompatMode()) {
+        if (hasCompatScale()) {
             pw.println(prefix + "mCompatFrame=" + mWindowFrames.mCompatFrame.toShortString(sTmpSB));
         }
         if (dumpAll) {
@@ -4215,18 +4260,18 @@
         float x, y;
         int w,h;
 
-        final boolean inSizeCompatMode = inSizeCompatMode();
+        final boolean hasCompatScale = hasCompatScale();
         if ((mAttrs.flags & FLAG_SCALED) != 0) {
             if (mAttrs.width < 0) {
                 w = pw;
-            } else if (inSizeCompatMode) {
+            } else if (hasCompatScale) {
                 w = (int)(mAttrs.width * mGlobalScale + .5f);
             } else {
                 w = mAttrs.width;
             }
             if (mAttrs.height < 0) {
                 h = ph;
-            } else if (inSizeCompatMode) {
+            } else if (hasCompatScale) {
                 h = (int)(mAttrs.height * mGlobalScale + .5f);
             } else {
                 h = mAttrs.height;
@@ -4234,21 +4279,21 @@
         } else {
             if (mAttrs.width == MATCH_PARENT) {
                 w = pw;
-            } else if (inSizeCompatMode) {
+            } else if (hasCompatScale) {
                 w = (int)(mRequestedWidth * mGlobalScale + .5f);
             } else {
                 w = mRequestedWidth;
             }
             if (mAttrs.height == MATCH_PARENT) {
                 h = ph;
-            } else if (inSizeCompatMode) {
+            } else if (hasCompatScale) {
                 h = (int)(mRequestedHeight * mGlobalScale + .5f);
             } else {
                 h = mRequestedHeight;
             }
         }
 
-        if (inSizeCompatMode) {
+        if (hasCompatScale) {
             x = mAttrs.x * mGlobalScale;
             y = mAttrs.y * mGlobalScale;
         } else {
@@ -4276,7 +4321,7 @@
         // We need to make sure we update the CompatFrame as it is used for
         // cropping decisions, etc, on systems where we lack a decor layer.
         windowFrames.mCompatFrame.set(windowFrames.mFrame);
-        if (inSizeCompatMode) {
+        if (hasCompatScale) {
             // See comparable block in computeFrameLw.
             windowFrames.mCompatFrame.scale(mInvGlobalScale);
         }
@@ -4394,7 +4439,7 @@
 
     float translateToWindowX(float x) {
         float winX = x - mWindowFrames.mFrame.left;
-        if (inSizeCompatMode()) {
+        if (hasCompatScale()) {
             winX *= mGlobalScale;
         }
         return winX;
@@ -4402,7 +4447,7 @@
 
     float translateToWindowY(float y) {
         float winY = y - mWindowFrames.mFrame.top;
-        if (inSizeCompatMode()) {
+        if (hasCompatScale()) {
             winY *= mGlobalScale;
         }
         return winY;
@@ -5279,13 +5324,17 @@
         // prior to the rotation.
         if (!mSurfaceAnimator.hasLeash() && mPendingSeamlessRotate == null
                 && !mLastSurfacePosition.equals(mSurfacePosition)) {
-            t.setPosition(mSurfaceControl, mSurfacePosition.x, mSurfacePosition.y);
+            final boolean frameSizeChanged = mWindowFrames.isFrameSizeChangeReported();
+            final boolean surfaceInsetsChanged = surfaceInsetsChanging();
+            final boolean surfaceSizeChanged = frameSizeChanged || surfaceInsetsChanged;
             mLastSurfacePosition.set(mSurfacePosition.x, mSurfacePosition.y);
-            if (surfaceInsetsChanging() && mWinAnimator.hasSurface()) {
+            if (surfaceInsetsChanged) {
                 mLastSurfaceInsets.set(mAttrs.surfaceInsets);
-                t.deferTransactionUntil(mSurfaceControl,
-                        mWinAnimator.mSurfaceController.mSurfaceControl,
-                        getFrameNumber());
+            }
+            if (surfaceSizeChanged) {
+                applyWithNextDraw(mSetSurfacePositionConsumer);
+            } else {
+                mSetSurfacePositionConsumer.accept(t);
             }
         }
     }
@@ -5324,6 +5373,8 @@
         // Expand for surface insets. See WindowState.expandForSurfaceInsets.
         transformSurfaceInsetsPosition(mTmpPoint, mAttrs.surfaceInsets);
         outPoint.offset(-mTmpPoint.x, -mTmpPoint.y);
+
+        outPoint.y += mSurfaceTranslationY;
     }
 
     /**
@@ -5331,7 +5382,7 @@
      * scaled, the insets also need to be scaled for surface position in global coordinate.
      */
     private void transformSurfaceInsetsPosition(Point outPos, Rect surfaceInsets) {
-        if (!inSizeCompatMode()) {
+        if (!hasCompatScale()) {
             outPos.x = surfaceInsets.left;
             outPos.y = surfaceInsets.top;
             return;
@@ -5684,6 +5735,8 @@
             Slog.i(TAG, "finishDrawing of relaunch: " + this + " " + duration + "ms");
             mActivityRecord.mRelaunchStartTime = 0;
         }
+
+        executeDrawHandlers(postDrawTransaction);
         if (!onSyncFinishedDrawing()) {
             return mWinAnimator.finishDrawingLocked(postDrawTransaction);
         }
@@ -5698,6 +5751,7 @@
     }
 
     void immediatelyNotifyBlastSync() {
+        prepareDrawHandlers();
         finishDrawing(null);
         mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
         if (!useBLASTSync()) return;
@@ -5777,4 +5831,75 @@
         outSize.inset(-attrs.surfaceInsets.left, -attrs.surfaceInsets.top,
                 -attrs.surfaceInsets.right, -attrs.surfaceInsets.bottom);
     }
+
+    /**
+     * This method is used to control whether we return the BLAST_SYNC flag
+     * from relayoutWindow calls on this window (triggering the client to redirect
+     * it's next draw in to a transaction). If we have pending draw handlers, we are
+     * looking for the client to sync.
+     *
+     * See {@link WindowState#mPendingDrawHandlers}
+     */
+    @Override
+    boolean useBLASTSync() {
+        return super.useBLASTSync() || (mPendingDrawHandlers.size() != 0);
+    }
+
+    /**
+     * Apply the transaction with the next window redraw. A full relayout/finishDrawing
+     * cycle must occur before completion. This means if you call the function while
+     * "in relayout", the results may be undefined but at all other times the function
+     * should sort of transparently work like this:
+     *    1. Make changes to WM hierarchy (say change app configuration)
+     *    2. Call apply with next draw.
+     *    3. After finishDrawing, our consumer will be passed the Transaction
+     *    containing the buffer, and we can merge in additional operations.
+     * See {@link WindowState#mPendingDrawHandlers}
+     */
+    void applyWithNextDraw(Consumer<SurfaceControl.Transaction> consumer) {
+        mPendingDrawHandlers.add(consumer);
+        requestRedrawForSync();
+
+        mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this,
+            BLAST_TIMEOUT_DURATION);
+    }
+
+    /**
+     * Called from relayout, to indicate the next "finishDrawing" will contain
+     * all changes applied by the time mPendingDrawHandlers was populated.
+     *
+     * See {@link WindowState#mPendingDrawHandlers}
+     */
+    void prepareDrawHandlers() {
+        mReadyDrawHandlers.addAll(mPendingDrawHandlers);
+        mPendingDrawHandlers.clear();
+    }
+
+    /**
+     * Drain the draw handlers, called from finishDrawing()
+     * See {@link WindowState#mPendingDrawHandlers}
+     */
+    boolean executeDrawHandlers(SurfaceControl.Transaction t) {
+        if (t == null) t = mTmpTransaction;
+        boolean hadHandlers = false;
+        for (int i = 0; i < mReadyDrawHandlers.size(); i++) {
+            mReadyDrawHandlers.get(i).accept(t);
+            hadHandlers = true;
+        }
+        mReadyDrawHandlers.clear();
+        mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
+
+        t.apply();
+
+        return hadHandlers;
+    }
+
+    /**
+     * Adds an additional translation offset to be applied when positioning the surface. Used to
+     * correct offsets in specific reparenting situations, e.g. the navigation bar window attached
+     * on the lower split-screen app.
+     */
+    void setSurfaceTranslationY(int translationY) {
+        mSurfaceTranslationY = translationY;
+    }
 }
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 10705af..be06d03 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1783,8 +1783,9 @@
     im->setSystemUiLightsOut(lightsOut);
 }
 
-static jboolean nativeTransferTouchFocus(JNIEnv* env,
-        jclass /* clazz */, jlong ptr, jobject fromChannelTokenObj, jobject toChannelTokenObj) {
+static jboolean nativeTransferTouchFocus(JNIEnv* env, jclass /* clazz */, jlong ptr,
+                                         jobject fromChannelTokenObj, jobject toChannelTokenObj,
+                                         jboolean isDragDrop) {
     if (fromChannelTokenObj == nullptr || toChannelTokenObj == nullptr) {
         return JNI_FALSE;
     }
@@ -1793,8 +1794,8 @@
     sp<IBinder> toChannelToken = ibinderForJavaObject(env, toChannelTokenObj);
 
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
-    if (im->getInputManager()->getDispatcher()->transferTouchFocus(
-            fromChannelToken, toChannelToken)) {
+    if (im->getInputManager()->getDispatcher()->transferTouchFocus(fromChannelToken, toChannelToken,
+                                                                   isDragDrop)) {
         return JNI_TRUE;
     } else {
         return JNI_FALSE;
@@ -2267,7 +2268,7 @@
          (void*)nativeRequestPointerCapture},
         {"nativeSetInputDispatchMode", "(JZZ)V", (void*)nativeSetInputDispatchMode},
         {"nativeSetSystemUiLightsOut", "(JZ)V", (void*)nativeSetSystemUiLightsOut},
-        {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)Z",
+        {"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;Z)Z",
          (void*)nativeTransferTouchFocus},
         {"nativeSetPointerSpeed", "(JI)V", (void*)nativeSetPointerSpeed},
         {"nativeSetShowTouches", "(JZ)V", (void*)nativeSetShowTouches},
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index 6a8f6d4..d43cf3f 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -45,13 +45,6 @@
 }
 
 xsd_config {
-    name: "cec-config",
-    srcs: ["cec-config/cec-config.xsd"],
-    api_dir: "cec-config/schema",
-    package_name: "com.android.server.hdmi.cec.config",
-}
-
-xsd_config {
     name: "device-state-config",
     srcs: ["device-state-config/device-state-config.xsd"],
     api_dir: "device-state-config/schema",
diff --git a/services/core/xsd/cec-config/cec-config.xsd b/services/core/xsd/cec-config/cec-config.xsd
deleted file mode 100644
index b59c93c..0000000
--- a/services/core/xsd/cec-config/cec-config.xsd
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<xs:schema version="2.0"
-           elementFormDefault="qualified"
-           attributeFormDefault="unqualified"
-  xmlns:xs="http://www.w3.org/2001/XMLSchema">
-  <xs:element name="cec-settings">
-    <xs:complexType>
-      <xs:sequence>
-        <xs:element name="setting" type="setting" minOccurs="0" maxOccurs="unbounded"/>
-      </xs:sequence>
-    </xs:complexType>
-  </xs:element>
-  <xs:complexType name="setting">
-    <xs:attribute name="name" type="xs:string"/>
-    <xs:attribute name="value-type" type="xs:string"/>
-    <xs:attribute name="user-configurable" type="xs:boolean"/>
-    <xs:element name="allowed-values" type="value-list" minOccurs="1" maxOccurs="1"/>
-    <xs:element name="default-value" type="value" minOccurs="1" maxOccurs="1"/>
-  </xs:complexType>
-  <xs:complexType name="value-list">
-      <xs:sequence>
-        <xs:element name="value" type="value" minOccurs="1" maxOccurs="unbounded"/>
-      </xs:sequence>
-  </xs:complexType>
-  <xs:complexType name="value">
-    <xs:attribute name="string-value" type="xs:string"/>
-    <xs:attribute name="int-value" type="xs:string"/>
-  </xs:complexType>
-</xs:schema>
diff --git a/services/core/xsd/cec-config/schema/current.txt b/services/core/xsd/cec-config/schema/current.txt
deleted file mode 100644
index 75872d4..0000000
--- a/services/core/xsd/cec-config/schema/current.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-// Signature format: 2.0
-package com.android.server.hdmi.cec.config {
-
-  public class CecSettings {
-    ctor public CecSettings();
-    method public java.util.List<com.android.server.hdmi.cec.config.Setting> getSetting();
-  }
-
-  public class Setting {
-    ctor public Setting();
-    method public com.android.server.hdmi.cec.config.ValueList getAllowedValues();
-    method public com.android.server.hdmi.cec.config.Value getDefaultValue();
-    method public String getName();
-    method public boolean getUserConfigurable();
-    method public String getValueType();
-    method public void setAllowedValues(com.android.server.hdmi.cec.config.ValueList);
-    method public void setDefaultValue(com.android.server.hdmi.cec.config.Value);
-    method public void setName(String);
-    method public void setUserConfigurable(boolean);
-    method public void setValueType(String);
-  }
-
-  public class Value {
-    ctor public Value();
-    method public String getIntValue();
-    method public String getStringValue();
-    method public void setIntValue(String);
-    method public void setStringValue(String);
-  }
-
-  public class ValueList {
-    ctor public ValueList();
-    method public java.util.List<com.android.server.hdmi.cec.config.Value> getValue();
-  }
-
-  public class XmlParser {
-    ctor public XmlParser();
-    method public static com.android.server.hdmi.cec.config.CecSettings read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
-  }
-
-}
-
diff --git a/services/core/xsd/cec-config/schema/last_current.txt b/services/core/xsd/cec-config/schema/last_current.txt
deleted file mode 100644
index e69de29..0000000
--- a/services/core/xsd/cec-config/schema/last_current.txt
+++ /dev/null
diff --git a/services/core/xsd/cec-config/schema/last_removed.txt b/services/core/xsd/cec-config/schema/last_removed.txt
deleted file mode 100644
index e69de29..0000000
--- a/services/core/xsd/cec-config/schema/last_removed.txt
+++ /dev/null
diff --git a/services/core/xsd/cec-config/schema/removed.txt b/services/core/xsd/cec-config/schema/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/services/core/xsd/cec-config/schema/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1590ef1..2855c70 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1588,7 +1588,7 @@
             CryptoTestHelper.runAndLogSelfTest();
         }
 
-        public String[] getPersonalAppsForSuspension(int userId) {
+        public String[] getPersonalAppsForSuspension(@UserIdInt int userId) {
             return PersonalAppsSuspensionHelper.forUser(mContext, userId)
                     .getPersonalAppsForSuspension();
         }
@@ -2969,7 +2969,7 @@
 
     private class DpmsUpgradeDataProvider implements PolicyUpgraderDataProvider {
         @Override
-        public boolean isUserDeviceOwner(int userId, ComponentName who) {
+        public boolean isDeviceOwner(int userId, ComponentName who) {
             return mOwners.isDeviceOwnerUserId(userId)
                     && mOwners.getDeviceOwnerComponent().equals(who);
         }
@@ -2999,14 +2999,19 @@
             return component -> findAdmin(component, userId, /* throwForMissingPermission= */
                     false);
         }
+
+        @Override
+        public int[] getUsersForUpgrade() {
+            List<UserInfo> allUsers = mUserManager.getUsers();
+            return allUsers.stream().mapToInt(u -> u.id).toArray();
+        }
     }
 
     private void performPolicyVersionUpgrade() {
-        List<UserInfo> allUsers = mUserManager.getUsers();
         PolicyVersionUpgrader upgrader = new PolicyVersionUpgrader(
                 new DpmsUpgradeDataProvider());
 
-        upgrader.upgradePolicy(allUsers.stream().mapToInt(u -> u.id).toArray(), DPMS_VERSION);
+        upgrader.upgradePolicy(DPMS_VERSION);
     }
 
     private void revertTransferOwnershipIfNecessaryLocked() {
@@ -10615,6 +10620,30 @@
     }
 
     @Override
+    public List<String> listPolicyExemptApps() {
+        Preconditions.checkCallAuthorization(
+                hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
+
+        // TODO(b/181238156): decide whether it should only list the apps set by the resources,
+        // or also the "critical" apps defined by PersonalAppsSuspensionHelper (like SMS app).
+        // If it's the latter, refactor PersonalAppsSuspensionHelper so it (or a superclass) takes
+        // the resources on constructor.
+        String[] core = mContext.getResources().getStringArray(R.array.policy_exempt_apps);
+        String[] vendor = mContext.getResources().getStringArray(R.array.vendor_policy_exempt_apps);
+
+        int size = core.length + vendor.length;
+        Set<String> apps = new ArraySet<>(size);
+        for (String app : core) {
+            apps.add(app);
+        }
+        for (String app : vendor) {
+            apps.add(app);
+        }
+
+        return new ArrayList<>(apps);
+    }
+
+    @Override
     public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner,
             boolean parent) {
         Objects.requireNonNull(who, "ComponentName is null");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 5484a14..8e31029 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -21,6 +21,7 @@
 import com.android.server.devicepolicy.Owners.OwnerDto;
 
 import java.io.PrintWriter;
+import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
 
@@ -30,6 +31,7 @@
     private static final String CMD_IS_SAFE_OPERATION_BY_REASON = "is-operation-safe-by-reason";
     private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe";
     private static final String CMD_LIST_OWNERS = "list-owners";
+    private static final String CMD_LIST_POLICY_EXEMPT_APPS = "list-policy-exempt-apps";
 
     private final DevicePolicyManagerService mService;
 
@@ -60,6 +62,8 @@
                     return runSetSafeOperation(pw);
                 case CMD_LIST_OWNERS:
                     return runListOwners(pw);
+                case CMD_LIST_POLICY_EXEMPT_APPS:
+                    return runListPolicyExemptApps(pw);
                 default:
                     return onInvalidCommand(pw, cmd);
             }
@@ -88,6 +92,8 @@
                 + " \n\n");
         pw.printf("  %s\n", CMD_LIST_OWNERS);
         pw.printf("    Lists the device / profile owners per user \n\n");
+        pw.printf("  %s\n", CMD_LIST_POLICY_EXEMPT_APPS);
+        pw.printf("    Lists the apps that are exempt from policies\n\n");
     }
 
     private int runIsSafeOperation(PrintWriter pw) {
@@ -119,18 +125,20 @@
         return 0;
     }
 
-    private int runListOwners(PrintWriter pw) {
-        List<OwnerDto> owners = mService.listAllOwners();
-        if (owners.isEmpty()) {
-            pw.println("none");
+    private int printAndGetSize(PrintWriter pw, Collection<?> collection, String nameOnSingular) {
+        if (collection.isEmpty()) {
+            pw.printf("no %ss\n", nameOnSingular);
             return 0;
         }
-        int size = owners.size();
-        if (size == 1) {
-            pw.println("1 owner:");
-        } else {
-            pw.printf("%d owners:\n", size);
-        }
+        int size = collection.size();
+        pw.printf("%d %s%s:\n", size, nameOnSingular, (size == 1 ? "" : "s"));
+        return size;
+    }
+
+    private int runListOwners(PrintWriter pw) {
+        List<OwnerDto> owners = mService.listAllOwners();
+        int size = printAndGetSize(pw, owners, "owner");
+        if (size == 0) return 0;
 
         for (int i = 0; i < size; i++) {
             OwnerDto owner = owners.get(i);
@@ -150,4 +158,17 @@
         return 0;
     }
 
+
+    private int runListPolicyExemptApps(PrintWriter pw) {
+        List<String> apps = mService.listPolicyExemptApps();
+        int size = printAndGetSize(pw, apps, "policy exempt app");
+
+        if (size == 0) return 0;
+
+        for (int i = 0; i < size; i++) {
+            String app = apps.get(i);
+            pw.printf("  %d: %s\n", i, app);
+        }
+        return 0;
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java
index c20d0f5..19a7659 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyUpgraderDataProvider.java
@@ -33,7 +33,7 @@
      * Returns true if the provided {@code userId} is a device owner. May affect some policy
      * defaults.
      */
-    boolean isUserDeviceOwner(int userId, ComponentName who);
+    boolean isDeviceOwner(int userId, ComponentName who);
 
     /**
      * Returns true if the storage manager indicates file-based encryption is enabled.
@@ -60,4 +60,9 @@
      * user.
      */
     Function<ComponentName, DeviceAdminInfo> getAdminInfoSupplier(int userId);
+
+    /**
+     * Returns the users to upgrade.
+     */
+    int[] getUsersForUpgrade();
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
index 2ab4b66..6bc7ba6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
@@ -62,7 +62,7 @@
      *                 managed profile user IDs.
      * @param dpmsVersion The version to upgrade to.
      */
-    public void upgradePolicy(int[] allUsers, int dpmsVersion) {
+    public void upgradePolicy(int dpmsVersion) {
         int oldVersion = readVersion();
         if (oldVersion >= dpmsVersion) {
             Slog.i(LOG_TAG, String.format("Current version %d, latest version %d, not upgrading.",
@@ -70,6 +70,8 @@
             return;
         }
 
+        final int[] allUsers = mProvider.getUsersForUpgrade();
+
         //NOTE: The current version is provided in case the XML file format changes in a
         // non-backwards-compatible way, so that DeviceAdminData could load it with
         // old tags, for example.
@@ -94,7 +96,7 @@
                     continue;
                 }
                 for (ActiveAdmin admin : userData.mAdminList) {
-                    if (mProvider.isUserDeviceOwner(userId, admin.info.getComponent())) {
+                    if (mProvider.isDeviceOwner(userId, admin.info.getComponent())) {
                         Slog.i(LOG_TAG, String.format(
                                 "Marking Device Owner in user %d for permission grant ", userId));
                         admin.mAdminCanGrantSensorsPermissions = true;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 1fcc284..c38d0b3 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -74,6 +74,13 @@
 
     // If DL was up and not crashing for 10mins, we consider it healthy and reset all delays.
     static constexpr auto healthyDataLoaderUptime = 10min;
+
+    // For healthy DLs, we'll retry every ~5secs for ~10min
+    static constexpr auto bindRetryInterval = 5s;
+    static constexpr auto bindGracePeriod = 10min;
+
+    static constexpr auto bindingTimeout = 1min;
+
     // 10s, 100s (~2min), 1000s (~15min), 10000s (~3hrs)
     static constexpr auto minBindDelay = 10s;
     static constexpr auto maxBindDelay = 10000s;
@@ -293,6 +300,7 @@
         mTimedQueue(sm.getTimedQueue()),
         mProgressUpdateJobQueue(sm.getProgressUpdateJobQueue()),
         mFs(sm.getFs()),
+        mClock(sm.getClock()),
         mIncrementalDir(rootDir) {
     CHECK(mVold) << "Vold service is unavailable";
     CHECK(mDataLoaderManager) << "DataLoaderManagerService is unavailable";
@@ -302,6 +310,7 @@
     CHECK(mTimedQueue) << "TimedQueue is unavailable";
     CHECK(mProgressUpdateJobQueue) << "mProgressUpdateJobQueue is unavailable";
     CHECK(mFs) << "Fs is unavailable";
+    CHECK(mClock) << "Clock is unavailable";
 
     mJobQueue.reserve(16);
     mJobProcessor = std::thread([this]() {
@@ -2241,17 +2250,44 @@
                << status << " (current " << mCurrentStatus << ")";
 }
 
-Milliseconds IncrementalService::DataLoaderStub::updateBindDelay() {
+std::optional<Milliseconds> IncrementalService::DataLoaderStub::needToBind() {
     std::unique_lock lock(mMutex);
+
+    const auto now = mService.mClock->now();
+    const bool healthy = (mPreviousBindDelay == 0ms);
+
+    if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_BINDING &&
+        now - mCurrentStatusTs <= Constants::bindingTimeout) {
+        LOG(INFO) << "Binding still in progress. "
+                  << (healthy ? "The DL is healthy/freshly bound, ok to retry for a few times."
+                              : "Already unhealthy, don't do anything.");
+        // Binding still in progress.
+        if (!healthy) {
+            // Already unhealthy, don't do anything.
+            return {};
+        }
+        // The DL is healthy/freshly bound, ok to retry for a few times.
+        if (now - mPreviousBindTs <= Constants::bindGracePeriod) {
+            // Still within grace period.
+            if (now - mCurrentStatusTs >= Constants::bindRetryInterval) {
+                // Retry interval passed, retrying.
+                mCurrentStatusTs = now;
+                mPreviousBindDelay = 0ms;
+                return 0ms;
+            }
+            return {};
+        }
+        // fallthrough, mark as unhealthy, and retry with delay
+    }
+
     const auto previousBindTs = mPreviousBindTs;
-    const auto now = Clock::now();
     mPreviousBindTs = now;
 
     const auto nonCrashingInterval = std::max(castToMs(now - previousBindTs), 100ms);
     if (previousBindTs.time_since_epoch() == Clock::duration::zero() ||
         nonCrashingInterval > Constants::healthyDataLoaderUptime) {
         mPreviousBindDelay = 0ms;
-        return mPreviousBindDelay;
+        return 0ms;
     }
 
     constexpr auto minBindDelayMs = castToMs(Constants::minBindDelay);
@@ -2264,12 +2300,16 @@
     const auto bindDelayJitterRangeMs = bindDelayMs / Constants::bindDelayJitterDivider;
     const auto bindDelayJitterMs = rand() % (bindDelayJitterRangeMs * 2) - bindDelayJitterRangeMs;
     mPreviousBindDelay = std::chrono::milliseconds(bindDelayMs + bindDelayJitterMs);
-
     return mPreviousBindDelay;
 }
 
 bool IncrementalService::DataLoaderStub::bind() {
-    const auto bindDelay = updateBindDelay();
+    const auto maybeBindDelay = needToBind();
+    if (!maybeBindDelay) {
+        LOG(DEBUG) << "Skipping bind to " << mParams.packageName << " because of pending bind.";
+        return true;
+    }
+    const auto bindDelay = *maybeBindDelay;
     if (bindDelay > 1s) {
         LOG(INFO) << "Delaying bind to " << mParams.packageName << " by "
                   << bindDelay.count() / 1000 << "s";
@@ -2279,7 +2319,21 @@
     auto status = mService.mDataLoaderManager->bindToDataLoader(id(), mParams, bindDelay.count(),
                                                                 this, &result);
     if (!status.isOk() || !result) {
-        LOG(ERROR) << "Failed to bind a data loader for mount " << id();
+        const bool healthy = (bindDelay == 0ms);
+        LOG(ERROR) << "Failed to bind a data loader for mount " << id()
+                   << (healthy ? ", retrying." : "");
+
+        // Internal error, retry for healthy/new DLs.
+        // Let needToBind migrate it to unhealthy after too many retries.
+        if (healthy) {
+            if (mService.addTimedJob(*mService.mTimedQueue, id(), Constants::bindRetryInterval,
+                                     [this]() { fsmStep(); })) {
+                // Mark as binding so that we know it's not the DL's fault.
+                setCurrentStatus(IDataLoaderStatusListener::DATA_LOADER_BINDING);
+                return true;
+            }
+        }
+
         return false;
     }
     return true;
@@ -2339,7 +2393,14 @@
             // Do nothing, this is a reset state.
             break;
         case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: {
-            return destroy();
+            switch (currentStatus) {
+                case IDataLoaderStatusListener::DATA_LOADER_BINDING:
+                    setCurrentStatus(IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
+                    return true;
+                default:
+                    return destroy();
+            }
+            break;
         }
         case IDataLoaderStatusListener::DATA_LOADER_STARTED: {
             switch (currentStatus) {
@@ -2353,6 +2414,7 @@
             switch (currentStatus) {
                 case IDataLoaderStatusListener::DATA_LOADER_DESTROYED:
                 case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE:
+                case IDataLoaderStatusListener::DATA_LOADER_BINDING:
                     return bind();
                 case IDataLoaderStatusListener::DATA_LOADER_BOUND:
                     return create();
@@ -2372,7 +2434,8 @@
                 fromServiceSpecificError(-EINVAL, "onStatusChange came to invalid DataLoaderStub");
     }
     if (id() != mountId) {
-        LOG(ERROR) << "Mount ID mismatch: expected " << id() << ", but got: " << mountId;
+        LOG(ERROR) << "onStatusChanged: mount ID mismatch: expected " << id()
+                   << ", but got: " << mountId;
         return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch.");
     }
     if (newStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) {
@@ -2396,11 +2459,13 @@
         }
 
         oldStatus = mCurrentStatus;
-        mCurrentStatus = newStatus;
         targetStatus = mTargetStatus;
-
         listener = mStatusListener;
 
+        // Change the status.
+        mCurrentStatus = newStatus;
+        mCurrentStatusTs = mService.mClock->now();
+
         if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE ||
             mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) {
             // For unavailable, unbind from DataLoader to ensure proper re-commit.
@@ -2428,7 +2493,8 @@
                                          "reportStreamHealth came to invalid DataLoaderStub");
     }
     if (id() != mountId) {
-        LOG(ERROR) << "Mount ID mismatch: expected " << id() << ", but got: " << mountId;
+        LOG(ERROR) << "reportStreamHealth: mount ID mismatch: expected " << id()
+                   << ", but got: " << mountId;
         return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch.");
     }
     {
@@ -2694,6 +2760,8 @@
 void IncrementalService::DataLoaderStub::onDump(int fd) {
     dprintf(fd, "    dataLoader: {\n");
     dprintf(fd, "      currentStatus: %d\n", mCurrentStatus);
+    dprintf(fd, "      currentStatusTs: %lldmcs\n",
+            (long long)(elapsedMcs(mCurrentStatusTs, Clock::now())));
     dprintf(fd, "      targetStatus: %d\n", mTargetStatus);
     dprintf(fd, "      targetStatusTs: %lldmcs\n",
             (long long)(elapsedMcs(mTargetStatusTs, Clock::now())));
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 14e5a77..4eb5138 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -267,7 +267,10 @@
         BootClockTsUs getOldestTsFromLastPendingReads();
         Milliseconds elapsedMsSinceKernelTs(TimePoint now, BootClockTsUs kernelTsUs);
 
-        Milliseconds updateBindDelay();
+        // If the stub has to bind to the DL.
+        // Returns {} if bind operation is already in progress.
+        // Or bind delay in ms.
+        std::optional<Milliseconds> needToBind();
 
         void registerForPendingReads();
         void unregisterFromPendingReads();
@@ -283,6 +286,7 @@
 
         std::condition_variable mStatusCondition;
         int mCurrentStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
+        TimePoint mCurrentStatusTs = {};
         int mTargetStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
         TimePoint mTargetStatusTs = {};
 
@@ -443,6 +447,7 @@
     const std::unique_ptr<TimedQueueWrapper> mTimedQueue;
     const std::unique_ptr<TimedQueueWrapper> mProgressUpdateJobQueue;
     const std::unique_ptr<FsWrapper> mFs;
+    const std::unique_ptr<ClockWrapper> mClock;
     const std::string mIncrementalDir;
 
     mutable std::mutex mLock;
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index d613289..80f409f 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -329,6 +329,14 @@
     }
 };
 
+class RealClockWrapper final : public ClockWrapper {
+public:
+    RealClockWrapper() = default;
+    ~RealClockWrapper() = default;
+
+    TimePoint now() const final { return Clock::now(); }
+};
+
 RealServiceManager::RealServiceManager(sp<IServiceManager> serviceManager, JNIEnv* env)
       : mServiceManager(std::move(serviceManager)), mJvm(RealJniWrapper::getJvm(env)) {}
 
@@ -388,6 +396,10 @@
     return std::make_unique<RealFsWrapper>();
 }
 
+std::unique_ptr<ClockWrapper> RealServiceManager::getClock() {
+    return std::make_unique<RealClockWrapper>();
+}
+
 static JavaVM* getJavaVm(JNIEnv* env) {
     CHECK(env);
     JavaVM* jvm = nullptr;
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 245bb31..d113f99 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -158,6 +158,12 @@
     virtual void listFilesRecursive(std::string_view directoryPath, FileCallback onFile) const = 0;
 };
 
+class ClockWrapper {
+public:
+    virtual ~ClockWrapper() = default;
+    virtual TimePoint now() const = 0;
+};
+
 class ServiceManagerWrapper {
 public:
     virtual ~ServiceManagerWrapper() = default;
@@ -170,6 +176,7 @@
     virtual std::unique_ptr<TimedQueueWrapper> getTimedQueue() = 0;
     virtual std::unique_ptr<TimedQueueWrapper> getProgressUpdateJobQueue() = 0;
     virtual std::unique_ptr<FsWrapper> getFs() = 0;
+    virtual std::unique_ptr<ClockWrapper> getClock() = 0;
 };
 
 // --- Real stuff ---
@@ -187,6 +194,7 @@
     std::unique_ptr<TimedQueueWrapper> getTimedQueue() final;
     std::unique_ptr<TimedQueueWrapper> getProgressUpdateJobQueue() final;
     std::unique_ptr<FsWrapper> getFs() final;
+    std::unique_ptr<ClockWrapper> getClock() final;
 
 private:
     template <class INTERFACE>
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 5236983..25b34b56 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -248,6 +248,27 @@
         }
         return binder::Status::ok();
     }
+    binder::Status bindToDataLoaderNotOkWithNoDelay(int32_t mountId,
+                                                    const DataLoaderParamsParcel& params,
+                                                    int bindDelayMs,
+                                                    const sp<IDataLoaderStatusListener>& listener,
+                                                    bool* _aidl_return) {
+        CHECK(bindDelayMs == 0) << bindDelayMs;
+        *_aidl_return = false;
+        return binder::Status::ok();
+    }
+    binder::Status bindToDataLoaderBindingWithNoDelay(int32_t mountId,
+                                                      const DataLoaderParamsParcel& params,
+                                                      int bindDelayMs,
+                                                      const sp<IDataLoaderStatusListener>& listener,
+                                                      bool* _aidl_return) {
+        CHECK(bindDelayMs == 0) << bindDelayMs;
+        *_aidl_return = true;
+        if (listener) {
+            listener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_BINDING);
+        }
+        return binder::Status::ok();
+    }
     binder::Status bindToDataLoaderOkWith10sDelay(int32_t mountId,
                                                   const DataLoaderParamsParcel& params,
                                                   int bindDelayMs,
@@ -557,6 +578,21 @@
     }
 };
 
+class MockClockWrapper : public ClockWrapper {
+public:
+    MOCK_CONST_METHOD0(now, TimePoint());
+
+    void start() { ON_CALL(*this, now()).WillByDefault(Invoke(this, &MockClockWrapper::getClock)); }
+    template <class Delta>
+    void advance(Delta delta) {
+        mClock += delta;
+    }
+
+    TimePoint getClock() const { return mClock; }
+
+    TimePoint mClock = Clock::now();
+};
+
 class MockStorageHealthListener : public os::incremental::BnStorageHealthListener {
 public:
     MOCK_METHOD2(onHealthStatus, binder::Status(int32_t storageId, int32_t status));
@@ -594,7 +630,7 @@
                        std::unique_ptr<MockLooperWrapper> looper,
                        std::unique_ptr<MockTimedQueueWrapper> timedQueue,
                        std::unique_ptr<MockTimedQueueWrapper> progressUpdateJobQueue,
-                       std::unique_ptr<MockFsWrapper> fs)
+                       std::unique_ptr<MockFsWrapper> fs, std::unique_ptr<MockClockWrapper> clock)
           : mVold(std::move(vold)),
             mDataLoaderManager(std::move(dataLoaderManager)),
             mIncFs(std::move(incfs)),
@@ -603,7 +639,8 @@
             mLooper(std::move(looper)),
             mTimedQueue(std::move(timedQueue)),
             mProgressUpdateJobQueue(std::move(progressUpdateJobQueue)),
-            mFs(std::move(fs)) {}
+            mFs(std::move(fs)),
+            mClock(std::move(clock)) {}
     std::unique_ptr<VoldServiceWrapper> getVoldService() final { return std::move(mVold); }
     std::unique_ptr<DataLoaderManagerWrapper> getDataLoaderManager() final {
         return std::move(mDataLoaderManager);
@@ -619,6 +656,7 @@
         return std::move(mProgressUpdateJobQueue);
     }
     std::unique_ptr<FsWrapper> getFs() final { return std::move(mFs); }
+    std::unique_ptr<ClockWrapper> getClock() final { return std::move(mClock); }
 
 private:
     std::unique_ptr<MockVoldService> mVold;
@@ -630,6 +668,7 @@
     std::unique_ptr<MockTimedQueueWrapper> mTimedQueue;
     std::unique_ptr<MockTimedQueueWrapper> mProgressUpdateJobQueue;
     std::unique_ptr<MockFsWrapper> mFs;
+    std::unique_ptr<MockClockWrapper> mClock;
 };
 
 // --- IncrementalServiceTest ---
@@ -657,6 +696,8 @@
         mProgressUpdateJobQueue = progressUpdateJobQueue.get();
         auto fs = std::make_unique<NiceMock<MockFsWrapper>>();
         mFs = fs.get();
+        auto clock = std::make_unique<NiceMock<MockClockWrapper>>();
+        mClock = clock.get();
         mIncrementalService = std::make_unique<
                 IncrementalService>(MockServiceManager(std::move(vold),
                                                        std::move(dataloaderManager),
@@ -664,12 +705,13 @@
                                                        std::move(jni), std::move(looper),
                                                        std::move(timedQueue),
                                                        std::move(progressUpdateJobQueue),
-                                                       std::move(fs)),
+                                                       std::move(fs), std::move(clock)),
                                     mRootDir.path);
         mDataLoaderParcel.packageName = "com.test";
         mDataLoaderParcel.arguments = "uri";
         mDataLoaderManager->unbindFromDataLoaderSuccess();
         mIncrementalService->onSystemReady();
+        mClock->start();
         setupSuccess();
     }
 
@@ -724,6 +766,7 @@
     NiceMock<MockTimedQueueWrapper>* mTimedQueue = nullptr;
     NiceMock<MockTimedQueueWrapper>* mProgressUpdateJobQueue = nullptr;
     NiceMock<MockFsWrapper>* mFs = nullptr;
+    NiceMock<MockClockWrapper>* mClock = nullptr;
     NiceMock<MockDataLoader>* mDataLoader = nullptr;
     std::unique_ptr<IncrementalService> mIncrementalService;
     TemporaryDir mRootDir;
@@ -853,6 +896,119 @@
     mDataLoaderManager->setDataLoaderStatusDestroyed();
 }
 
+TEST_F(IncrementalServiceTest, testDataLoaderOnRestart) {
+    mIncFs->waitForPendingReadsSuccess();
+    mIncFs->openMountSuccess();
+
+    constexpr auto bindRetryInterval = 5s;
+
+    EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(10);
+    EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
+    EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(6);
+    EXPECT_CALL(*mDataLoader, start(_)).Times(6);
+    EXPECT_CALL(*mDataLoader, destroy(_)).Times(1);
+    EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
+    EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(2);
+    TemporaryDir tempDir;
+    int storageId =
+            mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel,
+                                               IncrementalService::CreateOptions::CreateNew);
+    ASSERT_GE(storageId, 0);
+
+    // First binds to DataLoader fails... because it's restart.
+    ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+            .WillByDefault(Invoke(mDataLoaderManager,
+                                  &MockDataLoaderManager::bindToDataLoaderNotOkWithNoDelay));
+
+    // Request DL start.
+    mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {});
+
+    // Retry callback present.
+    ASSERT_EQ(storageId, mTimedQueue->mId);
+    ASSERT_EQ(mTimedQueue->mAfter, bindRetryInterval);
+    auto retryCallback = mTimedQueue->mWhat;
+    mTimedQueue->clearJob(storageId);
+
+    // Expecting the same bindToDataLoaderNotOkWithNoDelay call.
+    mClock->advance(5s);
+
+    retryCallback();
+    // Retry callback present.
+    ASSERT_EQ(storageId, mTimedQueue->mId);
+    ASSERT_EQ(mTimedQueue->mAfter, bindRetryInterval);
+    retryCallback = mTimedQueue->mWhat;
+    mTimedQueue->clearJob(storageId);
+
+    // Returning "binding" so that we can retry.
+    ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+            .WillByDefault(Invoke(mDataLoaderManager,
+                                  &MockDataLoaderManager::bindToDataLoaderBindingWithNoDelay));
+
+    // Expecting bindToDataLoaderBindingWithNoDelay call.
+    mClock->advance(5s);
+
+    retryCallback();
+    // No retry callback.
+    ASSERT_EQ(mTimedQueue->mAfter, 0ms);
+    ASSERT_EQ(mTimedQueue->mWhat, nullptr);
+
+    // Should not change the bindToDataLoader call count
+    ASSERT_NE(nullptr, mLooper->mCallback);
+    ASSERT_NE(nullptr, mLooper->mCallbackData);
+    auto looperCb = mLooper->mCallback;
+    auto looperCbData = mLooper->mCallbackData;
+    looperCb(-1, -1, looperCbData);
+
+    // Expecting the same bindToDataLoaderBindingWithNoDelay call.
+    mClock->advance(5s);
+
+    // Use pending reads callback to trigger binding.
+    looperCb(-1, -1, looperCbData);
+
+    // No retry callback.
+    ASSERT_EQ(mTimedQueue->mAfter, 0ms);
+    ASSERT_EQ(mTimedQueue->mWhat, nullptr);
+
+    // Now we are out of 10m "retry" budget, let's finally bind.
+    ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+            .WillByDefault(Invoke(mDataLoaderManager, &MockDataLoaderManager::bindToDataLoaderOk));
+    mClock->advance(11min);
+
+    // Use pending reads callback to trigger binding.
+    looperCb(-1, -1, looperCbData);
+
+    // No retry callback.
+    ASSERT_EQ(mTimedQueue->mAfter, 0ms);
+    ASSERT_EQ(mTimedQueue->mWhat, nullptr);
+
+    // And test the rest of the backoff.
+    // Simulated crash/other connection breakage.
+    ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+            .WillByDefault(Invoke(mDataLoaderManager,
+                                  &MockDataLoaderManager::bindToDataLoaderOkWith10sDelay));
+    mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+    ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+            .WillByDefault(Invoke(mDataLoaderManager,
+                                  &MockDataLoaderManager::bindToDataLoaderOkWith100sDelay));
+    mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+    ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+            .WillByDefault(Invoke(mDataLoaderManager,
+                                  &MockDataLoaderManager::bindToDataLoaderOkWith1000sDelay));
+    mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+    ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+            .WillByDefault(Invoke(mDataLoaderManager,
+                                  &MockDataLoaderManager::bindToDataLoaderOkWith10000sDelay));
+    mDataLoaderManager->setDataLoaderStatusDestroyed();
+
+    ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+            .WillByDefault(Invoke(mDataLoaderManager,
+                                  &MockDataLoaderManager::bindToDataLoaderOkWith10000sDelay));
+    mDataLoaderManager->setDataLoaderStatusDestroyed();
+}
+
 TEST_F(IncrementalServiceTest, testStartDataLoaderCreate) {
     mDataLoader->initializeCreateOkNoStatus();
     EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1);
diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
index 169b85e..b07fe19 100644
--- a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
+++ b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.MANAGE_SMARTSPACE;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
 import static android.content.Context.SMARTSPACE_SERVICE;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -161,11 +162,13 @@
                 Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid()
                         + ", uid=" + Binder.getCallingUid());
             }
-            if (!(mServiceNameResolver.isTemporary(userId)
+            Context ctx = getContext();
+            if (!(ctx.checkCallingPermission(MANAGE_SMARTSPACE) == PERMISSION_GRANTED
+                    || mServiceNameResolver.isTemporary(userId)
                     || mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) {
 
-                String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
-                        + ", uid=" + Binder.getCallingUid();
+                String msg = "Permission Denial: Cannot call " + func + " from pid="
+                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
                 Slog.w(TAG, msg);
                 throw new SecurityException(msg);
             }
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
index f00edcc..fcd6b84 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
@@ -120,6 +120,11 @@
         return this;
     }
 
+    CompatConfigBuilder addEnabledSinceApexChangeWithId(int sdk, long id) {
+        mChanges.add(new CompatChange(id, "", -1, sdk, false, false, "", false));
+        return this;
+    }
+
     CompatConfig build() {
         CompatConfig config = new CompatConfig(mBuildClassifier, mContext);
         config.forceNonDebuggableFinalForTest(false);
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index b6b6932..bd77405 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -86,6 +86,7 @@
         // Assume userdebug/eng non-final build
         when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
         when(mBuildClassifier.isFinalBuild()).thenReturn(false);
+        when(mBuildClassifier.platformTargetSdk()).thenReturn(30);
         ChangeIdStateCache.disable();
         when(mPackageManager.getApplicationInfo(anyString(), anyInt()))
                 .thenThrow(new NameNotFoundException());
@@ -567,6 +568,34 @@
     }
 
     @Test
+    public void testReadApexConfig() throws IOException {
+        String configXml = "<config>"
+                + "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />"
+                + "<compat-change id=\"1235\" name=\"MY_CHANGE2\" disabled=\"true\" />"
+                + "<compat-change id=\"1236\" name=\"MY_CHANGE3\" />"
+                + "<compat-change id=\"1237\" name=\"MY_CHANGE4\" enableSinceTargetSdk=\"31\" />"
+                + "</config>";
+
+        File dir = createTempDir();
+        writeToFile(dir, "platform_compat_config.xml", configXml);
+        CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+        compatConfig.forceNonDebuggableFinalForTest(false);
+
+        compatConfig.initConfigFromLib(dir);
+
+        assertThat(compatConfig.isChangeEnabled(1234L,
+            ApplicationInfoBuilder.create().withTargetSdk(1).build())).isFalse();
+        assertThat(compatConfig.isChangeEnabled(1234L,
+            ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue();
+        assertThat(compatConfig.isChangeEnabled(1235L,
+            ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse();
+        assertThat(compatConfig.isChangeEnabled(1236L,
+            ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue();
+        assertThat(compatConfig.isChangeEnabled(1237L,
+            ApplicationInfoBuilder.create().withTargetSdk(31).build())).isTrue();
+    }
+
+    @Test
     public void testReadConfigMultipleFiles() throws IOException {
         String configXml1 = "<config>"
                 + "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />"
diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
index 0fd6445..57fdcd3 100644
--- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
@@ -22,6 +22,7 @@
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
 import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
+import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -52,6 +53,7 @@
     private static final int TARGET_SDK = 10;
     private static final int TARGET_SDK_BEFORE = 9;
     private static final int TARGET_SDK_AFTER = 11;
+    private static final int PLATFORM_SDK_VERSION = 30;
 
     @Mock
     private PackageManager mPackageManager;
@@ -61,6 +63,7 @@
     private AndroidBuildClassifier debuggableBuild() {
         AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
         when(buildClassifier.isDebuggableBuild()).thenReturn(true);
+        when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
         return buildClassifier;
     }
 
@@ -68,6 +71,7 @@
         AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
         when(buildClassifier.isDebuggableBuild()).thenReturn(false);
         when(buildClassifier.isFinalBuild()).thenReturn(false);
+        when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
         return buildClassifier;
     }
 
@@ -75,6 +79,7 @@
         AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class);
         when(buildClassifier.isDebuggableBuild()).thenReturn(false);
         when(buildClassifier.isFinalBuild()).thenReturn(true);
+        when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION);
         return buildClassifier;
     }
 
@@ -333,6 +338,26 @@
     }
 
     @Test
+    public void getOverrideAllowedState_targetSdkChangeGreaterThanOsVersion_rejectOverride()
+            throws Exception {
+        final AndroidBuildClassifier buildClassifier = finalBuild();
+        CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+                        .addEnabledSinceApexChangeWithId(PLATFORM_SDK_VERSION + 1, 1).build();
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .withPackageName(PACKAGE_NAME)
+                        .debuggable()
+                        .build());
+
+        OverrideAllowedState stateTargetSdkLessChange =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+        assertThat(stateTargetSdkLessChange).isEqualTo(
+                new OverrideAllowedState(PLATFORM_TOO_OLD, -1,
+                                         PLATFORM_SDK_VERSION));
+    }
+
+    @Test
     public void getOverrideAllowedState_finalBuildEnabledChangeDebugApp_rejectOverride()
             throws Exception {
         CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 799b067..3fc6e99 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -78,11 +78,12 @@
         when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
             .thenThrow(new PackageManager.NameNotFoundException());
         mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
-        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
         // Assume userdebug/eng non-final build
         mCompatConfig.forceNonDebuggableFinalForTest(false);
         when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
         when(mBuildClassifier.isFinalBuild()).thenReturn(false);
+        when(mBuildClassifier.platformTargetSdk()).thenReturn(30);
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
     }
@@ -99,7 +100,7 @@
                 .addLoggingOnlyChangeWithId(7L)
                 .addOverridableChangeWithId(8L)
                 .build();
-        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
         assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly(
                 new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
                 new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
@@ -125,8 +126,9 @@
                 .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
                 .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.R, 6L)
                 .addLoggingOnlyChangeWithId(7L)
+                .addEnableSinceSdkChangeWithId(31, 8L)
                 .build();
-        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
         assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
                 new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false),
                 new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false),
@@ -144,7 +146,7 @@
                 .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.O, 3L)
                 .build();
         mCompatConfig.forceNonDebuggableFinalForTest(true);
-        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
+        mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier);
 
         // Before adding overrides.
         assertThat(mPlatformCompat.isChangeEnabledByPackageName(1, PACKAGE_NAME, 0)).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 89435e9..77a39d8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -107,6 +107,7 @@
 import android.telephony.data.ApnSetting;
 import android.test.MoreAsserts; // TODO(b/171932723): replace by Truth
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.Pair;
 
 import androidx.test.filters.SmallTest;
@@ -154,6 +155,9 @@
 @SmallTest
 @Presubmit
 public class DevicePolicyManagerTest extends DpmTestBase {
+
+    private static final String TAG = DevicePolicyManagerTest.class.getSimpleName();
+
     private static final List<String> OWNER_SETUP_PERMISSIONS = Arrays.asList(
             permission.MANAGE_DEVICE_ADMINS, permission.MANAGE_PROFILE_AND_DEVICE_OWNERS,
             permission.MANAGE_USERS, permission.INTERACT_ACROSS_USERS_FULL);
@@ -7187,6 +7191,47 @@
         assertThat(dpm.isUsbDataSignalingEnabled()).isEqualTo(enabled);
     }
 
+    @Test
+    public void testGetPolicyExemptApps_noPermission() {
+        assertThrows(SecurityException.class, () -> dpm.getPolicyExemptApps());
+    }
+
+    @Test
+    public void testGetPolicyExemptApps_empty() {
+        grantManageDeviceAdmins();
+        mockPolicyExemptApps();
+        mockVendorPolicyExemptApps();
+
+        assertThat(dpm.getPolicyExemptApps()).isEmpty();
+    }
+
+    @Test
+    public void testGetPolicyExemptApps_baseOnly() {
+        grantManageDeviceAdmins();
+        mockPolicyExemptApps("foo");
+        mockVendorPolicyExemptApps();
+
+        assertThat(dpm.getPolicyExemptApps()).containsExactly("foo");
+    }
+
+    @Test
+    public void testGetPolicyExemptApps_vendorOnly() {
+        grantManageDeviceAdmins();
+        mockPolicyExemptApps();
+        mockVendorPolicyExemptApps("bar");
+
+        assertThat(dpm.getPolicyExemptApps()).containsExactly("bar");
+    }
+
+    @Test
+    public void testGetPolicyExemptApps_baseAndVendor() {
+        grantManageDeviceAdmins();
+        mockPolicyExemptApps("4", "23", "15", "42", "8");
+        mockVendorPolicyExemptApps("16", "15", "4");
+
+        assertThat(dpm.getPolicyExemptApps()).containsExactly("4", "8", "15", "16", "23", "42");
+    }
+
     private void setUserUnlocked(int userHandle, boolean unlocked) {
         when(getServices().userManager.isUserUnlocked(eq(userHandle))).thenReturn(unlocked);
     }
@@ -7408,4 +7453,18 @@
         return new StringParceledListSlice(Arrays.asList(s));
     }
 
+    private void grantManageDeviceAdmins() {
+        Log.d(TAG, "Granting " + permission.MANAGE_DEVICE_ADMINS);
+        mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+    }
+
+    private void mockPolicyExemptApps(String... apps) {
+        Log.d(TAG, "Mocking R.array.policy_exempt_apps to return " + Arrays.toString(apps));
+        when(mContext.resources.getStringArray(R.array.policy_exempt_apps)).thenReturn(apps);
+    }
+
+    private void mockVendorPolicyExemptApps(String... apps) {
+        Log.d(TAG, "Mocking R.array.vendor_policy_exempt_apps to return " + Arrays.toString(apps));
+        when(mContext.resources.getStringArray(R.array.vendor_policy_exempt_apps)).thenReturn(apps);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
index 2fe47d3..2fe2f40 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/PolicyVersionUpgraderTest.java
@@ -65,9 +65,10 @@
         Map<Integer, ComponentName> mUserToComponent = new HashMap<>();
         Map<ComponentName, DeviceAdminInfo> mComponentToDeviceAdminInfo = new HashMap<>();
         File mDataDir;
+        int[] mUsers;
 
         @Override
-        public boolean isUserDeviceOwner(int userId, ComponentName who) {
+        public boolean isDeviceOwner(int userId, ComponentName who) {
             return userId == mDeviceOwnerUserId && mDeviceOwnerComponent.equals(who);
         }
 
@@ -105,6 +106,11 @@
         public Function<ComponentName, DeviceAdminInfo> getAdminInfoSupplier(int userId) {
             return componentName -> mComponentToDeviceAdminInfo.get(componentName);
         }
+
+        @Override
+        public int[] getUsersForUpgrade() {
+            return mUsers;
+        }
     }
 
     private final Context mRealTestContext = InstrumentationRegistry.getTargetContext();
@@ -126,16 +132,17 @@
         ActivityInfo activityInfo = createActivityInfo(mFakeAdmin);
         DeviceAdminInfo dai = createDeviceAdminInfo(activityInfo);
         mProvider.mComponentToDeviceAdminInfo.put(mFakeAdmin, dai);
+        mProvider.mUsers = new int[] {0};
     }
 
     @Test
     public void testSameVersionDoesNothing() throws IOException {
-        int[] users = new int[] {0};
         writeVersionToXml(DevicePolicyManagerService.DPMS_VERSION);
-        preparePoliciesFile(users[0]);
-        String oldContents = readPoliciesFile(0);
+        final int userId = mProvider.mUsers[0];
+        preparePoliciesFile(userId);
+        String oldContents = readPoliciesFile(userId);
 
-        mUpgrader.upgradePolicy(users, DevicePolicyManagerService.DPMS_VERSION);
+        mUpgrader.upgradePolicy(DevicePolicyManagerService.DPMS_VERSION);
 
         String newContents = readPoliciesFile(0);
         assertThat(newContents).isEqualTo(oldContents);
@@ -144,18 +151,18 @@
     @Test
     public void testUpgrade0To1RemovesPasswordMetrics() throws IOException, XmlPullParserException {
         final String activePasswordTag = "active-password";
-        int[] users = new int[] {0, 10};
+        mProvider.mUsers = new int[] {0, 10};
         writeVersionToXml(0);
-        for (int userId : users) {
+        for (int userId : mProvider.mUsers) {
             preparePoliciesFile(userId);
         }
         // Validate test set-up.
         assertThat(isTagPresent(readPoliciesFileToStream(0), activePasswordTag)).isTrue();
 
-        mUpgrader.upgradePolicy(users, 1);
+        mUpgrader.upgradePolicy(1);
 
         assertThat(readVersionFromXml()).isGreaterThan(1);
-        for (int user: users) {
+        for (int user: mProvider.mUsers) {
             assertThat(isTagPresent(readPoliciesFileToStream(user), activePasswordTag)).isFalse();
         }
     }
@@ -163,21 +170,22 @@
     @Test
     public void testUpgrade1To2MarksDoForPermissionControl()
             throws IOException, XmlPullParserException {
-        int[] users = new int[] {0, 10};
+        final int ownerUser = 10;
+        mProvider.mUsers = new int[] {0, ownerUser};
         writeVersionToXml(1);
-        for (int userId : users) {
+        for (int userId : mProvider.mUsers) {
             preparePoliciesFile(userId);
         }
-        mProvider.mDeviceOwnerUserId = 10;
+        mProvider.mDeviceOwnerUserId = ownerUser;
         mProvider.mDeviceOwnerComponent = mFakeAdmin;
-        mProvider.mUserToComponent.put(10, mFakeAdmin);
+        mProvider.mUserToComponent.put(ownerUser, mFakeAdmin);
 
-        mUpgrader.upgradePolicy(users, 2);
+        mUpgrader.upgradePolicy(2);
 
         assertThat(readVersionFromXml()).isEqualTo(2);
-        assertThat(getBooleanValueTag(readPoliciesFileToStream(users[0]),
+        assertThat(getBooleanValueTag(readPoliciesFileToStream(mProvider.mUsers[0]),
                 PERMISSIONS_TAG)).isFalse();
-        assertThat(getBooleanValueTag(readPoliciesFileToStream(users[1]),
+        assertThat(getBooleanValueTag(readPoliciesFileToStream(ownerUser),
                 PERMISSIONS_TAG)).isTrue();
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index 843296e..dbb415c 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -68,7 +68,7 @@
      */
     private static class FakeFontFileParser implements UpdatableFontDir.FontFileParser {
         @Override
-        public String getPostScriptName(File file) throws IOException {
+        public String getCanonicalFileName(File file) throws IOException {
             String content = FileUtils.readTextFile(file, 100, "");
             return content.split(",")[0];
         }
@@ -160,10 +160,10 @@
         assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedTimeMillis())
                 .isEqualTo(expectedModifiedDate);
         dirForPreparation.update(Arrays.asList(
-                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
-                newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,4", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE),
                 newAddFontFamilyRequest("<family name='foobar'>"
                         + "  <font>foo.ttf</font>"
                         + "  <font>bar.ttf</font>"
@@ -214,10 +214,10 @@
                 mConfigFile);
         dirForPreparation.loadFontFileMap();
         dirForPreparation.update(Arrays.asList(
-                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
-                newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,4", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE),
                 newAddFontFamilyRequest("<family name='foobar'>"
                         + "  <font>foo.ttf</font>"
                         + "  <font>bar.ttf</font>"
@@ -246,10 +246,10 @@
                 mConfigFile);
         dirForPreparation.loadFontFileMap();
         dirForPreparation.update(Arrays.asList(
-                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
-                newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,4", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE),
                 newAddFontFamilyRequest("<family name='foobar'>"
                         + "  <font>foo.ttf</font>"
                         + "  <font>bar.ttf</font>"
@@ -279,10 +279,10 @@
                 mConfigFile);
         dirForPreparation.loadFontFileMap();
         dirForPreparation.update(Arrays.asList(
-                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,2", GOOD_SIGNATURE),
-                newFontUpdateRequest("foo,3", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,4", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,3", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,4", GOOD_SIGNATURE),
                 newAddFontFamilyRequest("<family name='foobar'>"
                         + "  <font>foo.ttf</font>"
                         + "  <font>bar.ttf</font>"
@@ -332,14 +332,14 @@
                 mConfigFile);
         dirForPreparation.loadFontFileMap();
         dirForPreparation.update(Arrays.asList(
-                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
                 newAddFontFamilyRequest("<family name='foobar'>"
                         + "  <font>foo.ttf</font>"
                         + "</family>")));
         try {
             dirForPreparation.update(Arrays.asList(
-                    newFontUpdateRequest("foo,2", GOOD_SIGNATURE),
-                    newFontUpdateRequest("bar,2", "Invalid signature"),
+                    newFontUpdateRequest("foo.ttf,2", GOOD_SIGNATURE),
+                    newFontUpdateRequest("bar.ttf,2", "Invalid signature"),
                     newAddFontFamilyRequest("<family name='foobar'>"
                             + "  <font>foo.ttf</font>"
                             + "  <font>bar.ttf</font>"
@@ -372,7 +372,7 @@
                 mConfigFile);
         dir.loadFontFileMap();
 
-        dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
+        dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE)));
         assertThat(dir.getFontFileMap()).containsKey("test.ttf");
         assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1);
         File fontFile = dir.getFontFileMap().get("test.ttf");
@@ -390,9 +390,9 @@
                 mConfigFile);
         dir.loadFontFileMap();
 
-        dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
+        dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE)));
         Map<String, File> mapBeforeUpgrade = dir.getFontFileMap();
-        dir.update(Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE)));
+        dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2", GOOD_SIGNATURE)));
         assertThat(dir.getFontFileMap()).containsKey("test.ttf");
         assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(2);
         assertThat(mapBeforeUpgrade).containsKey("test.ttf");
@@ -409,9 +409,10 @@
                 mConfigFile);
         dir.loadFontFileMap();
 
-        dir.update(Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE)));
+        dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,2", GOOD_SIGNATURE)));
         try {
-            dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
+            dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1",
+                    GOOD_SIGNATURE)));
             fail("Expect SystemFontException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
@@ -430,8 +431,8 @@
                 mConfigFile);
         dir.loadFontFileMap();
 
-        dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
-        dir.update(Collections.singletonList(newFontUpdateRequest("bar,2", GOOD_SIGNATURE)));
+        dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE)));
+        dir.update(Collections.singletonList(newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE)));
         assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
         assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
         assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
@@ -448,8 +449,8 @@
         dir.loadFontFileMap();
 
         dir.update(Arrays.asList(
-                newFontUpdateRequest("foo,1", GOOD_SIGNATURE),
-                newFontUpdateRequest("bar,2", GOOD_SIGNATURE)));
+                newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("bar.ttf,2", GOOD_SIGNATURE)));
         assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
         assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(1);
         assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
@@ -467,7 +468,8 @@
 
         try {
             dir.update(
-                    Collections.singletonList(newFontUpdateRequest("test,1", "Invalid signature")));
+                    Collections.singletonList(newFontUpdateRequest("test.ttf,1",
+                            "Invalid signature")));
             fail("Expect SystemFontException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode())
@@ -480,14 +482,15 @@
     public void installFontFile_olderThanPreinstalledFont() throws Exception {
         FakeFontFileParser parser = new FakeFontFileParser();
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
-        FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test,1");
+        FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,1");
         UpdatableFontDir dir = new UpdatableFontDir(
                 mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
                 mConfigFile);
         dir.loadFontFileMap();
 
         try {
-            dir.update(Collections.singletonList(newFontUpdateRequest("test,1", GOOD_SIGNATURE)));
+            dir.update(Collections.singletonList(newFontUpdateRequest("test.ttf,1",
+                    GOOD_SIGNATURE)));
             fail("Expect SystemFontException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode()).isEqualTo(FontManager.RESULT_ERROR_DOWNGRADING);
@@ -500,7 +503,7 @@
         long expectedModifiedDate = 1234567890;
         FakeFontFileParser parser = new FakeFontFileParser();
         FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
-        FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test,1");
+        FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test.ttf,1");
 
         File readonlyDir = new File(mCacheDir, "readonly");
         assertThat(readonlyDir.mkdir()).isTrue();
@@ -519,7 +522,8 @@
 
             try {
                 dir.update(
-                        Collections.singletonList(newFontUpdateRequest("test,2", GOOD_SIGNATURE)));
+                        Collections.singletonList(newFontUpdateRequest("test.ttf,2",
+                                GOOD_SIGNATURE)));
             } catch (FontManagerService.SystemFontException e) {
                 assertThat(e.getErrorCode())
                         .isEqualTo(FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG);
@@ -539,7 +543,7 @@
                 mUpdatableFontFilesDir, mPreinstalledFontDirs,
                 new UpdatableFontDir.FontFileParser() {
                     @Override
-                    public String getPostScriptName(File file) throws IOException {
+                    public String getCanonicalFileName(File file) throws IOException {
                         return null;
                     }
 
@@ -551,7 +555,8 @@
         dir.loadFontFileMap();
 
         try {
-            dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+            dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1",
+                    GOOD_SIGNATURE)));
             fail("Expect SystemFontException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode())
@@ -567,7 +572,7 @@
                 mUpdatableFontFilesDir, mPreinstalledFontDirs,
                 new UpdatableFontDir.FontFileParser() {
                     @Override
-                    public String getPostScriptName(File file) throws IOException {
+                    public String getCanonicalFileName(File file) throws IOException {
                         throw new IOException();
                     }
 
@@ -579,7 +584,8 @@
         dir.loadFontFileMap();
 
         try {
-            dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+            dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1",
+                    GOOD_SIGNATURE)));
             fail("Expect SystemFontException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode())
@@ -615,7 +621,8 @@
         dir.loadFontFileMap();
 
         try {
-            dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+            dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1",
+                    GOOD_SIGNATURE)));
             fail("Expect SystemFontException");
         } catch (FontManagerService.SystemFontException e) {
             assertThat(e.getErrorCode())
@@ -633,11 +640,11 @@
                 mConfigFile);
         dir.loadFontFileMap();
 
-        dir.update(Collections.singletonList(newFontUpdateRequest("foo,1", GOOD_SIGNATURE)));
+        dir.update(Collections.singletonList(newFontUpdateRequest("foo.ttf,1", GOOD_SIGNATURE)));
         try {
             dir.update(Arrays.asList(
-                    newFontUpdateRequest("foo,2", GOOD_SIGNATURE),
-                    newFontUpdateRequest("bar,2", "Invalid signature")));
+                    newFontUpdateRequest("foo.ttf,2", GOOD_SIGNATURE),
+                    newFontUpdateRequest("bar.ttf,2", "Invalid signature")));
             fail("Batch update with invalid signature should fail");
         } catch (FontManagerService.SystemFontException e) {
             // Expected
@@ -657,7 +664,7 @@
         dir.loadFontFileMap();
 
         dir.update(Arrays.asList(
-                newFontUpdateRequest("test,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE),
                 newAddFontFamilyRequest("<family name='test'>"
                         + "  <font>test.ttf</font>"
                         + "</family>")));
@@ -680,7 +687,7 @@
 
         try {
             dir.update(Arrays.asList(
-                    newFontUpdateRequest("test,1", GOOD_SIGNATURE),
+                    newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE),
                     newAddFontFamilyRequest("<family lang='en'>"
                             + "  <font>test.ttf</font>"
                             + "</family>")));
@@ -722,7 +729,7 @@
         assertNamedFamilyExists(dir.getSystemFontConfig(), "monospace");
 
         dir.update(Arrays.asList(
-                newFontUpdateRequest("test,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE),
                 // Updating an existing font family.
                 newAddFontFamilyRequest("<family name='monospace'>"
                         + "  <font>test.ttf</font>"
@@ -755,7 +762,7 @@
         assertThat(firstFontFamily.getName()).isNotEmpty();
 
         dir.update(Arrays.asList(
-                newFontUpdateRequest("test,1", GOOD_SIGNATURE),
+                newFontUpdateRequest("test.ttf,1", GOOD_SIGNATURE),
                 newAddFontFamilyRequest("<family name='" + firstFontFamily.getName() + "'>"
                         + "  <font>test.ttf</font>"
                         + "</family>")));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
index 137bd88..375704e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
@@ -16,183 +16,206 @@
 
 package com.android.server.hdmi;
 
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
 import android.annotation.NonNull;
 import android.content.Context;
-import android.util.Slog;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
 
-import com.android.server.hdmi.cec.config.CecSettings;
-import com.android.server.hdmi.cec.config.XmlParser;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-
-import javax.xml.datatype.DatatypeConfigurationException;
+import com.android.internal.R;
 
 /**
- * Fake class which loads default system configuration with user-configurable
+ * Fake class which stubs default system configuration with user-configurable
  * settings (useful for testing).
  */
 final class FakeHdmiCecConfig extends HdmiCecConfig {
     private static final String TAG = "FakeHdmiCecConfig";
 
-    private static final String SYSTEM_CONFIG_XML =
-            "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                    + "<cec-settings>"
-                    + "  <setting name=\"send_standby_on_sleep\""
-                    + "           value-type=\"string\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value string-value=\"to_tv\" />"
-                    + "      <value string-value=\"broadcast\" />"
-                    + "      <value string-value=\"none\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value string-value=\"to_tv\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"power_state_change_on_active_source_lost\""
-                    + "           value-type=\"string\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value string-value=\"none\" />"
-                    + "      <value string-value=\"standby_now\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value string-value=\"none\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"hdmi_cec_enabled\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"1\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"hdmi_cec_version\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0x05\" />"
-                    + "      <value int-value=\"0x06\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"0x05\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"system_audio_mode_muting\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"1\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"hdmi_cec_enabled\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"1\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"volume_control_enabled\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"1\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"tv_wake_on_one_touch_play\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"1\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"tv_send_standby_on_sleep\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"true\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"1\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"rc_profile_tv\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"false\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0x0\" />"
-                    + "      <value int-value=\"0x2\" />"
-                    + "      <value int-value=\"0x6\" />"
-                    + "      <value int-value=\"0xA\" />"
-                    + "      <value int-value=\"0xE\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"0x0\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"rc_profile_source_handles_root_menu\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"false\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"1\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"rc_profile_source_handles_setup_menu\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"false\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"1\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"rc_profile_source_handles_contents_menu\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"false\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"0\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"rc_profile_source_handles_top_menu\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"false\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"0\" />"
-                    + "  </setting>"
-                    + "  <setting name=\"rc_profile_source_handles_media_context_sensitive_menu\""
-                    + "           value-type=\"int\""
-                    + "           user-configurable=\"false\">"
-                    + "    <allowed-values>"
-                    + "      <value int-value=\"0\" />"
-                    + "      <value int-value=\"1\" />"
-                    + "    </allowed-values>"
-                    + "    <default-value int-value=\"0\" />"
-                    + "  </setting>"
-                    + "</cec-settings>";
-
-    FakeHdmiCecConfig(@NonNull Context context) {
-        super(context, new StorageAdapter(context), parseFromString(SYSTEM_CONFIG_XML), null);
+    public static Context buildContext(Context context) {
+        Context contextSpy = spy(new ContextWrapper(context));
+        doReturn(buildResources(context)).when(contextSpy).getResources();
+        return contextSpy;
     }
 
-    private static CecSettings parseFromString(@NonNull String configXml) {
-        CecSettings config = null;
-        try {
-            config = XmlParser.read(
-                    new ByteArrayInputStream(configXml.getBytes()));
-        } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
-            Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e);
-        }
-        return config;
+    private static Resources buildResources(Context context) {
+        Resources resources = spy(context.getResources());
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecEnabled_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecControlEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecControlEnabled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecControlDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecControlDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecVersion_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecVersion14b_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecVersion14b_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecVersion20_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecHdmiCecVersion20_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSendStandbyOnSleep_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerControlModeTv_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerControlModeTv_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerControlModeBroadcast_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecPowerControlModeBroadcast_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerControlModeNone_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecPowerControlModeNone_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerStateChangeOnActiveSourceLost_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerStateChangeOnActiveSourceLostNone_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecPowerStateChangeOnActiveSourceLostStandbyNow_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSystemAudioModeMuting_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSystemAudioModeMutingEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSystemAudioModeMutingEnabled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecSystemAudioModeMutingDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecSystemAudioModeMutingDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecVolumeControlMode_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecVolumeControlModeEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecVolumeControlModeEnabled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecVolumeControlModeDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecVolumeControlModeDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecTvWakeOnOneTouchPlay_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecTvWakeOnOneTouchPlayEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecTvWakeOnOneTouchPlayEnabled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecTvWakeOnOneTouchPlayDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecTvWakeOnOneTouchPlayDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecTvSendStandbyOnSleep_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecTvSendStandbyOnSleepEnabled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecTvSendStandbyOnSleepEnabled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecTvSendStandbyOnSleepDisabled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecTvSendStandbyOnSleepDisabled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTv_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvNone_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvNone_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvOne_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvOne_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvTwo_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvTwo_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvThree_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvThree_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvFour_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileTvFour_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceRootMenu_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceRootMenuHandled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceRootMenuHandled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceRootMenuNotHandled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceRootMenuNotHandled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceSetupMenu_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceSetupMenuHandled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceSetupMenuHandled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceSetupMenuNotHandled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceSetupMenuNotHandled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceContentsMenu_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceContentsMenuHandled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceContentsMenuHandled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceContentsMenuNotHandled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceContentsMenuNotHandled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceTopMenu_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceTopMenuHandled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceTopMenuHandled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceTopMenuNotHandled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceTopMenuNotHandled_default);
+
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenu_userConfigurable);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_allowed);
+        doReturn(false).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuHandled_default);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_allowed);
+        doReturn(true).when(resources).getBoolean(
+                R.bool.config_cecRcProfileSourceMediaContextSensitiveMenuNotHandled_default);
+
+        return resources;
+    }
+
+    FakeHdmiCecConfig(@NonNull Context context) {
+        super(buildContext(context), new StorageAdapter(context));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
index 798cf85..c834510 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -20,6 +20,7 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -27,6 +28,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.content.res.Resources;
 import android.hardware.hdmi.HdmiControlManager;
 import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
@@ -35,6 +37,8 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.R;
+
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -61,265 +65,118 @@
     @Mock private HdmiCecConfig.StorageAdapter mStorageAdapter;
     @Mock private HdmiCecConfig.SettingChangeListener mSettingChangeListener;
 
+    private void setBooleanResource(int resId, boolean value) {
+        Resources resources = mContext.getResources();
+        doReturn(value).when(resources).getBoolean(resId);
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mContext = InstrumentationRegistry.getTargetContext();
-    }
-
-    @Test
-    public void getAllCecSettings_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThat(hdmiCecConfig.getAllSettings()).isEmpty();
-    }
-
-    @Test
-    public void getAllCecSettings_Empty() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
-        assertThat(hdmiCecConfig.getAllSettings()).isEmpty();
+        mContext = FakeHdmiCecConfig.buildContext(InstrumentationRegistry.getTargetContext());
     }
 
     @Test
     public void getAllCecSettings_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"false\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getAllSettings())
                 .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
-                                 HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
+                    HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                    HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+                    HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                    HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+                    HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+                    HdmiControlManager
+                        .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU);
     }
 
     @Test
-    public void getUserCecSettings_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThat(hdmiCecConfig.getUserSettings()).isEmpty();
-    }
-
-    @Test
-    public void getUserCecSettings_Empty() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
-        assertThat(hdmiCecConfig.getUserSettings()).isEmpty();
-    }
-
-    @Test
-    public void getUserCecSettings_OnlyMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+    public void getUserCecSettings_BasicSanity() {
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getUserSettings())
                 .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
-                                 HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
+                    HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                    HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+                    HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                    HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+                    HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+                    HdmiControlManager
+                        .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU);
     }
 
     @Test
     public void getUserCecSettings_WithOverride() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>",
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"false\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>");
+        setBooleanResource(R.bool.config_cecHdmiCecEnabled_userConfigurable, false);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getUserSettings())
-                .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
-    }
-
-    @Test
-    public void isStringValueType_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.isStringValueType("foo"));
+                .containsExactly(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION,
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+                    HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+                    HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
+                    HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+                    HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_CONTENTS_MENU,
+                    HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_TOP_MENU,
+                    HdmiControlManager
+                        .CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_MEDIA_CONTEXT_SENSITIVE_MENU);
     }
 
     @Test
     public void isStringValueType_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.isStringValueType("foo"));
     }
 
     @Test
     public void isStringValueType_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertTrue(hdmiCecConfig.isStringValueType(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE));
     }
 
     @Test
-    public void isIntValueType_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.isIntValueType("foo"));
-    }
-
-    @Test
     public void isIntValueType_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.isIntValueType("foo"));
     }
 
     @Test
     public void isIntValueType_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertTrue(hdmiCecConfig.isIntValueType(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
     }
 
     @Test
-    public void getAllowedStringValues_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.getAllowedStringValues("foo"));
-    }
-
-    @Test
     public void getAllowedStringValues_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getAllowedStringValues("foo"));
     }
 
     @Test
     public void getAllowedStringValues_InvalidValueType() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getAllowedStringValues(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
@@ -327,21 +184,7 @@
 
     @Test
     public void getAllowedStringValues_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getAllowedStringValues(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
                 .containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV,
@@ -350,41 +193,25 @@
     }
 
     @Test
-    public void getAllowedIntValues_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.getAllowedIntValues("foo"));
+    public void getAllowedStringValues_WithOverride() {
+        setBooleanResource(R.bool.config_cecPowerControlModeNone_allowed, false);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
+        assertThat(hdmiCecConfig.getAllowedStringValues(
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
+                .containsExactly(HdmiControlManager.POWER_CONTROL_MODE_TV,
+                                 HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
     }
 
     @Test
     public void getAllowedIntValues_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getAllowedIntValues("foo"));
     }
 
     @Test
     public void getAllowedIntValues_InvalidValueType() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getAllowedIntValues(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE));
@@ -392,20 +219,7 @@
 
     @Test
     public void getAllowedIntValues_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getAllowedIntValues(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
                 .containsExactly(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED,
@@ -413,62 +227,24 @@
     }
 
     @Test
-    public void getAllowedIntValues_HexValues() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0x00\" />"
-                + "      <value int-value=\"0x01\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"0x01\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+    public void getAllowedIntValues_WithOverride() {
+        setBooleanResource(R.bool.config_cecHdmiCecControlDisabled_allowed, false);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getAllowedIntValues(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
-                .containsExactly(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED,
-                                 HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
-    }
-
-    @Test
-    public void getDefaultStringValue_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.getDefaultStringValue("foo"));
+                .containsExactly(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
     }
 
     @Test
     public void getDefaultStringValue_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getDefaultStringValue("foo"));
     }
 
     @Test
     public void getDefaultStringValue_InvalidValueType() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getDefaultStringValue(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
@@ -476,62 +252,46 @@
 
     @Test
     public void getDefaultStringValue_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getDefaultStringValue(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
                 .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_TV);
     }
 
     @Test
-    public void getDefaultIntValue_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.getDefaultIntValue("foo"));
+    public void getDefaultStringValue_WithOverride() {
+        setBooleanResource(R.bool.config_cecPowerControlModeTv_default, false);
+        setBooleanResource(R.bool.config_cecPowerControlModeBroadcast_default, true);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
+        assertThat(hdmiCecConfig.getDefaultStringValue(
+                    HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
+                .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
+    }
+
+    @Test
+    public void getDefaultStringValue_MultipleDefaults() {
+        setBooleanResource(R.bool.config_cecPowerControlModeBroadcast_default, true);
+        assertThrows(RuntimeException.class,
+                () -> new HdmiCecConfig(mContext, mStorageAdapter));
+    }
+
+    @Test
+    public void getDefaultStringValue_NoDefault() {
+        setBooleanResource(R.bool.config_cecPowerControlModeTv_default, false);
+        assertThrows(RuntimeException.class,
+                () -> new HdmiCecConfig(mContext, mStorageAdapter));
     }
 
     @Test
     public void getDefaultIntValue_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getDefaultIntValue("foo"));
     }
 
     @Test
     public void getDefaultIntValue_InvalidValueType() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getDefaultIntValue(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE));
@@ -539,81 +299,32 @@
 
     @Test
     public void getDefaultIntValue_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getDefaultIntValue(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
                 .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
     }
 
     @Test
-    public void getDefaultIntValue_HexValue() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0x00\" />"
-                + "      <value int-value=\"0x01\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"0x01\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+    public void getDefaultIntValue_WithOverride() {
+        setBooleanResource(R.bool.config_cecHdmiCecControlEnabled_default, false);
+        setBooleanResource(R.bool.config_cecHdmiCecControlDisabled_default, true);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getDefaultIntValue(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
-                .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
-    }
-
-    @Test
-    public void getStringValue_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.getStringValue("foo"));
+                .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
     }
 
     @Test
     public void getStringValue_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getStringValue("foo"));
     }
 
     @Test
     public void getStringValue_InvalidType() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getStringValue(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED));
@@ -625,21 +336,7 @@
                   Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
                   HdmiControlManager.POWER_CONTROL_MODE_TV))
             .thenReturn(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getStringValue(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
                 .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
@@ -652,61 +349,22 @@
                   HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_NONE))
                 .thenReturn(
                         HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"power_state_change_on_active_source_lost\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"none\" />"
-                + "      <value string-value=\"standby_now\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"none\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getStringValue(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST))
                 .isEqualTo(HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
     }
 
     @Test
-    public void getIntValue_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.getIntValue("foo"));
-    }
-
-    @Test
     public void getIntValue_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getIntValue("foo"));
     }
 
     @Test
     public void getIntValue_InvalidType() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.getIntValue(
                     HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE));
@@ -718,45 +376,7 @@
                   Global.HDMI_CONTROL_ENABLED,
                   Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED)))
             .thenReturn(Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED));
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
-        assertThat(hdmiCecConfig.getIntValue(
-                    HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
-                .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
-    }
-
-    @Test
-    public void getIntValue_GlobalSetting_HexValue() {
-        when(mStorageAdapter.retrieveGlobalSetting(
-                  Global.HDMI_CONTROL_ENABLED,
-                  Integer.toHexString(HdmiControlManager.HDMI_CEC_CONTROL_ENABLED)))
-            .thenReturn(Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED));
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0x0\" />"
-                + "      <value int-value=\"0x1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"0x1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getIntValue(
                     HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED))
                 .isEqualTo(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
@@ -768,61 +388,23 @@
                   HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
                   Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_ENABLED)))
                 .thenReturn(Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED));
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"system_audio_mode_muting\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThat(hdmiCecConfig.getIntValue(
                     HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING))
                 .isEqualTo(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED);
     }
 
     @Test
-    public void setStringValue_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.setStringValue("foo", "bar"));
-    }
-
-    @Test
     public void setStringValue_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.setStringValue("foo", "bar"));
     }
 
     @Test
     public void setStringValue_NotConfigurable() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"false\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        setBooleanResource(R.bool.config_cecSendStandbyOnSleep_userConfigurable, false);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.setStringValue(
                         HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
@@ -831,21 +413,7 @@
 
     @Test
     public void setStringValue_InvalidValue() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.setStringValue(
                         HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
@@ -854,21 +422,7 @@
 
     @Test
     public void setStringValue_GlobalSetting_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"send_standby_on_sleep\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"to_tv\" />"
-                + "      <value string-value=\"broadcast\" />"
-                + "      <value string-value=\"none\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"to_tv\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         hdmiCecConfig.setStringValue(HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE,
                                HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
         verify(mStorageAdapter).storeGlobalSetting(
@@ -878,20 +432,7 @@
 
     @Test
     public void setStringValue_SharedPref_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"power_state_change_on_active_source_lost\""
-                + "           value-type=\"string\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value string-value=\"none\" />"
-                + "      <value string-value=\"standby_now\" />"
-                + "    </allowed-values>"
-                + "    <default-value string-value=\"none\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         hdmiCecConfig.setStringValue(
                   HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
                   HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
@@ -901,40 +442,16 @@
     }
 
     @Test
-    public void setIntValue_NoMasterXml() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter, null, null);
-        assertThrows(IllegalArgumentException.class,
-                () -> hdmiCecConfig.setIntValue("foo", 0));
-    }
-
-    @Test
     public void setIntValue_InvalidSetting() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.setIntValue("foo", 0));
     }
 
     @Test
     public void setIntValue_NotConfigurable() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"false\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        setBooleanResource(R.bool.config_cecHdmiCecEnabled_userConfigurable, false);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.setIntValue(
                         HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
@@ -943,20 +460,7 @@
 
     @Test
     public void setIntValue_InvalidValue() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         assertThrows(IllegalArgumentException.class,
                 () -> hdmiCecConfig.setIntValue(
                         HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
@@ -965,43 +469,7 @@
 
     @Test
     public void setIntValue_GlobalSetting_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0\" />"
-                + "      <value int-value=\"1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
-        hdmiCecConfig.setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
-                                  HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
-        verify(mStorageAdapter).storeGlobalSetting(
-                  Global.HDMI_CONTROL_ENABLED,
-                  Integer.toString(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED));
-    }
-
-    @Test
-    public void setIntValue_GlobalSetting_HexValue() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                + "<cec-settings>"
-                + "  <setting name=\"hdmi_cec_enabled\""
-                + "           value-type=\"int\""
-                + "           user-configurable=\"true\">"
-                + "    <allowed-values>"
-                + "      <value int-value=\"0x0\" />"
-                + "      <value int-value=\"0x1\" />"
-                + "    </allowed-values>"
-                + "    <default-value int-value=\"0x1\" />"
-                + "  </setting>"
-                + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         hdmiCecConfig.setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
                                   HdmiControlManager.HDMI_CEC_CONTROL_DISABLED);
         verify(mStorageAdapter).storeGlobalSetting(
@@ -1011,20 +479,7 @@
 
     @Test
     public void setIntValue_SharedPref_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                        + "<cec-settings>"
-                        + "  <setting name=\"system_audio_mode_muting\""
-                        + "           value-type=\"int\""
-                        + "           user-configurable=\"true\">"
-                        + "    <allowed-values>"
-                        + "      <value int-value=\"0\" />"
-                        + "      <value int-value=\"1\" />"
-                        + "    </allowed-values>"
-                        + "    <default-value int-value=\"1\" />"
-                        + "  </setting>"
-                        + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         hdmiCecConfig.setIntValue(
                 HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
                 HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED);
@@ -1035,20 +490,7 @@
 
     @Test
     public void registerChangeListener_SharedPref_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                        + "<cec-settings>"
-                        + "  <setting name=\"system_audio_mode_muting\""
-                        + "           value-type=\"int\""
-                        + "           user-configurable=\"true\">"
-                        + "    <allowed-values>"
-                        + "      <value int-value=\"0\" />"
-                        + "      <value int-value=\"1\" />"
-                        + "    </allowed-values>"
-                        + "    <default-value int-value=\"1\" />"
-                        + "  </setting>"
-                        + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         hdmiCecConfig.registerChangeListener(
                 HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
                 mSettingChangeListener);
@@ -1061,20 +503,7 @@
 
     @Test
     public void removeChangeListener_SharedPref_BasicSanity() {
-        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                mContext, mStorageAdapter,
-                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                        + "<cec-settings>"
-                        + "  <setting name=\"system_audio_mode_muting\""
-                        + "           value-type=\"int\""
-                        + "           user-configurable=\"true\">"
-                        + "    <allowed-values>"
-                        + "      <value int-value=\"0\" />"
-                        + "      <value int-value=\"1\" />"
-                        + "    </allowed-values>"
-                        + "    <default-value int-value=\"1\" />"
-                        + "  </setting>"
-                        + "</cec-settings>", null);
+        HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
         hdmiCecConfig.registerChangeListener(
                 HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
                 mSettingChangeListener);
@@ -1100,20 +529,7 @@
         String originalValue = Global.getString(mContext.getContentResolver(),
                 Global.HDMI_CONTROL_ENABLED);
         try {
-            HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
-                    mContext, mStorageAdapter,
-                    "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
-                            + "<cec-settings>"
-                            + "  <setting name=\"hdmi_cec_enabled\""
-                            + "           value-type=\"int\""
-                            + "           user-configurable=\"true\">"
-                            + "    <allowed-values>"
-                            + "      <value int-value=\"0\" />"
-                            + "      <value int-value=\"1\" />"
-                            + "    </allowed-values>"
-                            + "    <default-value int-value=\"1\" />"
-                            + "  </setting>"
-                            + "</cec-settings>", null);
+            HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
             hdmiCecConfig.registerGlobalSettingsObserver(mTestLooper.getLooper());
             HdmiCecConfig.SettingChangeListener latchUpdateListener =
                     new HdmiCecConfig.SettingChangeListener() {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index 907cf3e..c61635c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -136,7 +136,6 @@
         mPhysicalAddress = 0x2000;
         mNativeWrapper.setPhysicalAddress(mPhysicalAddress);
         mTestLooper.dispatchAll();
-        mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
     }
 
     private OneTouchPlayAction createOneTouchPlayAction(HdmiCecLocalDevicePlayback device,
@@ -147,7 +146,47 @@
     }
 
     @Test
+    public void succeedWithUnknownTvDevice() {
+        HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
+                mHdmiControlService);
+        playbackDevice.init();
+        mLocalDevices.add(playbackDevice);
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mTestLooper.dispatchAll();
+
+        TestActionTimer actionTimer = new TestActionTimer();
+        TestCallback callback = new TestCallback();
+        OneTouchPlayAction action = createOneTouchPlayAction(playbackDevice, actionTimer, callback,
+                false);
+        playbackDevice.addAndStartAction(action);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(
+                playbackDevice.mAddress, mPhysicalAddress);
+        HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(playbackDevice.mAddress,
+                ADDR_TV);
+        HdmiCecMessage giveDevicePowerStatus = HdmiCecMessageBuilder
+                .buildGiveDevicePowerStatus(playbackDevice.mAddress, ADDR_TV);
+
+        assertThat(mNativeWrapper.getResultMessages()).contains(textViewOn);
+        assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+        assertThat(mNativeWrapper.getResultMessages()).contains(giveDevicePowerStatus);
+        mNativeWrapper.clearResultMessages();
+        assertThat(actionTimer.getState()).isEqualTo(STATE_WAITING_FOR_REPORT_POWER_STATUS);
+        HdmiCecMessage reportPowerStatusOn = new HdmiCecMessage(
+                ADDR_TV, playbackDevice.mAddress, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+        action.processCommand(reportPowerStatusOn);
+        mTestLooper.dispatchAll();
+
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(textViewOn);
+        assertThat(mNativeWrapper.getResultMessages()).contains(activeSource);
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveDevicePowerStatus);
+        assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+    }
+
+    @Test
     public void succeedAfterGettingPowerStatusOn_Cec14b() {
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
         HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
                 mHdmiControlService);
         playbackDevice.init();
@@ -187,6 +226,7 @@
 
     @Test
     public void succeedAfterGettingTransientPowerStatus_Cec14b() {
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
         HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
                 mHdmiControlService);
         playbackDevice.init();
@@ -236,6 +276,7 @@
 
     @Test
     public void timeOut_Cec14b() {
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
         HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
                 mHdmiControlService);
         playbackDevice.init();
@@ -276,6 +317,7 @@
 
     @Test
     public void succeedIfPowerStatusOn_Cec20() {
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
         HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
                 mHdmiControlService);
         playbackDevice.init();
@@ -307,6 +349,7 @@
 
     @Test
     public void succeedIfPowerStatusUnknown_Cec20() {
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
         HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
                 mHdmiControlService);
         playbackDevice.init();
@@ -348,6 +391,7 @@
 
     @Test
     public void succeedIfPowerStatusStandby_Cec20() {
+        mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_TV);
         HdmiCecLocalDevicePlayback playbackDevice = new HdmiCecLocalDevicePlayback(
                 mHdmiControlService);
         playbackDevice.init();
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 9a52643..9f428c7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -31,6 +31,7 @@
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.SigningDetails;
 import android.content.pm.Signature;
 import android.content.pm.UserInfo;
 import android.content.pm.parsing.ParsingPackage;
@@ -141,6 +142,10 @@
         return pkg(packageName).addReceiver(receiver);
     }
 
+    private static ParsingPackage pkgWithSharedLibrary(String packageName, String libName) {
+        return pkg(packageName).addLibraryName(libName);
+    }
+
     private static ParsedActivity createActivity(String packageName, IntentFilter[] filters) {
         ParsedActivity activity = new ParsedActivity();
         activity.setPackageName(packageName);
@@ -413,6 +418,118 @@
     }
 
     @Test
+    public void testNoUsesLibrary_Filters() throws Exception {
+        final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
+                new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
+                mMockExecutor);
+
+        simulateAddBasicAndroid(appsFilter);
+        appsFilter.onSystemReady();
+
+        final Signature mockSignature = Mockito.mock(Signature.class);
+        final SigningDetails mockSigningDetails = new SigningDetails(
+                new Signature[]{mockSignature},
+                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2);
+
+        final PackageSetting target = simulateAddPackage(appsFilter,
+                pkgWithSharedLibrary("com.some.package", "com.some.shared_library"),
+                DUMMY_TARGET_APPID,
+                setting -> setting.setSigningDetails(mockSigningDetails)
+                        .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+        final PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package"), DUMMY_CALLING_APPID);
+
+        assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+                SYSTEM_USER));
+    }
+
+    @Test
+    public void testUsesLibrary_DoesntFilter() throws Exception {
+        final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
+                new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
+                mMockExecutor);
+
+        simulateAddBasicAndroid(appsFilter);
+        appsFilter.onSystemReady();
+
+        final Signature mockSignature = Mockito.mock(Signature.class);
+        final SigningDetails mockSigningDetails = new SigningDetails(
+                new Signature[]{mockSignature},
+                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2);
+
+        final PackageSetting target = simulateAddPackage(appsFilter,
+                pkgWithSharedLibrary("com.some.package", "com.some.shared_library"),
+                DUMMY_TARGET_APPID,
+                setting -> setting.setSigningDetails(mockSigningDetails)
+                        .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+        final PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package").addUsesLibrary("com.some.shared_library"),
+                DUMMY_CALLING_APPID);
+
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+                SYSTEM_USER));
+    }
+
+    @Test
+    public void testUsesOptionalLibrary_DoesntFilter() throws Exception {
+        final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
+                new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
+                mMockExecutor);
+
+        simulateAddBasicAndroid(appsFilter);
+        appsFilter.onSystemReady();
+
+        final Signature mockSignature = Mockito.mock(Signature.class);
+        final SigningDetails mockSigningDetails = new SigningDetails(
+                new Signature[]{mockSignature},
+                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2);
+
+        final PackageSetting target = simulateAddPackage(appsFilter,
+                pkgWithSharedLibrary("com.some.package", "com.some.shared_library"),
+                DUMMY_TARGET_APPID,
+                setting -> setting.setSigningDetails(mockSigningDetails)
+                        .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+        final PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package").addUsesOptionalLibrary("com.some.shared_library"),
+                DUMMY_CALLING_APPID);
+
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+                SYSTEM_USER));
+    }
+
+    @Test
+    public void testUsesLibrary_ShareUid_DoesntFilter() throws Exception {
+        final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock,
+                new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null,
+                mMockExecutor);
+
+        simulateAddBasicAndroid(appsFilter);
+        appsFilter.onSystemReady();
+
+        final Signature mockSignature = Mockito.mock(Signature.class);
+        final SigningDetails mockSigningDetails = new SigningDetails(
+                new Signature[]{mockSignature},
+                SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2);
+
+        final PackageSetting target = simulateAddPackage(appsFilter,
+                pkgWithSharedLibrary("com.some.package", "com.some.shared_library"),
+                DUMMY_TARGET_APPID,
+                setting -> setting.setSigningDetails(mockSigningDetails)
+                        .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
+        final PackageSetting calling = simulateAddPackage(appsFilter,
+                pkg("com.some.other.package_a").setSharedUserId("com.some.uid"),
+                DUMMY_CALLING_APPID);
+        simulateAddPackage(appsFilter, pkg("com.some.other.package_b")
+                .setSharedUserId("com.some.uid").addUsesLibrary("com.some.shared_library"),
+                DUMMY_CALLING_APPID);
+
+        // Although package_a doesn't use library, it should be granted visibility. It's because
+        // package_a shares userId with package_b, and package_b uses that shared library.
+        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
+                SYSTEM_USER));
+    }
+
+    @Test
     public void testForceQueryable_SystemDoesntFilter() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
index bad380a..51f627a 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -118,6 +118,11 @@
     }
 
     @Override
+    public MetricsTimeZoneDetectorState generateMetricsState() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void addDumpable(Dumpable dumpable) {
         mDumpables.add(dumpable);
     }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java
new file mode 100644
index 0000000..af954d5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class OrdinalGeneratorTest {
+
+    @Test
+    public void testOrdinal() {
+        OrdinalGenerator<String> ordinalGenerator = new OrdinalGenerator<>();
+        int oneOrd = ordinalGenerator.ordinal("One");
+        int twoOrd = ordinalGenerator.ordinal("Two");
+        assertNotEquals(oneOrd, twoOrd);
+
+        assertEquals(oneOrd, ordinalGenerator.ordinal("One"));
+        assertEquals(twoOrd, ordinalGenerator.ordinal("Two"));
+
+        int threeOrd = ordinalGenerator.ordinal("Three");
+        assertNotEquals(oneOrd, threeOrd);
+        assertNotEquals(twoOrd, threeOrd);
+    }
+
+    @Test
+    public void testOrdinals() {
+        OrdinalGenerator<String> ordinalGenerator = new OrdinalGenerator<>();
+        int[] oneTwoOrds = ordinalGenerator.ordinals(Arrays.asList("One", "Two"));
+        int[] twoThreeOrds = ordinalGenerator.ordinals(Arrays.asList("Two", "Three"));
+        assertEquals(oneTwoOrds[0], ordinalGenerator.ordinal("One"));
+        assertEquals(oneTwoOrds[1], ordinalGenerator.ordinal("Two"));
+        assertEquals(twoThreeOrds[0], ordinalGenerator.ordinal("Two"));
+        assertEquals(twoThreeOrds[1], ordinalGenerator.ordinal("Three"));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index b0341d7..f91ce87 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -925,6 +925,106 @@
         assertTrue(dumpCalled.get());
     }
 
+    @Test
+    public void testGenerateMetricsState() {
+        ConfigurationInternal expectedInternalConfig = CONFIG_INT_AUTO_DISABLED_GEO_DISABLED;
+        String expectedDeviceTimeZoneId = "InitialZoneId";
+
+        Script script = new Script()
+                .initializeConfig(expectedInternalConfig)
+                .initializeTimeZoneSetting(expectedDeviceTimeZoneId);
+
+        assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId, null, null,
+                null, MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL);
+
+        // Make sure the manual suggestion is recorded.
+        ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Zone1");
+        script.simulateManualTimeZoneSuggestion(USER_ID, manualSuggestion,
+                true /* expectedResult */)
+                .verifyTimeZoneChangedAndReset(manualSuggestion);
+        expectedDeviceTimeZoneId = manualSuggestion.getZoneId();
+        assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
+                manualSuggestion, null, null,
+                MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL);
+
+        // With time zone auto detection off, telephony suggestions will be recorded, but geo
+        // suggestions won't out of an abundance of caution around respecting user privacy when
+        // geo detection is off.
+        TelephonyTimeZoneSuggestion telephonySuggestion =
+                createTelephonySuggestion(0 /* slotIndex */, MATCH_TYPE_NETWORK_COUNTRY_ONLY,
+                        QUALITY_SINGLE_ZONE, "Zone2");
+        GeolocationTimeZoneSuggestion geolocationTimeZoneSuggestion =
+                createGeoLocationSuggestion(Arrays.asList("Zone3", "Zone2"));
+        script.simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
+                .verifyTimeZoneNotChanged()
+                .simulateGeolocationTimeZoneSuggestion(geolocationTimeZoneSuggestion)
+                .verifyTimeZoneNotChanged();
+
+        assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
+                manualSuggestion, telephonySuggestion, null /* expectedGeoSuggestion */,
+                MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL);
+
+        // Update the config and confirm that the config metrics state updates also.
+        TimeZoneConfiguration configUpdate =
+                createConfig(true /* autoDetection */, true /* geoDetection */);
+        expectedInternalConfig = new ConfigurationInternal.Builder(expectedInternalConfig)
+                .setAutoDetectionEnabled(true)
+                .setGeoDetectionEnabled(true)
+                .build();
+        script.simulateUpdateConfiguration(USER_ID, configUpdate, true /* expectedResult */)
+                .verifyConfigurationChangedAndReset(expectedInternalConfig)
+                .verifyTimeZoneNotChanged();
+        assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
+                manualSuggestion, telephonySuggestion, null  /* expectedGeoSuggestion */,
+                MetricsTimeZoneDetectorState.DETECTION_MODE_GEO);
+
+        // Now simulate a geo suggestion and confirm it is used and reported in the metrics too.
+        expectedDeviceTimeZoneId = geolocationTimeZoneSuggestion.getZoneIds().get(0);
+        script.simulateGeolocationTimeZoneSuggestion(geolocationTimeZoneSuggestion)
+                .verifyTimeZoneChangedAndReset(expectedDeviceTimeZoneId);
+        assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
+                manualSuggestion, telephonySuggestion, geolocationTimeZoneSuggestion,
+                MetricsTimeZoneDetectorState.DETECTION_MODE_GEO);
+    }
+
+    /**
+     * Asserts that the information returned by {@link
+     * TimeZoneDetectorStrategy#generateMetricsState()} matches expectations.
+     */
+    private void assertMetricsState(
+            ConfigurationInternal expectedInternalConfig,
+            String expectedDeviceTimeZoneId, ManualTimeZoneSuggestion expectedManualSuggestion,
+            TelephonyTimeZoneSuggestion expectedTelephonySuggestion,
+            GeolocationTimeZoneSuggestion expectedGeolocationTimeZoneSuggestion,
+            int expectedDetectionMode) {
+
+        MetricsTimeZoneDetectorState actualState = mTimeZoneDetectorStrategy.generateMetricsState();
+
+        // Check the various feature state values are what we expect.
+        assertFeatureStateMatchesConfig(expectedInternalConfig, actualState, expectedDetectionMode);
+
+        OrdinalGenerator<String> tzIdOrdinalGenerator = new OrdinalGenerator<>();
+        MetricsTimeZoneDetectorState expectedState =
+                MetricsTimeZoneDetectorState.create(
+                        tzIdOrdinalGenerator, expectedInternalConfig, expectedDeviceTimeZoneId,
+                        expectedManualSuggestion, expectedTelephonySuggestion,
+                        expectedGeolocationTimeZoneSuggestion);
+        // Rely on MetricsTimeZoneDetectorState.equals() for time zone ID ordinal comparisons.
+        assertEquals(expectedState, actualState);
+    }
+
+    private static void assertFeatureStateMatchesConfig(ConfigurationInternal config,
+            MetricsTimeZoneDetectorState actualState, int expectedDetectionMode) {
+        assertEquals(config.isTelephonyDetectionSupported(),
+                actualState.isTelephonyDetectionSupported());
+        assertEquals(config.isGeoDetectionSupported(), actualState.isGeoDetectionSupported());
+        assertEquals(config.getAutoDetectionEnabledSetting(),
+                actualState.getAutoDetectionEnabledSetting());
+        assertEquals(config.getGeoDetectionEnabledSetting(),
+                actualState.getGeoDetectionEnabledSetting());
+        assertEquals(expectedDetectionMode, actualState.getDetectionMode());
+    }
+
     private static ManualTimeZoneSuggestion createManualSuggestion(String zoneId) {
         return new ManualTimeZoneSuggestion(zoneId);
     }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
index 3daa7f0..5a100a2 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
@@ -79,15 +79,18 @@
         // For simplicity, the TestThreadingDomain uses the test's main thread. To execute posted
         // runnables, the test must call methods on mTestThreadingDomain otherwise those runnables
         // will never get a chance to execute.
+        LocationTimeZoneProvider.ProviderMetricsLogger stubbedProviderMetricsLogger = stateEnum -> {
+            // Stubbed.
+        };
         mTestThreadingDomain = new TestThreadingDomain();
         mTestCallback = new TestCallback(mTestThreadingDomain);
         mTimeZoneAvailabilityChecker = new FakeTimeZoneIdValidator();
-        mTestPrimaryLocationTimeZoneProvider =
-                new TestLocationTimeZoneProvider(
-                        mTestThreadingDomain, "primary", mTimeZoneAvailabilityChecker);
-        mTestSecondaryLocationTimeZoneProvider =
-                new TestLocationTimeZoneProvider(
-                        mTestThreadingDomain, "secondary", mTimeZoneAvailabilityChecker);
+        mTestPrimaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider(
+                stubbedProviderMetricsLogger, mTestThreadingDomain, "primary",
+                mTimeZoneAvailabilityChecker);
+        mTestSecondaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider(
+                stubbedProviderMetricsLogger, mTestThreadingDomain, "secondary",
+                mTimeZoneAvailabilityChecker);
     }
 
     @Test
@@ -1181,10 +1184,11 @@
         /**
          * Creates the instance.
          */
-        TestLocationTimeZoneProvider(ThreadingDomain threadingDomain,
-                String providerName,
+        TestLocationTimeZoneProvider(ProviderMetricsLogger providerMetricsLogger,
+                ThreadingDomain threadingDomain, String providerName,
                 TimeZoneIdValidator timeZoneIdValidator) {
-            super(threadingDomain, providerName, timeZoneIdValidator);
+            super(providerMetricsLogger, threadingDomain, providerName,
+                    timeZoneIdValidator);
         }
 
         public void setFailDuringInitialization(boolean failInitialization) {
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
index 278fdaf..d13a04e 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
@@ -53,6 +53,7 @@
 import java.time.Duration;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
@@ -79,19 +80,18 @@
     @Test
     public void lifecycle() {
         String providerName = "arbitrary";
-        TestLocationTimeZoneProvider provider =
-                new TestLocationTimeZoneProvider(
-                        mTestThreadingDomain,
-                        providerName,
-                        mTimeZoneAvailabilityChecker);
+        RecordingProviderMetricsLogger providerMetricsLogger = new RecordingProviderMetricsLogger();
+        TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
+                providerMetricsLogger, mTestThreadingDomain, providerName,
+                mTimeZoneAvailabilityChecker);
         mTimeZoneAvailabilityChecker.validIds("Europe/London");
 
         // initialize()
         provider.initialize(mProviderListener);
         provider.assertOnInitializeCalled();
 
-        ProviderState currentState = provider.getCurrentState();
-        assertEquals(PROVIDER_STATE_STOPPED, currentState.stateEnum);
+        ProviderState currentState = assertAndReturnProviderState(
+                provider, providerMetricsLogger, PROVIDER_STATE_STOPPED);
         assertNull(currentState.currentUserConfiguration);
         assertSame(provider, currentState.provider);
         mTestThreadingDomain.assertQueueEmpty();
@@ -105,9 +105,9 @@
 
         provider.assertOnStartCalled(arbitraryInitializationTimeout);
 
-        currentState = provider.getCurrentState();
+        currentState = assertAndReturnProviderState(
+                provider, providerMetricsLogger, PROVIDER_STATE_STARTED_INITIALIZING);
         assertSame(provider, currentState.provider);
-        assertEquals(PROVIDER_STATE_STARTED_INITIALIZING, currentState.stateEnum);
         assertEquals(config, currentState.currentUserConfiguration);
         assertNull(currentState.event);
         // The initialization timeout should be queued.
@@ -129,9 +129,9 @@
         TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(suggestion);
         provider.simulateProviderEventReceived(event);
 
-        currentState = provider.getCurrentState();
+        currentState = assertAndReturnProviderState(
+                provider, providerMetricsLogger, PROVIDER_STATE_STARTED_CERTAIN);
         assertSame(provider, currentState.provider);
-        assertEquals(PROVIDER_STATE_STARTED_CERTAIN, currentState.stateEnum);
         assertEquals(event, currentState.event);
         assertEquals(config, currentState.currentUserConfiguration);
         mTestThreadingDomain.assertQueueEmpty();
@@ -141,9 +141,9 @@
         event = TimeZoneProviderEvent.createUncertainEvent();
         provider.simulateProviderEventReceived(event);
 
-        currentState = provider.getCurrentState();
+        currentState = assertAndReturnProviderState(
+                provider, providerMetricsLogger, PROVIDER_STATE_STARTED_UNCERTAIN);
         assertSame(provider, currentState.provider);
-        assertEquals(PROVIDER_STATE_STARTED_UNCERTAIN, currentState.stateEnum);
         assertEquals(event, currentState.event);
         assertEquals(config, currentState.currentUserConfiguration);
         mTestThreadingDomain.assertQueueEmpty();
@@ -153,7 +153,8 @@
         provider.stopUpdates();
         provider.assertOnStopUpdatesCalled();
 
-        currentState = provider.getCurrentState();
+        currentState = assertAndReturnProviderState(
+                provider, providerMetricsLogger, PROVIDER_STATE_STOPPED);
         assertSame(provider, currentState.provider);
         assertEquals(PROVIDER_STATE_STOPPED, currentState.stateEnum);
         assertNull(currentState.event);
@@ -171,11 +172,10 @@
     @Test
     public void defaultHandleTestCommandImpl() {
         String providerName = "primary";
-        TestLocationTimeZoneProvider provider =
-                new TestLocationTimeZoneProvider(
-                        mTestThreadingDomain,
-                        providerName,
-                        mTimeZoneAvailabilityChecker);
+        StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
+        TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
+                providerMetricsLogger, mTestThreadingDomain, providerName,
+                mTimeZoneAvailabilityChecker);
 
         TestCommand testCommand = TestCommand.createForTests("test", new Bundle());
         AtomicReference<Bundle> resultReference = new AtomicReference<>();
@@ -191,11 +191,10 @@
     @Test
     public void stateRecording() {
         String providerName = "primary";
-        TestLocationTimeZoneProvider provider =
-                new TestLocationTimeZoneProvider(
-                        mTestThreadingDomain,
-                        providerName,
-                        mTimeZoneAvailabilityChecker);
+        StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
+        TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
+                providerMetricsLogger, mTestThreadingDomain, providerName,
+                mTimeZoneAvailabilityChecker);
         provider.setStateChangeRecordingEnabled(true);
         mTimeZoneAvailabilityChecker.validIds("Europe/London");
 
@@ -237,11 +236,10 @@
     @Test
     public void considerSuggestionWithInvalidTimeZoneIdsAsUncertain() {
         String providerName = "primary";
-        TestLocationTimeZoneProvider provider =
-                new TestLocationTimeZoneProvider(
-                        mTestThreadingDomain,
-                        providerName,
-                        mTimeZoneAvailabilityChecker);
+        StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger();
+        TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider(
+                providerMetricsLogger, mTestThreadingDomain, providerName,
+                mTimeZoneAvailabilityChecker);
         provider.setStateChangeRecordingEnabled(true);
         provider.initialize(mProviderListener);
 
@@ -285,6 +283,20 @@
         }
     }
 
+    /**
+     * Returns the provider's state after asserting that the current state matches what is expected.
+     * This also asserts that the metrics logger was informed of the state change.
+     */
+    private static ProviderState assertAndReturnProviderState(
+            TestLocationTimeZoneProvider provider,
+            RecordingProviderMetricsLogger providerMetricsLogger, int expectedStateEnum) {
+        ProviderState currentState = provider.getCurrentState();
+        assertEquals(expectedStateEnum, currentState.stateEnum);
+        providerMetricsLogger.assertChangeLoggedAndRemove(expectedStateEnum);
+        providerMetricsLogger.assertNoMoreLogEntries();
+        return currentState;
+    }
+
     private static class TestLocationTimeZoneProvider extends LocationTimeZoneProvider {
 
         private boolean mOnInitializeCalled;
@@ -294,10 +306,11 @@
         private boolean mOnStopUpdatesCalled;
 
         /** Creates the instance. */
-        TestLocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain,
+        TestLocationTimeZoneProvider(@NonNull ProviderMetricsLogger providerMetricsLogger,
+                @NonNull ThreadingDomain threadingDomain,
                 @NonNull String providerName,
                 @NonNull TimeZoneIdValidator timeZoneIdValidator) {
-            super(threadingDomain, providerName, timeZoneIdValidator);
+            super(providerMetricsLogger, threadingDomain, providerName, timeZoneIdValidator);
         }
 
         @Override
@@ -366,6 +379,36 @@
         public void validIds(String... timeZoneIdss) {
             mValidTimeZoneIds.addAll(asList(timeZoneIdss));
         }
+    }
 
+    private static class StubbedProviderMetricsLogger implements
+            LocationTimeZoneProvider.ProviderMetricsLogger {
+
+        @Override
+        public void onProviderStateChanged(int stateEnum) {
+            // Stubbed
+        }
+    }
+
+    private static class RecordingProviderMetricsLogger implements
+            LocationTimeZoneProvider.ProviderMetricsLogger {
+
+        private LinkedList<Integer> mStates = new LinkedList<>();
+
+        @Override
+        public void onProviderStateChanged(int stateEnum) {
+            mStates.add(stateEnum);
+        }
+
+        public void assertChangeLoggedAndRemove(int expectedLoggedState) {
+            assertEquals("expected loggedState=" + expectedLoggedState
+                    + " but states logged were=" + mStates,
+                    (Integer) expectedLoggedState, mStates.peekFirst());
+            mStates.removeFirst();
+        }
+
+        public void assertNoMoreLogEntries() {
+            assertTrue(mStates.isEmpty());
+        }
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index d8e7582..c19f348 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -159,6 +159,11 @@
         setBooted(mAtm);
     }
 
+    private TestStartingWindowOrganizer registerTestStartingWindowOrganizer() {
+        return new TestStartingWindowOrganizer(mAtm,
+                mSystemServicesTestRule.getPowerManagerWrapper());
+    }
+
     @Test
     public void testStackCleanupOnClearingTask() {
         final ActivityRecord activity = createActivityWith2LevelTask();
@@ -2294,6 +2299,7 @@
 
     @Test
     public void testCreateRemoveStartingWindow() {
+        registerTestStartingWindowOrganizer();
         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
         activity.addStartingWindow(mPackageName,
                 android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
@@ -2307,6 +2313,7 @@
 
     @Test
     public void testAddRemoveRace() {
+        registerTestStartingWindowOrganizer();
         // There was once a race condition between adding and removing starting windows
         final ActivityRecord appToken = new ActivityBuilder(mAtm).setCreateTask(true).build();
         for (int i = 0; i < 1000; i++) {
@@ -2321,6 +2328,7 @@
 
     @Test
     public void testTransferStartingWindow() {
+        registerTestStartingWindowOrganizer();
         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         activity1.addStartingWindow(mPackageName,
@@ -2337,9 +2345,10 @@
 
     @Test
     public void testTransferStartingWindowWhileCreating() {
+        final TestStartingWindowOrganizer organizer = registerTestStartingWindowOrganizer();
         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
-        ((TestWindowManagerPolicy) activity1.mWmService.mPolicy).setRunnableWhenAddingSplashScreen(
+        organizer.setRunnableWhenAddingSplashScreen(
                 () -> {
                     // Surprise, ...! Transfer window in the middle of the creation flow.
                     activity2.addStartingWindow(mPackageName,
@@ -2357,6 +2366,7 @@
 
     @Test
     public void testTransferStartingWindowCanAnimate() {
+        registerTestStartingWindowOrganizer();
         final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         activity1.addStartingWindow(mPackageName,
@@ -2380,6 +2390,7 @@
 
     @Test
     public void testTransferStartingWindowFromFinishingActivity() {
+        registerTestStartingWindowOrganizer();
         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final Task task = activity.getTask();
         activity.addStartingWindow(mPackageName, android.R.style.Theme, null /* compatInfo */,
@@ -2423,6 +2434,7 @@
 
     @Test
     public void testTransferStartingWindowSetFixedRotation() {
+        registerTestStartingWindowOrganizer();
         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final Task task = activity.getTask();
         final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
@@ -2454,6 +2466,7 @@
 
     @Test
     public void testTryTransferStartingWindowFromHiddenAboveToken() {
+        registerTestStartingWindowOrganizer();
         // Add two tasks on top of each other.
         final ActivityRecord activityTop = new ActivityBuilder(mAtm).setCreateTask(true).build();
         final ActivityRecord activityBottom = new ActivityBuilder(mAtm).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 8703c31..7f9e7da 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -154,7 +154,7 @@
         mWindow = createDropTargetWindow("Drag test window", 0);
         doReturn(mWindow).when(mDisplayContent).getTouchableWinAtPointLocked(0, 0);
         when(mWm.mInputManager.transferTouchFocus(any(InputChannel.class),
-                any(InputChannel.class))).thenReturn(true);
+                any(InputChannel.class), any(boolean.class))).thenReturn(true);
 
         mWm.mWindowMap.put(mWindow.mClient.asBinder(), mWindow);
     }
@@ -370,7 +370,7 @@
                     .build();
 
             assertTrue(mWm.mInputManager.transferTouchFocus(new InputChannel(),
-                    new InputChannel()));
+                    new InputChannel(), true /* isDragDrop */));
             mToken = mTarget.performDrag(0, 0, mWindow.mClient, flag, surface, 0, 0, 0, 0, 0, data);
             assertNotNull(mToken);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 9830631..c483ae9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -141,7 +141,7 @@
         assertNull(mProvider.getControlTarget());
 
         // We can have the control and the control target after seamless rotation.
-        mProvider.finishSeamlessRotation(false /* timeout */);
+        mProvider.finishSeamlessRotation();
         mProvider.updateControlForTarget(target, false /* force */);
         assertNotNull(mProvider.getControl(target));
         assertNotNull(mProvider.getControlTarget());
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 c6be987..7a4ad74 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -49,6 +52,7 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
@@ -511,6 +515,8 @@
         final WindowToken navToken = mDefaultDisplay.getDisplayPolicy().getNavigationBar().mToken;
         final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
 
+        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+                mDefaultDisplay.mDisplayId, false);
         verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl());
 
         final WindowContainer parent = navToken.getParent();
@@ -518,6 +524,8 @@
                 mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController();
 
         mController.cleanupAnimation(REORDER_MOVE_TO_TOP);
+        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+                mDefaultDisplay.mDisplayId, true);
         verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
         verify(navBarFadeAnimationController).fadeWindowToken(true);
     }
@@ -532,6 +540,8 @@
         final WindowToken navToken = mDefaultDisplay.getDisplayPolicy().getNavigationBar().mToken;
         final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
 
+        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+                mDefaultDisplay.mDisplayId, false);
         verify(transaction).reparent(navToken.getSurfaceControl(), activity.getSurfaceControl());
 
         final WindowContainer parent = navToken.getParent();
@@ -539,6 +549,51 @@
                 mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController();
 
         mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+                mDefaultDisplay.mDisplayId, true);
+        verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
+        verify(navBarFadeAnimationController, never()).fadeWindowToken(anyBoolean());
+    }
+
+    @Test
+    public void testNotAttachNavigationBar_controlledByFixedRotationAnimation() {
+        setupForShouldAttachNavBarDuringTransition();
+        FixedRotationAnimationController mockController =
+                mock(FixedRotationAnimationController.class);
+        doReturn(mockController).when(mDefaultDisplay).getFixedRotationAnimationController();
+        final ActivityRecord homeActivity = createHomeActivity();
+        initializeRecentsAnimationController(mController, homeActivity);
+        assertFalse(mController.isNavigationBarAttachedToApp());
+    }
+
+    @Test
+    public void testAttachNavBarInSplitScreenMode() {
+        setupForShouldAttachNavBarDuringTransition();
+        final ActivityRecord primary = createActivityRecordWithParentTask(mDefaultDisplay,
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+        final ActivityRecord secondary = createActivityRecordWithParentTask(mDefaultDisplay,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD);
+        final ActivityRecord homeActivity = createHomeActivity();
+        homeActivity.setVisibility(true);
+        initializeRecentsAnimationController(mController, homeActivity);
+
+        WindowState navWindow = mController.getNavigationBarWindow();
+        final WindowToken navToken = navWindow.mToken;
+        final SurfaceControl.Transaction transaction = navToken.getPendingTransaction();
+
+        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+                mDefaultDisplay.mDisplayId, false);
+        verify(navWindow).setSurfaceTranslationY(-secondary.getBounds().top);
+        verify(transaction).reparent(navToken.getSurfaceControl(), secondary.getSurfaceControl());
+        reset(navWindow);
+
+        mController.cleanupAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+        final WindowContainer parent = navToken.getParent();
+        final NavBarFadeAnimationController navBarFadeAnimationController =
+                mDefaultDisplay.getDisplayPolicy().getNavBarFadeAnimationController();
+        verify(mController.mStatusBar).setNavigationBarLumaSamplingEnabled(
+                mDefaultDisplay.mDisplayId, true);
+        verify(navWindow).setSurfaceTranslationY(0);
         verify(transaction).reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
         verify(navBarFadeAnimationController, never()).fadeWindowToken(anyBoolean());
     }
@@ -600,9 +655,10 @@
 
     private void setupForShouldAttachNavBarDuringTransition() {
         mController.mShouldAttachNavBarToAppDuringTransition = true;
-        final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar");
+        final WindowState navBar = spy(createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar"));
         mDefaultDisplay.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
         mWm.setRecentsAnimationController(mController);
+        doReturn(navBar).when(mController).getNavigationBarWindow();
         final NavBarFadeAnimationController mockNavBarFadeAnimationController =
                 mock(NavBarFadeAnimationController.class);
         final DisplayPolicy displayPolicy = spy(mDefaultDisplay.getDisplayPolicy());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 75226b7..8bc4ced 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -34,7 +34,6 @@
 import android.platform.test.annotations.Presubmit;
 import android.view.InputChannel;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
@@ -63,7 +62,8 @@
 
         when(mWm.mInputManager.transferTouchFocus(
                 any(InputChannel.class),
-                any(InputChannel.class))).thenReturn(true);
+                any(InputChannel.class),
+                any(boolean.class))).thenReturn(true);
 
         mWindow = createWindow(null, TYPE_BASE_APPLICATION, "window");
         mWindow.getTask().setResizeMode(RESIZE_MODE_RESIZEABLE);
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 86d8eee..7822a85 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -119,7 +119,7 @@
             mRunnableWhenAddingSplashScreen.run();
             mRunnableWhenAddingSplashScreen = null;
         }
-        return () -> {
+        return (a) -> {
             synchronized (wm.mGlobalLock) {
                 activity.removeChild(window);
                 activity.mStartingWindow = null;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 2c2c09a..01c503e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -547,7 +547,8 @@
             }
 
             @Override
-            public void removeStartingWindow(int taskId) { }
+            public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+                    boolean playRevealAnimation) { }
 
             @Override
             public void copySplashScreenView(int taskId) { }
@@ -614,7 +615,8 @@
 
             }
             @Override
-            public void removeStartingWindow(int taskId) { }
+            public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+                    boolean playRevealAnimation) { }
             @Override
             public void copySplashScreenView(int taskId) { }
             @Override
@@ -688,7 +690,8 @@
             }
 
             @Override
-            public void removeStartingWindow(int taskId) { }
+            public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+                    boolean playRevealAnimation) { }
             @Override
             public void copySplashScreenView(int taskId) { }
             @Override
@@ -832,7 +835,8 @@
         @Override
         public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { }
         @Override
-        public void removeStartingWindow(int taskId) { }
+        public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+                boolean playRevealAnimation) { }
         @Override
         public void copySplashScreenView(int taskId) { }
         @Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 51aec65..5b5b1da 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -521,7 +521,7 @@
         matrix.mapPoints(curSurfacePos);
         verify(t).setPosition(eq(app.mSurfaceControl), eq(curSurfacePos[0]), eq(curSurfacePos[1]));
 
-        app.finishSeamlessRotation(false /* timeout */);
+        app.finishSeamlessRotation(t);
         assertFalse(app.mSeamlesslyRotated);
         assertNull(app.mPendingSeamlessRotate);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 827ff6c..4a7784c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -36,6 +36,7 @@
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -50,6 +51,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
 
@@ -67,6 +69,7 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.Build;
 import android.os.Bundle;
@@ -74,6 +77,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
+import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.IDisplayWindowInsetsController;
@@ -100,6 +104,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.util.HashMap;
 
 /** Common base class for window manager unit test classes. */
 class WindowTestsBase extends SystemServiceTestsBase {
@@ -1123,6 +1128,88 @@
         }
     }
 
+    static class TestStartingWindowOrganizer extends ITaskOrganizer.Stub {
+        private final ActivityTaskManagerService mAtm;
+        private final WindowManagerService mWMService;
+        private final WindowState.PowerManagerWrapper mPowerManagerWrapper;
+
+        private Runnable mRunnableWhenAddingSplashScreen;
+        private final SparseArray<IBinder> mTaskAppMap = new SparseArray<>();
+        private final HashMap<IBinder, WindowState> mAppWindowMap = new HashMap<>();
+
+        TestStartingWindowOrganizer(ActivityTaskManagerService service,
+                WindowState.PowerManagerWrapper powerManagerWrapper) {
+            mAtm = service;
+            mWMService = mAtm.mWindowManager;
+            mPowerManagerWrapper = powerManagerWrapper;
+            if (DEBUG_ENABLE_SHELL_DRAWER) {
+                mAtm.mTaskOrganizerController.setDeferTaskOrgCallbacksConsumer(Runnable::run);
+                mAtm.mTaskOrganizerController.registerTaskOrganizer(this);
+            }
+        }
+
+        void setRunnableWhenAddingSplashScreen(Runnable r) {
+            if (DEBUG_ENABLE_SHELL_DRAWER) {
+                mRunnableWhenAddingSplashScreen = r;
+            } else {
+                ((TestWindowManagerPolicy) mWMService.mPolicy).setRunnableWhenAddingSplashScreen(r);
+            }
+        }
+
+        @Override
+        public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
+            synchronized (mWMService.mGlobalLock) {
+                final ActivityRecord activity = mWMService.mRoot.getActivityRecord(
+                        appToken);
+                IWindow iWindow = mock(IWindow.class);
+                doReturn(mock(IBinder.class)).when(iWindow).asBinder();
+                final WindowState window = WindowTestsBase.createWindow(null,
+                        TYPE_APPLICATION_STARTING, activity,
+                        "Starting window", 0 /* ownerId */, 0 /* userId*/,
+                        false /* internalWindows */, mWMService, mock(Session.class),
+                        iWindow,
+                        mPowerManagerWrapper);
+                activity.mStartingWindow = window;
+                mAppWindowMap.put(appToken, window);
+                mTaskAppMap.put(info.taskInfo.taskId, appToken);
+            }
+            if (mRunnableWhenAddingSplashScreen != null) {
+                mRunnableWhenAddingSplashScreen.run();
+                mRunnableWhenAddingSplashScreen = null;
+            }
+        }
+        @Override
+        public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+                boolean playRevealAnimation) {
+            synchronized (mWMService.mGlobalLock) {
+                final IBinder appToken = mTaskAppMap.get(taskId);
+                if (appToken != null) {
+                    mTaskAppMap.remove(taskId);
+                    final ActivityRecord activity = mWMService.mRoot.getActivityRecord(
+                            appToken);
+                    WindowState win = mAppWindowMap.remove(appToken);
+                    activity.removeChild(win);
+                    activity.mStartingWindow = null;
+                }
+            }
+        }
+        @Override
+        public void copySplashScreenView(int taskId) {
+        }
+        @Override
+        public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) {
+        }
+        @Override
+        public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
+        }
+        @Override
+        public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
+        }
+        @Override
+        public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
+        }
+    }
+
     static class TestSplitOrganizer extends ITaskOrganizer.Stub {
         final ActivityTaskManagerService mService;
         Task mPrimary;
@@ -1161,7 +1248,8 @@
         public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
         }
         @Override
-        public void removeStartingWindow(int taskId) {
+        public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
+                boolean playRevealAnimation) {
         }
         @Override
         public void copySplashScreenView(int taskId) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index c82ba99..d967891 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -470,7 +470,7 @@
                 mWm, mockRunner, null, displayId);
         spyOn(controller);
         controller.mShouldAttachNavBarToAppDuringTransition = true;
-        doReturn(mNavBarWindow.mToken).when(controller).getNavigationBarWindowToken();
+        doReturn(mNavBarWindow).when(controller).getNavigationBarWindow();
         mWm.setRecentsAnimationController(controller);
 
         // set ime visible
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index ed71d17..e089995a 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -17,6 +17,7 @@
 package com.android.server.voiceinteraction;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -25,8 +26,10 @@
 import android.media.AudioAttributes;
 import android.media.AudioRecord;
 import android.media.MediaRecorder;
+import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.SharedMemory;
 import android.service.voice.AlwaysOnHotwordDetector;
 import android.service.voice.HotwordDetectionService;
 import android.service.voice.IDspHotwordDetectionCallback;
@@ -74,7 +77,8 @@
     boolean mBound;
 
     HotwordDetectionConnection(Object lock, Context context, ComponentName serviceName,
-            int userId, boolean bindInstantServiceAllowed) {
+            int userId, boolean bindInstantServiceAllowed, @Nullable Bundle options,
+            @Nullable SharedMemory sharedMemory) {
         mLock = lock;
         mContext = context;
         mDetectionComponentName = serviceName;
@@ -90,6 +94,14 @@
                     boolean connected) {
                 synchronized (mLock) {
                     mBound = connected;
+                    if (connected) {
+                        try {
+                            service.setConfig(options, sharedMemory);
+                        } catch (RemoteException e) {
+                            // TODO: (b/181842909) Report an error to voice interactor
+                            Slog.w(TAG, "Failed to setConfig for HotwordDetectionService", e);
+                        }
+                    }
                 }
             }
 
@@ -117,6 +129,11 @@
         }
     }
 
+    void setConfigLocked(Bundle options, SharedMemory sharedMemory) {
+        mRemoteHotwordDetectionService.run(
+                service -> service.setConfig(options, sharedMemory));
+    }
+
     private void detectFromDspSource(SoundTrigger.KeyphraseRecognitionEvent recognitionEvent,
             IHotwordRecognitionStatusCallback externalCallback) {
         if (DEBUG) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index dce63eb..2626bfd 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -60,6 +60,7 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.SharedMemory;
 import android.os.ShellCallback;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -982,23 +983,46 @@
         }
 
         @Override
-        public int setHotwordDetectionConfig(Bundle options) {
+        public void setHotwordDetectionServiceConfig(@Nullable Bundle options,
+                @Nullable SharedMemory sharedMemory) {
             synchronized (this) {
                 enforceIsCurrentVoiceInteractionService();
 
                 if (mImpl == null) {
                     Slog.w(TAG,
-                            "setHotwordDetectionConfig without running voice interaction service");
-                    return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
+                            "setHotwordDetectionServiceConfig without running voice"
+                                    + " interaction service");
+                    return;
                 }
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    return mImpl.setHotwordDetectionConfigLocked(options);
+                    mImpl.setHotwordDetectionServiceConfigLocked(options, sharedMemory);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
             }
         }
+
+        @Override
+        public void shutdownHotwordDetectionService() {
+            synchronized (this) {
+                enforceIsCurrentVoiceInteractionService();
+
+                if (mImpl == null) {
+                    Slog.w(TAG,
+                            "shutdownHotwordDetectionService without running voice"
+                                    + " interaction service");
+                    return;
+                }
+                final long caller = Binder.clearCallingIdentity();
+                try {
+                    mImpl.shutdownHotwordDetectionServiceLocked();
+                } finally {
+                    Binder.restoreCallingIdentity(caller);
+                }
+            }
+        }
+
         //----------------- Model management APIs --------------------------------//
 
         @Override
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 04dea3f..5861610 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -43,11 +43,13 @@
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SharedMemory;
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionService;
 import android.service.voice.IVoiceInteractionSession;
 import android.service.voice.VoiceInteractionService;
 import android.service.voice.VoiceInteractionServiceInfo;
+import android.system.OsConstants;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.view.IWindowManager;
@@ -389,25 +391,48 @@
         return mInfo.getSupportsLocalInteraction();
     }
 
-    public int setHotwordDetectionConfigLocked(Bundle options) {
+    public void setHotwordDetectionServiceConfigLocked(@Nullable Bundle options,
+            @Nullable SharedMemory sharedMemory) {
         if (DEBUG) {
-            Slog.d(TAG, "setHotwordDetectionConfigLocked");
+            Slog.d(TAG, "setHotwordDetectionServiceConfigLocked");
         }
         if (mHotwordDetectionComponentName == null) {
-            Slog.e(TAG, "Calling setHotwordDetectionConfigLocked, but hotword detection service"
-                    + " name not found");
-            return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
+            Slog.w(TAG, "Hotword detection service name not found");
+            throw new IllegalStateException("Hotword detection service name not found");
         }
         if (!isIsolatedProcessLocked(mHotwordDetectionComponentName)) {
-            return VoiceInteractionService.HOTWORD_CONFIG_FAILURE;
+            Slog.w(TAG, "Hotword detection service not in isolated process");
+            throw new IllegalStateException("Hotword detection service not in isolated process");
         }
         // TODO : Need to check related permissions for hotword detection service
         // TODO : Sanitize for bundle
 
-        mHotwordDetectionConnection = new HotwordDetectionConnection(mServiceStub, mContext,
-                mHotwordDetectionComponentName, mUser, /* bindInstantServiceAllowed= */ false);
+        if (sharedMemory != null && !sharedMemory.setProtect(OsConstants.PROT_READ)) {
+            Slog.w(TAG, "Can't set sharedMemory to be read-only");
+            throw new IllegalStateException("Can't set sharedMemory to be read-only");
+        }
 
-        return VoiceInteractionService.HOTWORD_CONFIG_SUCCESS;
+        if (mHotwordDetectionConnection == null) {
+            mHotwordDetectionConnection = new HotwordDetectionConnection(mServiceStub, mContext,
+                    mHotwordDetectionComponentName, mUser, /* bindInstantServiceAllowed= */ false,
+                    options, sharedMemory);
+        } else {
+            mHotwordDetectionConnection.setConfigLocked(options, sharedMemory);
+        }
+    }
+
+    public void shutdownHotwordDetectionServiceLocked() {
+        if (DEBUG) {
+            Slog.d(TAG, "shutdownHotwordDetectionServiceLocked");
+        }
+
+        if (mHotwordDetectionConnection == null) {
+            Slog.w(TAG, "shutdown, but no hotword detection connection");
+            return;
+        }
+
+        mHotwordDetectionConnection.cancelLocked();
+        mHotwordDetectionConnection = null;
     }
 
     public IRecognitionStatusCallback createSoundTriggerCallbackLocked(
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index dc2fb94..f84dd7b 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -338,7 +338,7 @@
      *
      * @param videoState The video state in which to answer the connection.
      */
-    public void onAnswer(int videoState) {}
+    public void onAnswer(@VideoProfile.VideoState int videoState) {}
 
     /**
      * Notifies this Conference, which is in {@code STATE_RINGING}, of
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 97a06a8..d8bd6a5 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -2594,9 +2594,9 @@
      * @return The {@code Connection} object to satisfy this call, or {@code null} to
      *         not handle the call.
      */
-    public final RemoteConnection createRemoteIncomingConnection(
-            PhoneAccountHandle connectionManagerPhoneAccount,
-            ConnectionRequest request) {
+    public final @Nullable RemoteConnection createRemoteIncomingConnection(
+            @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull ConnectionRequest request) {
         return mRemoteConnectionManager.createRemoteConnection(
                 connectionManagerPhoneAccount, request, true);
     }
@@ -2613,9 +2613,9 @@
      * @return The {@code Connection} object to satisfy this call, or {@code null} to
      *         not handle the call.
      */
-    public final RemoteConnection createRemoteOutgoingConnection(
-            PhoneAccountHandle connectionManagerPhoneAccount,
-            ConnectionRequest request) {
+    public final @Nullable RemoteConnection createRemoteOutgoingConnection(
+            @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull ConnectionRequest request) {
         return mRemoteConnectionManager.createRemoteConnection(
                 connectionManagerPhoneAccount, request, false);
     }
@@ -2855,12 +2855,14 @@
      * @param connectionManagerPhoneAccount See description at
      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
      * @param request Details about the incoming conference call.
-     * @return The {@code Conference} object to satisfy this call, or {@code null} to
-     *         not handle the call.
+     * @return The {@code Conference} object to satisfy this call. If the conference attempt is
+     *         failed, the return value will be a result of an invocation of
+     *         {@link Connection#createFailedConnection(DisconnectCause)}.
+     *         Return {@code null} if the {@link ConnectionService} cannot handle the call.
      */
     public @Nullable Conference onCreateIncomingConference(
-            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
-            @Nullable ConnectionRequest request) {
+            @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull ConnectionRequest request) {
         return null;
     }
 
@@ -2963,8 +2965,8 @@
      * @param request The outgoing connection request.
      */
     public void onCreateOutgoingConferenceFailed(
-            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
-            @Nullable ConnectionRequest request) {
+            @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull ConnectionRequest request) {
     }
 
 
@@ -3028,12 +3030,14 @@
      *         a {@code PhoneAccount} registered by this {@code ConnectionService} to use for
      *         making the connection.
      * @param request Details about the outgoing call.
-     * @return The {@code Conference} object to satisfy this call, or the result of an invocation
-     *         of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
+     * @return The {@code Conference} object to satisfy this call. If the conference attempt is
+     *         failed, the return value will be a result of an invocation of
+     *         {@link Connection#createFailedConnection(DisconnectCause)}.
+     *         Return {@code null} if the {@link ConnectionService} cannot handle the call.
      */
     public @Nullable Conference onCreateOutgoingConference(
-            @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
-            @Nullable ConnectionRequest request) {
+            @NonNull PhoneAccountHandle connectionManagerPhoneAccount,
+            @NonNull ConnectionRequest request) {
         return null;
     }
 
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 17749e8..1677c8c 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -314,8 +314,8 @@
     public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE";
 
     /**
-     * A URI representing the picture that was downloaded when a call is received or uploaded
-     * when a call is placed.
+     * A {@link Uri} representing the picture that was downloaded when a call is received or
+     * uploaded when a call is placed.
      *
      * This is a content URI within the call log provider which can be used to open a file
      * descriptor. This could be set a short time after a call is added to the Dialer app if the
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 705e93f..1a71f80 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -27,6 +27,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
+import android.net.ipsec.ike.SaProposal;
 import android.os.Build;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
@@ -4170,9 +4171,11 @@
                 KEY_PREFIX + "child_sa_rekey_soft_timer_sec_int";
 
         /**
-         * Supported DH groups for IKE negotiation. Possible values are {@link #DH_GROUP_NONE},
-         * {@link #DH_GROUP_1024_BIT_MODP}, {@link #DH_GROUP_1536_BIT_MODP}, {@link
-         * #DH_GROUP_2048_BIT_MODP}
+         * Supported DH groups for IKE negotiation. Possible values are:
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_NONE},
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_1024_BIT_MODP},
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_1536_BIT_MODP},
+         * {@link android.net.ipsec.ike.SaProposal#DH_GROUP_2048_BIT_MODP}
          */
         public static final String KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY =
                 KEY_PREFIX + "diffie_hellman_groups_int_array";
@@ -4208,23 +4211,29 @@
 
         /**
          * List of supported key sizes for AES Cipher Block Chaining (CBC) encryption mode of child
-         * session. Possible values are {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128}, {@link
-         * #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256}
+         * session. Possible values are:
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
          */
         public static final String KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY =
                 KEY_PREFIX + "child_session_aes_cbc_key_size_int_array";
 
         /**
          * List of supported key sizes for AES Counter (CTR) encryption mode of child session.
-         * Possible values are {@link #KEY_LEN_UNUSED},
-         * {@link #KEY_LEN_AES_128}, {@link #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256}
+         * Possible values are:
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
          */
         public static final String KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY =
                 KEY_PREFIX + "child_session_aes_ctr_key_size_int_array";
 
         /**
          * List of supported encryption algorithms for child session. Possible values are
-         * {@link #ENCRYPTION_ALGORITHM_AES_CBC}
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC}
          */
         public static final String KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY =
                 KEY_PREFIX + "supported_child_session_encryption_algorithms_int_array";
@@ -4245,8 +4254,11 @@
 
         /**
          * List of supported key sizes for AES Cipher Block Chaining (CBC) encryption mode of IKE
-         * session. Possible values - {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128}, {@link
-         * #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256}
+         * session. Possible values:
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
          */
         public static final String KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY =
                 KEY_PREFIX + "ike_session_encryption_aes_cbc_key_size_int_array";
@@ -4254,24 +4266,31 @@
 
         /**
          * List of supported key sizes for AES Counter (CTR) encryption mode of IKE session.
-         * Possible values - {@link #KEY_LEN_UNUSED}, {@link #KEY_LEN_AES_128},
-         * {@link #KEY_LEN_AES_192}, {@link #KEY_LEN_AES_256}
+         * Possible values -
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_UNUSED},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_128},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_192},
+         * {@link android.net.ipsec.ike.SaProposal#KEY_LEN_AES_256}
          */
          public static final String KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY =
                  KEY_PREFIX + "ike_session_encryption_aes_ctr_key_size_int_array";
 
         /**
          * List of supported encryption algorithms for IKE session. Possible values are
-         * {@link #ENCRYPTION_ALGORITHM_AES_CBC}, {@link #ENCRYPTION_ALGORITHM_AES_CTR}
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CBC},
+         * {@link android.net.ipsec.ike.SaProposal#ENCRYPTION_ALGORITHM_AES_CTR}
          */
         public static final String KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY =
                 KEY_PREFIX + "supported_ike_session_encryption_algorithms_int_array";
 
         /**
-         * List of supported integrity algorithms for IKE session Possible values are {@link
-         * #INTEGRITY_ALGORITHM_NONE}, {@link #INTEGRITY_ALGORITHM_HMAC_SHA1_96}, {@link
-         * #INTEGRITY_ALGORITHM_AES_XCBC_96}, {@link #INTEGRITY_ALGORITHM_HMAC_SHA2_256_128}, {@link
-         * #INTEGRITY_ALGORITHM_HMAC_SHA2_384_192}, {@link #INTEGRITY_ALGORITHM_HMAC_SHA2_512_256}
+         * List of supported integrity algorithms for IKE session. Possible values are
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_NONE},
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA1_96},
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_AES_XCBC_96},
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_256_128},
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_384_192},
+         * {@link android.net.ipsec.ike.SaProposal#INTEGRITY_ALGORITHM_HMAC_SHA2_512_256}
          */
         public static final String KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY =
                 KEY_PREFIX + "supported_integrity_algorithms_int_array";
@@ -4291,9 +4310,11 @@
 
         /**
          * List of supported pseudo random function algorithms for IKE session. Possible values are
-         * {@link #PSEUDORANDOM_FUNCTION_HMAC_SHA1}, {@link #PSEUDORANDOM_FUNCTION_AES128_XCBC},
-         * {@link #PSEUDORANDOM_FUNCTION_SHA2_256}, {@link #PSEUDORANDOM_FUNCTION_SHA2_384},
-         * {@link #PSEUDORANDOM_FUNCTION_SHA2_512}
+         * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_HMAC_SHA1},
+         * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_AES128_XCBC},
+         * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_256},
+         * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_384},
+         * {@link android.net.ipsec.ike.SaProposal#PSEUDORANDOM_FUNCTION_SHA2_512}
          */
         public static final String KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY =
                 KEY_PREFIX + "supported_prf_algorithms_int_array";
@@ -4366,182 +4387,6 @@
         public static final int EPDG_ADDRESS_CELLULAR_LOC = 3;
 
         /** @hide */
-        @IntDef({KEY_LEN_UNUSED, KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256})
-        public @interface EncrpytionKeyLengthType {}
-
-        public static final int KEY_LEN_UNUSED = 0;
-        /** AES Encryption/Ciphering Algorithm key length 128 bits. */
-        public static final int KEY_LEN_AES_128 = 128;
-        /** AES Encryption/Ciphering Algorithm key length 192 bits. */
-        public static final int KEY_LEN_AES_192 = 192;
-        /** AES Encryption/Ciphering Algorithm key length 256 bits. */
-        public static final int KEY_LEN_AES_256 = 256;
-
-        /** @hide */
-        @IntDef({
-            DH_GROUP_NONE,
-            DH_GROUP_1024_BIT_MODP,
-            DH_GROUP_1536_BIT_MODP,
-            DH_GROUP_2048_BIT_MODP,
-            DH_GROUP_3072_BIT_MODP,
-            DH_GROUP_4096_BIT_MODP
-        })
-        public @interface DhGroup {}
-
-        /** None Diffie-Hellman Group. */
-        public static final int DH_GROUP_NONE = 0;
-        /**
-         * 1024-bit MODP Diffie-Hellman Group.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int DH_GROUP_1024_BIT_MODP = 2;
-        /**
-         * 1536-bit MODP Diffie-Hellman Group.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int DH_GROUP_1536_BIT_MODP = 5;
-        /**
-         * 2048-bit MODP Diffie-Hellman Group.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int DH_GROUP_2048_BIT_MODP = 14;
-        /**
-         * 3072-bit MODP Diffie-Hellman Group.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int DH_GROUP_3072_BIT_MODP = 15;
-        /**
-         * 4096-bit MODP Diffie-Hellman Group.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int DH_GROUP_4096_BIT_MODP = 16;
-
-        /** @hide */
-        @IntDef({ENCRYPTION_ALGORITHM_AES_CBC, ENCRYPTION_ALGORITHM_AES_CTR})
-        public @interface EncryptionAlgorithm {}
-
-        /**
-         * AES-CBC Encryption/Ciphering Algorithm.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12;
-
-        /**
-         * AES-CTR Encryption/Ciphering Algorithm.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int ENCRYPTION_ALGORITHM_AES_CTR = 13;
-
-        /** @hide */
-        @IntDef({
-            INTEGRITY_ALGORITHM_NONE,
-            INTEGRITY_ALGORITHM_HMAC_SHA1_96,
-            INTEGRITY_ALGORITHM_AES_XCBC_96,
-            INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
-            INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
-            INTEGRITY_ALGORITHM_HMAC_SHA2_512_256
-        })
-        public @interface IntegrityAlgorithm {}
-
-        /** None Authentication/Integrity Algorithm. */
-        public static final int INTEGRITY_ALGORITHM_NONE = 0;
-        /**
-         * HMAC-SHA1 Authentication/Integrity Algorithm.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2;
-        /**
-         * AES-XCBC-96 Authentication/Integrity Algorithm.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5;
-        /**
-         * HMAC-SHA256 Authentication/Integrity Algorithm with 128-bit truncation.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12;
-        /**
-         * HMAC-SHA384 Authentication/Integrity Algorithm with 192-bit truncation.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13;
-        /**
-         * HMAC-SHA512 Authentication/Integrity Algorithm with 256-bit truncation.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14;
-
-        /** @hide */
-        @IntDef({
-            PSEUDORANDOM_FUNCTION_HMAC_SHA1,
-            PSEUDORANDOM_FUNCTION_AES128_XCBC,
-            PSEUDORANDOM_FUNCTION_SHA2_256,
-            PSEUDORANDOM_FUNCTION_SHA2_384,
-            PSEUDORANDOM_FUNCTION_SHA2_512
-        })
-        public @interface PseudorandomFunction {}
-
-        /**
-         * HMAC-SHA1 Pseudorandom Function.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2;
-        /**
-         * AES128-XCBC Pseudorandom Function.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4;
-        /**
-         * HMAC-SHA2-256 Pseudorandom Function.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int PSEUDORANDOM_FUNCTION_SHA2_256 = 5;
-        /**
-         * HMAC-SHA2-384 Pseudorandom Function.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int PSEUDORANDOM_FUNCTION_SHA2_384 = 6;
-        /**
-         * HMAC-SHA2-384 Pseudorandom Function.
-         *
-         * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3">RFC 7296, Internet Key
-         *     Exchange Protocol Version 2 (IKEv2)</a>
-         */
-        public static final int PSEUDORANDOM_FUNCTION_SHA2_512 = 7;
-
-        /** @hide */
         @IntDef({ID_TYPE_FQDN, ID_TYPE_RFC822_ADDR, ID_TYPE_KEY_ID})
         public @interface IkeIdType {}
 
@@ -4582,31 +4427,33 @@
             defaults.putIntArray(
                     KEY_DIFFIE_HELLMAN_GROUPS_INT_ARRAY,
                     new int[] {
-                        DH_GROUP_1024_BIT_MODP, DH_GROUP_1536_BIT_MODP, DH_GROUP_2048_BIT_MODP
+                        SaProposal.DH_GROUP_1024_BIT_MODP,
+                        SaProposal.DH_GROUP_1536_BIT_MODP,
+                        SaProposal.DH_GROUP_2048_BIT_MODP
                     });
             defaults.putIntArray(
                     KEY_SUPPORTED_IKE_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY,
-                    new int[] {ENCRYPTION_ALGORITHM_AES_CBC});
+                    new int[] {SaProposal.ENCRYPTION_ALGORITHM_AES_CBC});
             defaults.putIntArray(
                     KEY_SUPPORTED_CHILD_SESSION_ENCRYPTION_ALGORITHMS_INT_ARRAY,
-                    new int[] {ENCRYPTION_ALGORITHM_AES_CBC});
+                    new int[] {SaProposal.ENCRYPTION_ALGORITHM_AES_CBC});
             defaults.putIntArray(
                     KEY_SUPPORTED_INTEGRITY_ALGORITHMS_INT_ARRAY,
                     new int[] {
-                        INTEGRITY_ALGORITHM_AES_XCBC_96,
-                        INTEGRITY_ALGORITHM_HMAC_SHA1_96,
-                        INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
-                        INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
-                        INTEGRITY_ALGORITHM_HMAC_SHA2_512_256,
+                        SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96,
+                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96,
+                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128,
+                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192,
+                        SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256,
                     });
             defaults.putIntArray(
                     KEY_SUPPORTED_PRF_ALGORITHMS_INT_ARRAY,
                     new int[] {
-                        PSEUDORANDOM_FUNCTION_HMAC_SHA1,
-                        PSEUDORANDOM_FUNCTION_AES128_XCBC,
-                        PSEUDORANDOM_FUNCTION_SHA2_256,
-                        PSEUDORANDOM_FUNCTION_SHA2_384,
-                        PSEUDORANDOM_FUNCTION_SHA2_512
+                        SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1,
+                        SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC,
+                        SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256,
+                        SaProposal.PSEUDORANDOM_FUNCTION_SHA2_384,
+                        SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512
                     });
 
             defaults.putInt(KEY_EPDG_AUTHENTICATION_METHOD_INT, AUTHENTICATION_METHOD_EAP_ONLY);
@@ -4616,16 +4463,28 @@
             defaults.putInt(KEY_NATT_KEEP_ALIVE_TIMER_SEC_INT, 20);
             defaults.putIntArray(
                     KEY_IKE_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY,
-                    new int[] {KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256});
+                    new int[] {
+                      SaProposal.KEY_LEN_AES_128,
+                      SaProposal.KEY_LEN_AES_192,
+                      SaProposal.KEY_LEN_AES_256});
             defaults.putIntArray(
                     KEY_CHILD_SESSION_AES_CBC_KEY_SIZE_INT_ARRAY,
-                    new int[] {KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256});
+                    new int[] {
+                      SaProposal.KEY_LEN_AES_128,
+                      SaProposal.KEY_LEN_AES_192,
+                      SaProposal.KEY_LEN_AES_256});
             defaults.putIntArray(
                     KEY_IKE_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY,
-                    new int[] {KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256});
+                    new int[] {
+                      SaProposal.KEY_LEN_AES_128,
+                      SaProposal.KEY_LEN_AES_192,
+                      SaProposal.KEY_LEN_AES_256});
             defaults.putIntArray(
                     KEY_CHILD_SESSION_AES_CTR_KEY_SIZE_INT_ARRAY,
-                    new int[] {KEY_LEN_AES_128, KEY_LEN_AES_192, KEY_LEN_AES_256});
+                    new int[] {
+                      SaProposal.KEY_LEN_AES_128,
+                      SaProposal.KEY_LEN_AES_192,
+                      SaProposal.KEY_LEN_AES_256});
             defaults.putIntArray(
                     KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY,
                     new int[] {EPDG_ADDRESS_PLMN, EPDG_ADDRESS_STATIC});
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 2d5f5fb..f7580d7 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -602,6 +602,43 @@
     public @interface SimDisplayNameSource {}
 
     /**
+     * Device status is not shared to a remote party.
+     */
+    public static final int D2D_SHARING_DISABLED = 0;
+
+    /**
+     * Device status is shared with all numbers in the user's contacts.
+     */
+    public static final int D2D_SHARING_ALL_CONTACTS = 1;
+
+    /**
+     * Device status is shared with all starred contacts.
+     */
+    public static final int D2D_SHARING_STARRED_CONTACTS = 2;
+
+    /**
+     * Device status is shared whenever possible.
+     */
+    public static final int D2D_SHARING_ALL = 3;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"D2D_SHARING_"},
+            value = {
+                    D2D_SHARING_DISABLED,
+                    D2D_SHARING_ALL_CONTACTS,
+                    D2D_SHARING_STARRED_CONTACTS,
+                    D2D_SHARING_ALL
+            })
+    public @interface DeviceToDeviceStatusSharing {}
+
+    /**
+     * TelephonyProvider column name for device to device sharing status.
+     * <P>Type: INTEGER (int)</P>
+     */
+    public static final String D2D_STATUS_SHARING = SimInfo.COLUMN_D2D_STATUS_SHARING;
+
+    /**
      * TelephonyProvider column name for the color of a SIM.
      * <P>Type: INTEGER (int)</P>
      */
@@ -3374,6 +3411,36 @@
     }
 
     /**
+     * Set the device to device status sharing user preference for a subscription ID. The setting
+     * app uses this method to indicate with whom they wish to share device to device status
+     * information.
+     * @param sharing the status sharing preference
+     * @param subId the unique Subscription ID in database
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setDeviceToDeviceStatusSharing(@DeviceToDeviceStatusSharing int sharing,
+            int subId) {
+        if (VDBG) {
+            logd("[setDeviceToDeviceStatusSharing] + sharing: " + sharing + " subId: " + subId);
+        }
+        setSubscriptionPropertyHelper(subId, "setDeviceToDeviceSharingStatus",
+                (iSub)->iSub.setDeviceToDeviceStatusSharing(sharing, subId));
+    }
+
+    /**
+     * Returns the user-chosen device to device status sharing preference
+     * @param subId Subscription id of subscription
+     * @return The device to device status sharing preference
+     */
+    public @DeviceToDeviceStatusSharing int getDeviceToDeviceStatusSharing(int subId) {
+        if (VDBG) {
+            logd("[getDeviceToDeviceStatusSharing] + subId: " + subId);
+        }
+        return getIntegerSubscriptionProperty(subId, D2D_STATUS_SHARING, D2D_SHARING_DISABLED,
+                mContext);
+    }
+
+    /**
      * DO NOT USE.
      * This API is designed for features that are not finished at this point. Do not call this API.
      * @hide
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 3cb72b5..4dc6c7c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -139,6 +139,8 @@
 import java.util.UUID;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 /**
  * Provides access to information about the telephony services on
@@ -3147,6 +3149,10 @@
                 return NETWORK_TYPE_BITMASK_LTE_CA;
             case NETWORK_TYPE_NR:
                 return NETWORK_TYPE_BITMASK_NR;
+            case NETWORK_TYPE_IWLAN:
+                return NETWORK_TYPE_BITMASK_IWLAN;
+            case NETWORK_TYPE_IDEN:
+                return (1 << (NETWORK_TYPE_IDEN - 1));
             default:
                 return NETWORK_TYPE_BITMASK_UNKNOWN;
         }
@@ -8642,8 +8648,8 @@
     public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3;
 
     /**
-     * Set the allowed network types of the device and
-     * provide the reason triggering the allowed network change.
+     * Set the allowed network types of the device and provide the reason triggering the allowed
+     * network change.
      * This can be called for following reasons
      * <ol>
      * <li>Allowed network types control by USER {@link #ALLOWED_NETWORK_TYPES_REASON_USER}
@@ -8655,10 +8661,15 @@
      * </ol>
      * This API will result in allowing an intersection of allowed network types for all reasons,
      * including the configuration done through other reasons.
+     *
+     * The functionality of this API with the parameter
+     * {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER} is the same as the API
+     * {@link TelephonyManager#setAllowedNetworkTypes}. Use this API instead of
+     * {@link TelephonyManager#setAllowedNetworkTypes}.
      * <p>
      * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
      * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
-     * setAllowedNetworkTypesBitmap is used on the radio interface.  Otherwise,
+     * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise,
      * setPreferredNetworkTypesBitmap is used instead.
      *
      * @param reason the reason the allowed network type change is taking place
@@ -8698,21 +8709,17 @@
      * {@link #getAllowedNetworkTypesForReason} returns allowed network type for a
      * specific reason.
      *
-     * <p>Requires Permission:
-     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
-     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
-     *
      * @param reason the reason the allowed network type change is taking place
      * @return the allowed network type bitmask
      * @throws IllegalStateException    if the Telephony process is not currently available.
      * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     @RequiresFeature(
             enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
             value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED)
+    @SystemApi
     public @NetworkTypeBitMask long getAllowedNetworkTypesForReason(
             @AllowedNetworkTypesReason int reason) {
         if (!isValidAllowedNetworkTypesReason(reason)) {
@@ -8757,6 +8764,25 @@
     }
 
     /**
+     * Returns a string representation of the allowed network types{@link NetworkTypeBitMask}.
+     *
+     * @param networkTypeBitmask The bitmask of allowed network types.
+     * @return the name of the allowed network types
+     * @hide
+     */
+    public static String convertNetworkTypeBitmaskToString(
+            @NetworkTypeBitMask long networkTypeBitmask) {
+        String networkTypeName = IntStream.rangeClosed(NETWORK_TYPE_GPRS, NETWORK_TYPE_NR)
+                .filter(x -> {
+                    return (networkTypeBitmask & getBitMaskForNetworkType(x))
+                            == getBitMaskForNetworkType(x);
+                })
+                .mapToObj(x -> getNetworkTypeName(x))
+                .collect(Collectors.joining("|"));
+        return TextUtils.isEmpty(networkTypeName) ? "UNKNOWN" : networkTypeName;
+    }
+
+    /**
      * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
      * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
@@ -9135,9 +9161,7 @@
     /**
      * Set the user-set status for enriched calling with call composer.
      *
-     * @param status user-set status for enriched calling with call composer;
-     *               it must be either {@link #CALL_COMPOSER_STATUS_ON} or
-     *               {@link #CALL_COMPOSER_STATUS_OFF}.
+     * @param status user-set status for enriched calling with call composer.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
      * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
@@ -12579,6 +12603,7 @@
                     NETWORK_TYPE_BITMASK_LTE,
                     NETWORK_TYPE_BITMASK_LTE_CA,
                     NETWORK_TYPE_BITMASK_NR,
+                    NETWORK_TYPE_BITMASK_IWLAN
             })
     public @interface NetworkTypeBitMask {}
 
@@ -15218,13 +15243,17 @@
      * </ul>
      * @param appType icc application type, like {@link #APPTYPE_USIM} or {@link
      * #APPTYPE_ISIM} or {@link#APPTYPE_UNKNOWN}
-     * @param nafId Network Application Function(NAF) fully qualified domain name and
-     * the selected GBA mode. It shall contain two parts delimited by "@" sign. The first
-     * part is the constant string "3GPP-bootstrapping" (GBA_ME),
-     * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest),
-     * and the latter part shall be the FQDN of the NAF (e.g.
-     * "3GPP-bootstrapping@naf1.operator.com" or "3GPP-bootstrapping-uicc@naf1.operator.com",
-     * or "3GPP-bootstrapping-digest@naf1.operator.com").
+     * @param nafId A URI to specify Network Application Function(NAF) fully qualified domain
+     * name (FQDN) and the selected GBA mode. The authority of the URI must contain two parts
+     * delimited by "@" sign. The first part is the constant string "3GPP-bootstrapping" (GBA_ME),
+     * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest).
+     * The second part shall be the FQDN of the NAF. The scheme of the URI is not actually used
+     * for the authentication, which may be set the same as the resource that the application is
+     * going to access. For example, the nafId can be
+     * "https://3GPP-bootstrapping@naf1.operator.com",
+     * "https://3GPP-bootstrapping-uicc@naf1.operator.com",
+     * "https://3GPP-bootstrapping-digest@naf1.operator.com",
+     * "ftps://3GPP-bootstrapping-digest@naf1.operator.com".
      * @param securityProtocol Security protocol identifier between UE and NAF.  See
      * 3GPP TS 33.220 Annex H. Application can use
      * {@link UaSecurityProtocolIdentifier#createDefaultUaSpId},
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index a764229..ffe5399 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -18,6 +18,7 @@
 package android.telephony.data;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -29,6 +30,7 @@
 import android.telephony.data.ApnSetting.ProtocolType;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -812,11 +814,19 @@
 
         /**
          * Set pdu session id.
+         * <p/>
+         * The id must be between 1 and 15 when linked to a pdu session.  If no pdu session
+         * exists for the current data call, the id must be set to {@link PDU_SESSION_ID_NOT_SET}.
          *
          * @param pduSessionId Pdu Session Id of the data call.
          * @return The same instance of the builder.
          */
-        public @NonNull Builder setPduSessionId(int pduSessionId) {
+        public @NonNull Builder setPduSessionId(
+                @IntRange(from = PDU_SESSION_ID_NOT_SET, to = 15) int pduSessionId) {
+            Preconditions.checkArgument(pduSessionId >= PDU_SESSION_ID_NOT_SET,
+                    "pduSessionId must be greater than or equal to" + PDU_SESSION_ID_NOT_SET);
+            Preconditions.checkArgument(pduSessionId <= 15,
+                    "pduSessionId must be less than or equal to 15.");
             mPduSessionId = pduSessionId;
             return this;
         }
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index f5f29c6..048b329 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -41,6 +41,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Base class of data service. Services that extend DataService must register the service in
@@ -284,11 +285,11 @@
          *
          * Any resources being transferred cannot be released while a
          * handover is underway.
-         *
+         * <p/>
          * If a handover was unsuccessful, then the framework calls
          * {@link DataService#cancelHandover}.  The target transport retains ownership over any of
          * the resources being transferred.
-         *
+         * <p/>
          * If a handover was successful, the framework calls {@link DataService#deactivateDataCall}
          * with reason {@link DataService.REQUEST_REASON_HANDOVER}. The target transport now owns
          * the transferred resources and is responsible for releasing them.
@@ -299,21 +300,27 @@
          * @hide
          */
         public void startHandover(int cid, @NonNull DataServiceCallback callback) {
+            Objects.requireNonNull(callback, "callback cannot be null");
             // The default implementation is to return unsupported.
-            if (callback != null) {
-                Log.d(TAG, "startHandover: " + cid);
-                callback.onHandoverStarted(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
-            } else {
-                Log.e(TAG, "startHandover: " + cid + ", callback is null");
-            }
+            Log.d(TAG, "startHandover: " + cid);
+            callback.onHandoverStarted(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
         }
 
         /**
          * Indicates that a handover was cancelled after a call to
          * {@link DataService#startHandover}. This is called on the source transport.
-         *
+         * <p/>
          * Since the handover was unsuccessful, the source transport retains ownership over any of
          * the resources being transferred and is still responsible for releasing them.
+         * <p/>
+         * The handover can be cancelled up until either:
+         * <ul><li>
+         *     The handover was successful after receiving a successful response from
+         *     {@link DataService#setupDataCall} on the target transport.
+         * </li><li>
+         *     The data call on the source transport was lost.
+         * </li>
+         * </ul>
          *
          * @param cid The identifier of the data call which is provided in {@link DataCallResponse}
          * @param callback The result callback for this request.
@@ -321,13 +328,10 @@
          * @hide
          */
         public void cancelHandover(int cid, @NonNull DataServiceCallback callback) {
+            Objects.requireNonNull(callback, "callback cannot be null");
             // The default implementation is to return unsupported.
-            if (callback != null) {
-                Log.d(TAG, "cancelHandover: " + cid);
-                callback.onHandoverCancelled(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
-            } else {
-                Log.e(TAG, "cancelHandover: " + cid + ", callback is null");
-            }
+            Log.d(TAG, "cancelHandover: " + cid);
+            callback.onHandoverCancelled(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
         }
 
         /**
diff --git a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
index 041edc0..406c38b 100644
--- a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
+++ b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
@@ -184,16 +184,6 @@
         mRemoteAddresses = Collections.unmodifiableList(remoteAddresses);
     }
 
-    /**
-     * Creates attributes based off of a parcel
-     * @param in the parcel
-     * @return the attributes
-     */
-    @NonNull
-    public static EpsBearerQosSessionAttributes create(@NonNull final Parcel in) {
-        return new EpsBearerQosSessionAttributes(in);
-    }
-
     @Override
     public int describeContents() {
         return 0;
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 9bb4db8..486f746 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -277,6 +277,10 @@
      * server infrastructure to get the picture. It can be set via
      * {@link #setCallExtra(String, String)}.
      *
+     * Note that this URL is not intended to be parsed by the IMS stack -- it should be sent
+     * directly to the network for consumption by the called party or forwarded directly from the
+     * network to the platform for caching and download.
+     *
      * Reference: RCC.20 Section 2.4.3.2
      */
     public static final String EXTRA_PICTURE_URL = "android.telephony.ims.extra.PICTURE_URL";
@@ -729,6 +733,10 @@
 
     /**
      * Set the call extra value (Parcelable), given the call extra name.
+     *
+     * Note that the {@link Parcelable} provided must be a class defined in the Android API surface,
+     * as opposed to a class defined by your app.
+     *
      * @param name call extra name
      * @param parcelable call extra value
      */
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 85cd81b..abc5606 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -1010,6 +1010,16 @@
                 }
             }
 
+            @Override
+            public void onPreProvisioningReceived(byte[] configXml) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> mLocalCallback.onPreProvisioningReceived(configXml));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+
             private void setExecutor(Executor executor) {
                 mExecutor = executor;
             }
@@ -1022,7 +1032,7 @@
          * due to various triggers defined in GSMA RCC.14 for ACS(auto configuration
          * server) or other operator defined triggers. If RCS provisioning is already
          * completed at the time of callback registration, then this method shall be
-         * invoked with the current configuration
+         * invoked with the current configuration.
          * @param configXml The RCS configuration XML received by OTA. It is defined
          * by GSMA RCC.07.
          */
@@ -1055,6 +1065,20 @@
          */
         public void onRemoved() {}
 
+        /**
+         * Some carriers using ACS (auto configuration server) may send a carrier-specific
+         * pre-provisioning configuration XML if the user has not been provisioned for RCS
+         * services yet. When this provisioning XML is received, the framework will move
+         * into a "not provisioned" state for RCS. In order for provisioning to proceed,
+         * the application must parse this configuration XML and perform the carrier specific
+         * opt-in flow for RCS services. If the user accepts, {@link #triggerRcsReconfiguration}
+         * must be called in order for the device to move out of this state and try to fetch
+         * the RCS provisioning information.
+         *
+         * @param configXml the pre-provisioning config in carrier specified format.
+         */
+        public void onPreProvisioningReceived(@NonNull byte[] configXml) {}
+
         /**@hide*/
         public final IRcsConfigCallback getBinder() {
             return mBinder;
diff --git a/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl b/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
index 5a8973e..d0853d1 100644
--- a/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl
@@ -25,5 +25,6 @@
     void onAutoConfigurationErrorReceived(int errorCode, String errorString);
     void onConfigurationReset();
     void onRemoved();
+    void onPreProvisioningReceived(in byte[] config);
 }
 
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index 21aeb64..d75da90 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -552,11 +552,34 @@
         }
         mRcsCallbacks.broadcastAction(c -> {
             try {
-                //TODO compressed by default?
                 c.onAutoConfigurationErrorReceived(errorCode, errorString);
             } catch (RemoteException e) {
                 Log.w(TAG, "dead binder in notifyAutoConfigurationErrorReceived, skipping.");
             }
         });
     }
+
+    /**
+     * Notifies application that pre-provisioning config is received.
+     *
+     * <p>Some carriers using ACS (auto configuration server) may send a carrier-specific
+     * pre-provisioning configuration XML if the user has not been provisioned for RCS
+     * services yet. When such provisioning XML is received, ACS client must call this
+     * method to notify the application with the XML.
+     *
+     * @param configXml the pre-provisioning config in carrier specified format.
+     */
+    public final void notifyPreProvisioningReceived(@NonNull byte[] configXml) {
+        // can be null in testing
+        if (mRcsCallbacks == null) {
+            return;
+        }
+        mRcsCallbacks.broadcastAction(c -> {
+            try {
+                c.onPreProvisioningReceived(configXml);
+            } catch (RemoteException e) {
+                Log.w(TAG, "dead binder in notifyPreProvisioningReceived, skipping.");
+            }
+        });
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 571efce..9493c76 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -300,4 +300,6 @@
     boolean canDisablePhysicalSubscription();
 
     int setUiccApplicationsEnabled(boolean enabled, int subscriptionId);
+
+    int setDeviceToDeviceStatusSharing(int sharing, int subId);
 }
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index b2719fb..896ec9a 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -11,6 +11,8 @@
         <option name="force-skip-system-props" value="true" />
         <!-- set WM tracing verbose level to all -->
         <option name="run-command" value="cmd window tracing level all" />
+        <!-- set WM tracing to frame (avoid incomplete states) -->
+        <option name="run-command" value="cmd window tracing frame" />
         <!-- restart launcher to activate TAPL -->
         <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
     </target_preparer>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index fbf18d4..c92d40c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -16,37 +16,12 @@
 
 package com.android.server.wm.flicker.close
 
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow
-import com.android.server.wm.flicker.wallpaperWindowBecomesVisible
-import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
-import org.junit.Assume
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -59,110 +34,15 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseAppBackButtonTest(private val testSpec: FlickerTestParameter) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val testApp = SimpleAppHelper(instrumentation)
-
-    @FlickerBuilderProvider
-    fun buildFlicker(): FlickerBuilder {
-        return FlickerBuilder(instrumentation).apply {
-            withTestName { testSpec.name }
-            repeat { testSpec.config.repetitions }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                }
-                eachRun {
-                    this.setRotation(testSpec.config.startRotation)
-                    testApp.launchViaIntent(wmHelper)
-                }
-            }
+class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
             transitions {
                 device.pressBack()
                 wmHelper.waitForHomeActivityVisible()
             }
-            teardown {
-                eachRun {
-                    this.setRotation(Surface.ROTATION_0)
-                }
-                test {
-                    testApp.exit()
-                }
-            }
         }
-    }
-
-    @Presubmit
-    @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun launcherReplacesAppWindowAsTopWindow() =
-        testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
-
-    @Presubmit
-    @Test
-    fun wallpaperWindowBecomesVisible() = testSpec.wallpaperWindowBecomesVisible()
-
-    @Presubmit
-    @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
-        Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun wallpaperLayerReplacesAppLayer() = testSpec.wallpaperLayerReplacesAppLayer(testApp)
-
-    @Presubmit
-    @Test
-    fun navBarLayerRotatesAndScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest
-    @Test
-    fun navBarLayerRotatesAndScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Presubmit
-    @Test
-    fun statusBarLayerRotatesScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest
-    @Test
-    fun statusBarLayerRotatesScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest(bugId = 173684672)
-    @Test
-    fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
-        testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
-    @FlakyTest(bugId = 173684672)
-    @Test
-    fun visibleLayersShownMoreThanOneConsecutiveEntry() =
-        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index 08d2b7c..1f880f6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -16,37 +16,12 @@
 
 package com.android.server.wm.flicker.close
 
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow
-import com.android.server.wm.flicker.wallpaperWindowBecomesVisible
-import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.repetitions
-import com.android.server.wm.flicker.startRotation
-import org.junit.Assume
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -59,110 +34,15 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseAppHomeButtonTest(private val testSpec: FlickerTestParameter) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val testApp = SimpleAppHelper(instrumentation)
-
-    @FlickerBuilderProvider
-    fun buildFlicker(): FlickerBuilder {
-        return FlickerBuilder(instrumentation).apply {
-            withTestName { testSpec.name }
-            repeat { testSpec.config.repetitions }
-            setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                }
-                eachRun {
-                    testApp.launchViaIntent(wmHelper)
-                    this.setRotation(testSpec.config.startRotation)
-                }
-            }
+class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) {
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
             transitions {
                 device.pressHome()
                 wmHelper.waitForHomeActivityVisible()
             }
-            teardown {
-                eachRun {
-                    this.setRotation(Surface.ROTATION_0)
-                }
-                test {
-                    testApp.exit()
-                }
-            }
         }
-    }
-
-    @Presubmit
-    @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun launcherReplacesAppWindowAsTopWindow() =
-        testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
-
-    @Presubmit
-    @Test
-    fun wallpaperWindowBecomesVisible() = testSpec.wallpaperWindowBecomesVisible()
-
-    @Presubmit
-    @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(
-        testSpec.config.startRotation, Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun wallpaperLayerReplacesAppLayer() = testSpec.wallpaperLayerReplacesAppLayer(testApp)
-
-    @Presubmit
-    @Test
-    fun navBarLayerRotatesAndScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest
-    @Test
-    fun navBarLayerRotatesAndScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Presubmit
-    @Test
-    fun statusBarLayerRotatesScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest
-    @Test
-    fun statusBarLayerRotatesScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest(bugId = 173689015)
-    @Test
-    fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
-        testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
-    @FlakyTest(bugId = 173689015)
-    @Test
-    fun visibleLayersShownMoreThanOneConsecutiveEntry() =
-        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
new file mode 100644
index 0000000..fef49d9
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.close
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import androidx.test.filters.FlakyTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer
+import com.android.server.wm.flicker.wallpaperWindowBecomesVisible
+import org.junit.Assume
+import org.junit.Test
+
+abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) {
+    protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+    protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
+        setup {
+            eachRun {
+                testApp.launchViaIntent(wmHelper)
+                this.setRotation(testSpec.config.startRotation)
+            }
+        }
+        teardown {
+            test {
+                testApp.exit()
+            }
+        }
+    }
+
+    @FlickerBuilderProvider
+    fun buildFlicker(): FlickerBuilder {
+        return FlickerBuilder(instrumentation).apply {
+            transition(testSpec.config)
+        }
+    }
+
+    @Presubmit
+    @Test
+    open fun navBarWindowIsAlwaysVisible() {
+        testSpec.navBarWindowIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun statusBarWindowIsAlwaysVisible() {
+        testSpec.statusBarWindowIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun navBarLayerIsAlwaysVisible() {
+        testSpec.navBarLayerIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun statusBarLayerIsAlwaysVisible() {
+        testSpec.statusBarLayerIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun navBarLayerRotatesAndScales() {
+        Assume.assumeFalse(testSpec.isRotated)
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+    }
+
+    @FlakyTest
+    @Test
+    open fun navBarLayerRotatesAndScales_Flaky() {
+        Assume.assumeTrue(testSpec.isRotated)
+        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
+    }
+
+    @Presubmit
+    @Test
+    open fun statusBarLayerRotatesScales() {
+        Assume.assumeFalse(testSpec.isRotated)
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+    }
+
+    @FlakyTest
+    @Test
+    open fun statusBarLayerRotatesScales_Flaky() {
+        Assume.assumeTrue(testSpec.isRotated)
+        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
+    }
+
+    @FlakyTest(bugId = 173689015)
+    @Test
+    open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+        testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+    }
+
+    @FlakyTest(bugId = 173689015)
+    @Test
+    open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+    }
+
+    @Presubmit
+    @Test
+    open fun noUncoveredRegions() {
+        testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0)
+    }
+
+    @Presubmit
+    @Test
+    open fun launcherReplacesAppWindowAsTopWindow() {
+        testSpec.launcherReplacesAppWindowAsTopWindow(testApp)
+    }
+
+    @Presubmit
+    @Test
+    open fun wallpaperWindowBecomesVisible() {
+        testSpec.wallpaperWindowBecomesVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun wallpaperLayerReplacesAppLayer() {
+        testSpec.wallpaperLayerReplacesAppLayer(testApp)
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 47eaddf..17aa1d1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.ime
 
 import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -89,40 +88,40 @@
         }
     }
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
         testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeAppWindowIsAlwaysVisible() = testSpec.imeAppWindowIsAlwaysVisible(testApp)
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation)
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeLayerBecomesInvisible() = testSpec.imeLayerBecomesInvisible()
 
-    @Postsubmit
+    @Presubmit
     @Test
     fun imeAppLayerIsAlwaysVisible() = testSpec.imeAppLayerIsAlwaysVisible(testApp)
 
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 74f002d..56ed21b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -16,37 +16,14 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
-import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
-import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import org.junit.Assume
 import org.junit.FixMethodOrder
-import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
@@ -59,114 +36,25 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppColdTest(private val testSpec: FlickerTestParameter) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val testApp = SimpleAppHelper(instrumentation)
-
-    @FlickerBuilderProvider
-    fun buildFlicker(): FlickerBuilder {
-        return FlickerBuilder(instrumentation).apply {
-            withTestName { testSpec.name }
-            repeat { testSpec.config.repetitions }
+class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
             setup {
-                test {
-                    device.wakeUpAndGoToHomeScreen()
-                }
                 eachRun {
                     this.setRotation(testSpec.config.startRotation)
                 }
             }
-            transitions {
-                testApp.launchViaIntent(wmHelper)
-                // wmHelper.waitForFullScreenApp(testApp.component)
-            }
             teardown {
                 eachRun {
-                    testApp.exit()
-                    wmHelper.waitForAppTransitionIdle()
-                    this.setRotation(Surface.ROTATION_0)
+                    testApp.exit(wmHelper)
                 }
             }
+            transitions {
+                testApp.launchViaIntent(wmHelper)
+                wmHelper.waitForFullScreenApp(testApp.component)
+            }
         }
-    }
-
-    @Presubmit
-    @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
-        testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
-    @Presubmit
-    @Test
-    fun appWindowReplacesLauncherAsTopWindow() =
-        testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
-
-    @Presubmit
-    @Test
-    fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible()
-
-    @Presubmit
-    @Test
-    // During testing the launcher is always in portrait mode
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
-        Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun appLayerReplacesWallpaperLayer() =
-        testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
-
-    @Presubmit
-    @Test
-    fun navBarLayerRotatesAndScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest
-    @Test
-    fun navBarLayerRotatesAndScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Presubmit
-    @Test
-    fun statusBarLayerRotatesScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest
-    @Test
-    fun statusBarLayerRotatesScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Presubmit
-    @Test
-    fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
-
-    @FlakyTest
-    @Test
-    fun visibleLayersShownMoreThanOneConsecutiveEntry() =
-        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 18fac6a..4a32a9e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -16,36 +16,19 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.app.Instrumentation
 import android.platform.test.annotations.Presubmit
-import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.endRotation
-import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
 import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
 import com.android.server.wm.flicker.helpers.reopenAppFromOverview
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
 import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
-import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import org.junit.Assume
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -61,18 +44,12 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppFromOverviewTest(private val testSpec: FlickerTestParameter) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val testApp = SimpleAppHelper(instrumentation)
-
-    @FlickerBuilderProvider
-    fun buildFlicker(): FlickerBuilder {
-        return FlickerBuilder(instrumentation).apply {
-            withTestName { testSpec.name }
-            repeat { testSpec.config.repetitions }
+class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
             setup {
                 test {
-                    device.wakeUpAndGoToHomeScreen()
                     testApp.launchViaIntent(wmHelper)
                 }
                 eachRun {
@@ -87,71 +64,50 @@
                 device.reopenAppFromOverview(wmHelper)
                 wmHelper.waitForFullScreenApp(testApp.component)
             }
-            teardown {
-                test {
-                    testApp.exit()
-                }
-            }
         }
+
+    @Test
+    override fun appWindowReplacesLauncherAsTopWindow() =
+        super.appWindowReplacesLauncherAsTopWindow()
+
+    @Test
+    override fun wallpaperWindowBecomesInvisible() {
+        testSpec.wallpaperWindowBecomesInvisible()
     }
 
     @Presubmit
     @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
-
-    @Test
-    fun appWindowReplacesLauncherAsTopWindow() =
-        testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
-
-    @Test
-    fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible()
-
-    @Presubmit
-    @Test
-    fun appLayerReplacesWallpaperLayer() =
-        testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
-
-    @Presubmit
-    @Test
-    fun navBarLayerRotatesAndScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Presubmit
-    @Test
-    fun statusBarLayerRotatesScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() {
+    override fun statusBarLayerIsAlwaysVisible() {
         Assume.assumeTrue(testSpec.isRotated)
-        testSpec.statusBarLayerIsAlwaysVisible()
+        super.statusBarLayerIsAlwaysVisible()
     }
 
     @Presubmit
     @Test
-    fun navBarLayerIsAlwaysVisible() {
+    override fun navBarLayerIsAlwaysVisible() {
         Assume.assumeTrue(testSpec.isRotated)
-        testSpec.navBarLayerIsAlwaysVisible()
+        super.navBarLayerIsAlwaysVisible()
+    }
+
+    @FlakyTest
+    @Test
+    fun statusBarLayerIsAlwaysVisible_Flaky() {
+        Assume.assumeFalse(testSpec.isRotated)
+        super.statusBarLayerIsAlwaysVisible()
+    }
+
+    @FlakyTest
+    @Test
+    fun navBarLayerIsAlwaysVisible_Flaky() {
+        Assume.assumeFalse(testSpec.isRotated)
+        super.navBarLayerIsAlwaysVisible()
     }
 
     @Presubmit
     @Test
-    fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
-
-    @Presubmit
-    @Test
-    fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
         Assume.assumeFalse(testSpec.isRotated)
-        testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
     }
 
     @FlakyTest
@@ -163,9 +119,9 @@
 
     @Presubmit
     @Test
-    fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
         Assume.assumeFalse(testSpec.isRotated)
-        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
     }
 
     @FlakyTest
@@ -175,11 +131,6 @@
         testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
     }
 
-    @Presubmit
-    @Test
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(Surface.ROTATION_0,
-        testSpec.config.endRotation)
-
     companion object {
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
new file mode 100644
index 0000000..e9f0534
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch
+
+import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
+import android.view.Surface
+import androidx.test.filters.FlakyTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.endRotation
+import com.android.server.wm.flicker.focusChanges
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.navBarLayerRotatesAndScales
+import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.noUncoveredRegions
+import com.android.server.wm.flicker.repetitions
+import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
+import com.android.server.wm.flicker.statusBarLayerRotatesScales
+import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
+import org.junit.Assume
+import org.junit.Test
+
+abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) {
+    protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    protected val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+
+    protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = {
+        withTestName { testSpec.name }
+        repeat { testSpec.config.repetitions }
+        setup {
+            test {
+                device.wakeUpAndGoToHomeScreen()
+                this.setRotation(testSpec.config.startRotation)
+            }
+        }
+        teardown {
+            test {
+                testApp.exit()
+            }
+        }
+    }
+
+    @FlickerBuilderProvider
+    fun buildFlicker(): FlickerBuilder {
+        return FlickerBuilder(instrumentation).apply {
+            transition(testSpec.config)
+        }
+    }
+
+    @Presubmit
+    @Test
+    open fun navBarWindowIsAlwaysVisible() {
+        testSpec.navBarWindowIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun navBarLayerIsAlwaysVisible() {
+        testSpec.navBarLayerIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun navBarLayerRotatesAndScales() {
+        Assume.assumeFalse(testSpec.isRotated)
+        testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
+    }
+
+    @FlakyTest
+    @Test
+    open fun navBarLayerRotatesAndScales_Flaky() {
+        Assume.assumeTrue(testSpec.isRotated)
+        testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation)
+    }
+
+    @Presubmit
+    @Test
+    open fun statusBarWindowIsAlwaysVisible() {
+        testSpec.statusBarWindowIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun statusBarLayerIsAlwaysVisible() {
+        testSpec.statusBarLayerIsAlwaysVisible()
+    }
+
+    @Presubmit
+    @Test
+    open fun statusBarLayerRotatesScales() {
+        Assume.assumeFalse(testSpec.isRotated)
+        testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
+    }
+
+    @FlakyTest
+    @Test
+    open fun statusBarLayerRotatesScales_Flaky() {
+        Assume.assumeTrue(testSpec.isRotated)
+        testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation)
+    }
+
+    @Presubmit
+    @Test
+    open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+        testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+    }
+
+    @FlakyTest
+    @Test
+    open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+    }
+
+    @Presubmit
+    @Test
+    // During testing the launcher is always in portrait mode
+    open fun noUncoveredRegions() {
+        testSpec.noUncoveredRegions(Surface.ROTATION_0, testSpec.config.endRotation)
+    }
+
+    @Presubmit
+    @Test
+    open fun focusChanges() {
+        testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
+    }
+
+    @Presubmit
+    @Test
+    open fun appLayerReplacesWallpaperLayer() {
+        testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
+    }
+
+    @Presubmit
+    @Test
+    open fun appWindowReplacesLauncherAsTopWindow() {
+        testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
+    }
+
+    @Presubmit
+    @Test
+    open fun wallpaperWindowBecomesInvisible() {
+        testSpec.wallpaperWindowBecomesInvisible()
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
index b61310a..a8b5ea1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
@@ -16,34 +16,14 @@
 
 package com.android.server.wm.flicker.launch
 
-import android.app.Instrumentation
-import android.platform.test.annotations.Presubmit
-import android.view.Surface
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.RequiresDevice
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.server.wm.flicker.FlickerBuilderProvider
 import com.android.server.wm.flicker.FlickerParametersRunnerFactory
 import com.android.server.wm.flicker.FlickerTestParameter
 import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.focusChanges
 import com.android.server.wm.flicker.helpers.setRotation
-import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.repetitions
 import com.android.server.wm.flicker.startRotation
-import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
-import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
 import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.helpers.SimpleAppHelper
-import org.junit.Assume
 import org.junit.FixMethodOrder
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -58,20 +38,13 @@
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppWarmTest(private val testSpec: FlickerTestParameter) {
-    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
-    private val testApp = SimpleAppHelper(instrumentation)
-
-    @FlickerBuilderProvider
-    fun buildFlicker(): FlickerBuilder {
-        return FlickerBuilder(instrumentation).apply {
-            withTestName { testSpec.name }
-            repeat { testSpec.config.repetitions }
+class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) {
+    override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
+        get() = {
+            super.transition(this, it)
             setup {
                 test {
-                    device.wakeUpAndGoToHomeScreen()
                     testApp.launchViaIntent(wmHelper)
-                    // wmHelper.waitForFullScreenApp(testApp.component)
                 }
                 eachRun {
                     device.pressHome()
@@ -79,93 +52,21 @@
                     this.setRotation(testSpec.config.startRotation)
                 }
             }
+            teardown {
+                eachRun {
+                    testApp.exit(wmHelper)
+                }
+            }
             transitions {
                 testApp.launchViaIntent(wmHelper)
                 wmHelper.waitForFullScreenApp(testApp.component)
             }
-            teardown {
-                eachRun {
-                    this.setRotation(Surface.ROTATION_0)
-                }
-                test {
-                    testApp.exit()
-                }
-            }
         }
-    }
-
-    @Presubmit
-    @Test
-    fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
 
     @FlakyTest
     @Test
-    fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
-        testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
-    @Presubmit
-    @Test
-    fun appWindowReplacesLauncherAsTopWindow() =
-        testSpec.appWindowReplacesLauncherAsTopWindow(testApp)
-
-    @Presubmit
-    @Test
-    fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible()
-
-    @Presubmit
-    @Test
-    // During testing the launcher is always in portrait mode
-    fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation,
-        Surface.ROTATION_0)
-
-    @Presubmit
-    @Test
-    fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible()
-
-    @Presubmit
-    @Test
-    fun appLayerReplacesWallpaperLayer() =
-        testSpec.appLayerReplacesWallpaperLayer(testApp.`package`)
-
-    @Presubmit
-    @Test
-    fun navBarLayerRotatesAndScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest
-    @Test
-    fun navBarLayerRotatesAndScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Presubmit
-    @Test
-    fun statusBarLayerRotatesScales() {
-        Assume.assumeFalse(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @FlakyTest
-    @Test
-    fun statusBarLayerRotatesScales_Flaky() {
-        Assume.assumeTrue(testSpec.isRotated)
-        testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0)
-    }
-
-    @Presubmit
-    @Test
-    fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`)
+    override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+        super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 335c8d0..eacf5b2 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -9,14 +9,17 @@
 
 android_test {
     name: "InputTests",
-    srcs: ["src/**/*.kt"],
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
     platform_apis: true,
     certificate: "platform",
     static_libs: [
-            "androidx.test.ext.junit",
-            "androidx.test.rules",
-            "truth-prebuilt",
-            "ub-uiautomator",
-        ],
+        "androidx.test.ext.junit",
+        "androidx.test.rules",
+        "truth-prebuilt",
+        "ub-uiautomator",
+    ],
     test_suites: ["device-tests"],
 }
diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java
new file mode 100644
index 0000000..6350077
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputDeviceTest {
+    private static final float DELTA = 0.01f;
+    private static final int DEVICE_ID = 1000;
+
+    private void assertMotionRangeEquals(InputDevice.MotionRange range,
+            InputDevice.MotionRange outRange) {
+        assertEquals(range.getAxis(), outRange.getAxis());
+        assertEquals(range.getSource(), outRange.getSource());
+        assertEquals(range.getMin(), outRange.getMin(), DELTA);
+        assertEquals(range.getMax(), outRange.getMax(), DELTA);
+        assertEquals(range.getFlat(), outRange.getFlat(), DELTA);
+        assertEquals(range.getFuzz(), outRange.getFuzz(), DELTA);
+        assertEquals(range.getResolution(), outRange.getResolution(), DELTA);
+    }
+
+    private void assertDeviceEquals(InputDevice device, InputDevice outDevice) {
+        assertEquals(device.getId(), outDevice.getId());
+        assertEquals(device.getGeneration(), outDevice.getGeneration());
+        assertEquals(device.getControllerNumber(), outDevice.getControllerNumber());
+        assertEquals(device.getName(), outDevice.getName());
+        assertEquals(device.getVendorId(), outDevice.getVendorId());
+        assertEquals(device.getProductId(), outDevice.getProductId());
+        assertEquals(device.getDescriptor(), outDevice.getDescriptor());
+        assertEquals(device.isExternal(), outDevice.isExternal());
+        assertEquals(device.getSources(), outDevice.getSources());
+        assertEquals(device.getKeyboardType(), outDevice.getKeyboardType());
+        assertEquals(device.getMotionRanges().size(), outDevice.getMotionRanges().size());
+
+        KeyCharacterMap keyCharacterMap = device.getKeyCharacterMap();
+        KeyCharacterMap outKeyCharacterMap = outDevice.getKeyCharacterMap();
+        assertTrue("keyCharacterMap not equal", keyCharacterMap.equals(outKeyCharacterMap));
+
+        for (int j = 0; j < device.getMotionRanges().size(); j++) {
+            assertMotionRangeEquals(device.getMotionRanges().get(j),
+                    outDevice.getMotionRanges().get(j));
+        }
+    }
+
+    private void assertInputDeviceParcelUnparcel(KeyCharacterMap keyCharacterMap) {
+        final InputDevice device =
+                new InputDevice(DEVICE_ID, 0 /* generation */, 0 /* controllerNumber */, "name",
+                0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */,
+                0 /* sources */, 0 /* keyboardType */, keyCharacterMap,
+                false /* hasVibrator */, false /* hasMicrophone */, false /* hasButtonUnderpad */,
+                true /* hasSensor */, false /* hasBattery */);
+
+        Parcel parcel = Parcel.obtain();
+        device.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        InputDevice outDevice = InputDevice.CREATOR.createFromParcel(parcel);
+        assertDeviceEquals(device, outDevice);
+    }
+
+    @Test
+    public void testParcelUnparcelInputDevice_VirtualCharacterMap() {
+        final KeyCharacterMap keyCharacterMap =
+                KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+        assertInputDeviceParcelUnparcel(keyCharacterMap);
+    }
+
+    @Test
+    public void testParcelUnparcelInputDevice_EmptyCharacterMap() {
+        final KeyCharacterMap keyCharacterMap = KeyCharacterMap.obtainEmptyMap(DEVICE_ID);
+        assertInputDeviceParcelUnparcel(keyCharacterMap);
+    }
+}
diff --git a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
index d6846fa..e121b68 100644
--- a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
+++ b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
@@ -94,17 +94,16 @@
             if (platformCompat == null) {
                 throw new IllegalStateException("Could not get IPlatformCompat service!");
             }
-            uiAutomation.adoptShellPermissionIdentity(
-                    Manifest.permission.LOG_COMPAT_CHANGE,
-                    Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG,
-                    Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
+            adoptShellPermissions(uiAutomation);
             Compatibility.setOverrides(mConfig);
             try {
                 platformCompat.setOverridesForTest(new CompatibilityChangeConfig(mConfig),
                         packageName);
                 try {
+                    uiAutomation.dropShellPermissionIdentity();
                     mTestStatement.evaluate();
                 } finally {
+                    adoptShellPermissions(uiAutomation);
                     platformCompat.clearOverridesForTest(packageName);
                 }
             } catch (RemoteException e) {
@@ -114,5 +113,12 @@
                 Compatibility.clearOverrides();
             }
         }
+
+        private static void adoptShellPermissions(UiAutomation uiAutomation) {
+            uiAutomation.adoptShellPermissionIdentity(
+                    Manifest.permission.LOG_COMPAT_CHANGE,
+                    Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG,
+                    Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
+        }
     }
 }
diff --git a/tests/RollbackTest/MultiUserRollbackTest.xml b/tests/RollbackTest/MultiUserRollbackTest.xml
index 2f62af1..8fa0510 100644
--- a/tests/RollbackTest/MultiUserRollbackTest.xml
+++ b/tests/RollbackTest/MultiUserRollbackTest.xml
@@ -20,6 +20,8 @@
         <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
         <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
         <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+        <option name="run-command" value="setprop persist.rollback.is_test 1" />
+        <option name="teardown-command" value="setprop persist.rollback.is_test 0" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.HostTest" >
         <option name="class" value="com.android.tests.rollback.host.MultiUserRollbackTest" />
diff --git a/tests/RollbackTest/NetworkStagedRollbackTest.xml b/tests/RollbackTest/NetworkStagedRollbackTest.xml
index 2ab907a..13f6031 100644
--- a/tests/RollbackTest/NetworkStagedRollbackTest.xml
+++ b/tests/RollbackTest/NetworkStagedRollbackTest.xml
@@ -24,6 +24,8 @@
         <option name="run-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\\*' --esa flags &quot;ModuleConfig__versioned_immediate_commit_packages&quot; --esa types &quot;bytes&quot; --esa values &quot;Cm5vdGFwYWNrYWdlOgA=&quot; com.google.android.gms" />
         <option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\*' --esa flag &quot;ModuleConfig__immediate_commit_packages&quot; com.google.android.gms" />
         <option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\*' --esa flag &quot;ModuleConfig__versioned_immediate_commit_packages&quot; com.google.android.gms" />
+        <option name="run-command" value="setprop persist.rollback.is_test 1" />
+        <option name="teardown-command" value="setprop persist.rollback.is_test 0" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.HostTest" >
         <option name="class" value="com.android.tests.rollback.host.NetworkStagedRollbackTest" />
diff --git a/tests/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml
index 7b85cc8..fbb6e46 100644
--- a/tests/RollbackTest/RollbackTest.xml
+++ b/tests/RollbackTest/RollbackTest.xml
@@ -27,6 +27,8 @@
         <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
         <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
         <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+        <option name="run-command" value="setprop persist.rollback.is_test 1" />
+        <option name="teardown-command" value="setprop persist.rollback.is_test 0" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.tests.rollback" />
diff --git a/tests/RollbackTest/StagedRollbackTest.xml b/tests/RollbackTest/StagedRollbackTest.xml
index 83fef8e..0ca4daf 100644
--- a/tests/RollbackTest/StagedRollbackTest.xml
+++ b/tests/RollbackTest/StagedRollbackTest.xml
@@ -24,6 +24,8 @@
         <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
         <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
         <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+        <option name="run-command" value="setprop persist.rollback.is_test 1" />
+        <option name="teardown-command" value="setprop persist.rollback.is_test 0" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.HostTest" >
         <option name="class" value="com.android.tests.rollback.host.StagedRollbackTest" />
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 7f0318a..e1a424f 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -79,6 +79,7 @@
         "android.test.runner",
         "android.test.base",
         "android.test.mock",
+        "ServiceConnectivityResources",
     ],
     jni_libs: [
         "libservice-connectivity",
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index e84b992..0dfec75 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -28,6 +28,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE;
@@ -44,6 +45,10 @@
 import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
 import static android.os.Process.INVALID_UID;
 
+import static com.android.modules.utils.build.SdkLevel.isAtLeastR;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
+import static com.android.testutils.MiscAsserts.assertEmpty;
+import static com.android.testutils.MiscAsserts.assertThrows;
 import static com.android.testutils.ParcelUtils.assertParcelSane;
 import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
 import static com.android.testutils.ParcelUtils.parcelingRoundTrip;
@@ -67,7 +72,6 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.modules.utils.build.SdkLevel;
 import com.android.testutils.CompatUtil;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -85,6 +89,9 @@
 public class NetworkCapabilitiesTest {
     private static final String TEST_SSID = "TEST_SSID";
     private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID";
+    private static final int TEST_SUBID1 = 1;
+    private static final int TEST_SUBID2 = 2;
+    private static final int TEST_SUBID3 = 3;
 
     @Rule
     public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule();
@@ -92,14 +99,6 @@
     private DiscoverySession mDiscoverySession = Mockito.mock(DiscoverySession.class);
     private PeerHandle mPeerHandle = Mockito.mock(PeerHandle.class);
 
-    private boolean isAtLeastR() {
-        return SdkLevel.isAtLeastR();
-    }
-
-    private boolean isAtLeastS() {
-        return SdkLevel.isAtLeastS();
-    }
-
     @Test
     public void testMaybeMarkCapabilitiesRestricted() {
         // verify EIMS is restricted
@@ -305,7 +304,9 @@
             .setUids(uids)
             .addCapability(NET_CAPABILITY_EIMS)
             .addCapability(NET_CAPABILITY_NOT_METERED);
-        if (isAtLeastR()) {
+        if (isAtLeastS()) {
+            netCap.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2));
+        } else if (isAtLeastR()) {
             netCap.setOwnerUid(123);
             netCap.setAdministratorUids(new int[] {5, 11});
         }
@@ -380,7 +381,7 @@
 
     private void testParcelSane(NetworkCapabilities cap) {
         if (isAtLeastS()) {
-            assertParcelSane(cap, 16);
+            assertParcelSane(cap, 17);
         } else if (isAtLeastR()) {
             assertParcelSane(cap, 15);
         } else {
@@ -614,6 +615,20 @@
         assertFalse(nc2.appliesToUid(12));
         assertTrue(nc1.appliesToUid(22));
         assertTrue(nc2.appliesToUid(22));
+
+        // Verify the subscription id list can be combined only when they are equal.
+        if (isAtLeastS()) {
+            nc1.setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2));
+            nc2.setSubIds(Set.of(TEST_SUBID2));
+            assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
+
+            nc2.setSubIds(Set.of());
+            assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
+
+            nc2.setSubIds(Set.of(TEST_SUBID2, TEST_SUBID1));
+            nc2.combineCapabilities(nc1);
+            assertEquals(Set.of(TEST_SUBID2, TEST_SUBID1), nc2.getSubIds());
+        }
     }
 
     @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
@@ -762,6 +777,24 @@
         nc1.setUids(uidRange(10, 13));
         nc2.set(nc1);  // Overwrites, as opposed to combineCapabilities
         assertEquals(nc1, nc2);
+
+        if (isAtLeastS()) {
+            assertThrows(NullPointerException.class, () -> nc1.setSubIds(null));
+            nc1.setSubIds(Set.of());
+            nc2.set(nc1);
+            assertEquals(nc1, nc2);
+
+            nc1.setSubIds(Set.of(TEST_SUBID1));
+            nc2.set(nc1);
+            assertEquals(nc1, nc2);
+
+            nc2.setSubIds(Set.of(TEST_SUBID2, TEST_SUBID1));
+            nc2.set(nc1);
+            assertEquals(nc1, nc2);
+
+            nc2.setSubIds(Set.of(TEST_SUBID3, TEST_SUBID2));
+            assertNotEquals(nc1, nc2);
+        }
     }
 
     @Test
@@ -842,6 +875,50 @@
         } catch (NullPointerException expected) { }
     }
 
+    private static NetworkCapabilities capsWithSubIds(Integer ... subIds) {
+        // Since the NetworkRequest would put NOT_VCN_MANAGED capabilities in general, for
+        // every NetworkCapabilities that simulates networks needs to add it too in order to
+        // satisfy these requests.
+        final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+                .setSubIds(new ArraySet<>(subIds)).build();
+        assertEquals(new ArraySet<>(subIds), nc.getSubIds());
+        return nc;
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testSubIds() throws Exception {
+        final NetworkCapabilities ncWithoutId = capsWithSubIds();
+        final NetworkCapabilities ncWithId = capsWithSubIds(TEST_SUBID1);
+        final NetworkCapabilities ncWithOtherIds = capsWithSubIds(TEST_SUBID1, TEST_SUBID3);
+        final NetworkCapabilities ncWithoutRequestedIds = capsWithSubIds(TEST_SUBID3);
+
+        final NetworkRequest requestWithoutId = new NetworkRequest.Builder().build();
+        assertEmpty(requestWithoutId.networkCapabilities.getSubIds());
+        final NetworkRequest requestWithIds = new NetworkRequest.Builder()
+                .setSubIds(Set.of(TEST_SUBID1, TEST_SUBID2)).build();
+        assertEquals(Set.of(TEST_SUBID1, TEST_SUBID2),
+                requestWithIds.networkCapabilities.getSubIds());
+
+        assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutId));
+        assertTrue(requestWithIds.canBeSatisfiedBy(ncWithOtherIds));
+        assertFalse(requestWithIds.canBeSatisfiedBy(ncWithoutRequestedIds));
+        assertTrue(requestWithIds.canBeSatisfiedBy(ncWithId));
+        assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithoutId));
+        assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithId));
+    }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testEqualsSubIds() throws Exception {
+        assertEquals(capsWithSubIds(), capsWithSubIds());
+        assertNotEquals(capsWithSubIds(), capsWithSubIds(TEST_SUBID1));
+        assertEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID1));
+        assertNotEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID2));
+        assertNotEquals(capsWithSubIds(TEST_SUBID1), capsWithSubIds(TEST_SUBID2, TEST_SUBID1));
+        assertEquals(capsWithSubIds(TEST_SUBID1, TEST_SUBID2),
+                capsWithSubIds(TEST_SUBID2, TEST_SUBID1));
+    }
+
     @Test
     public void testLinkBandwidthKbps() {
         final NetworkCapabilities nc = new NetworkCapabilities();
@@ -1022,5 +1099,11 @@
             fail("Should not set null into NetworkCapabilities.Builder");
         } catch (NullPointerException expected) { }
         assertEquals(nc, new NetworkCapabilities.Builder(nc).build());
+
+        if (isAtLeastS()) {
+            final NetworkCapabilities nc2 = new NetworkCapabilities.Builder()
+                    .setSubIds(Set.of(TEST_SUBID1)).build();
+            assertEquals(Set.of(TEST_SUBID1), nc2.getSubIds());
+        }
     }
 }
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 6a09b02..6fc605e 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -220,7 +220,7 @@
 
         // register callback
         when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
-                any(), nullable(String.class))).thenReturn(request);
+                anyInt(), any(), nullable(String.class))).thenReturn(request);
         manager.requestNetwork(request, callback, handler);
 
         // callback triggers
@@ -248,7 +248,7 @@
 
         // register callback
         when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
-                any(), nullable(String.class))).thenReturn(req1);
+                anyInt(), any(), nullable(String.class))).thenReturn(req1);
         manager.requestNetwork(req1, callback, handler);
 
         // callback triggers
@@ -266,7 +266,7 @@
 
         // callback can be registered again
         when(mService.requestNetwork(any(), anyInt(), captor.capture(), anyInt(), any(), anyInt(),
-                any(), nullable(String.class))).thenReturn(req2);
+                anyInt(), any(), nullable(String.class))).thenReturn(req2);
         manager.requestNetwork(req2, callback, handler);
 
         // callback triggers
@@ -289,8 +289,8 @@
         info.targetSdkVersion = VERSION_CODES.N_MR1 + 1;
 
         when(mCtx.getApplicationInfo()).thenReturn(info);
-        when(mService.requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), any(),
-                nullable(String.class))).thenReturn(request);
+        when(mService.requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(), anyInt(),
+                any(), nullable(String.class))).thenReturn(request);
 
         Handler handler = new Handler(Looper.getMainLooper());
         manager.requestNetwork(request, callback, handler);
@@ -358,34 +358,34 @@
 
         manager.requestNetwork(request, callback);
         verify(mService).requestNetwork(eq(request.networkCapabilities),
-                eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+                eq(REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
                 eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         // Verify that register network callback does not calls requestNetwork at all.
         manager.registerNetworkCallback(request, callback);
-        verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(),
+        verify(mService, never()).requestNetwork(any(), anyInt(), any(), anyInt(), any(), anyInt(),
                 anyInt(), any(), any());
-        verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(),
+        verify(mService).listenForNetwork(eq(request.networkCapabilities), any(), any(), anyInt(),
                 eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         manager.registerDefaultNetworkCallback(callback);
         verify(mService).requestNetwork(eq(null),
-                eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
-                eq(testPkgName), eq(testAttributionTag));
-        reset(mService);
-
-        manager.requestBackgroundNetwork(request, null, callback);
-        verify(mService).requestNetwork(eq(request.networkCapabilities),
-                eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+                eq(TRACK_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
                 eq(testPkgName), eq(testAttributionTag));
         reset(mService);
 
         Handler handler = new Handler(ConnectivityThread.getInstanceLooper());
+        manager.requestBackgroundNetwork(request, handler, callback);
+        verify(mService).requestNetwork(eq(request.networkCapabilities),
+                eq(BACKGROUND_REQUEST.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
+                eq(testPkgName), eq(testAttributionTag));
+        reset(mService);
+
         manager.registerSystemDefaultNetworkCallback(callback, handler);
         verify(mService).requestNetwork(eq(null),
-                eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE),
+                eq(TRACK_SYSTEM_DEFAULT.ordinal()), any(), anyInt(), any(), eq(TYPE_NONE), anyInt(),
                 eq(testPkgName), eq(testAttributionTag));
         reset(mService);
     }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 3102b99..fadd1ea 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -275,6 +275,7 @@
 import com.android.net.module.util.ArrayTrackRecord;
 import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
 import com.android.server.connectivity.ConnectivityConstants;
+import com.android.server.connectivity.ConnectivityResources;
 import com.android.server.connectivity.MockableSystemProperties;
 import com.android.server.connectivity.Nat464Xlat;
 import com.android.server.connectivity.NetworkAgentInfo;
@@ -444,6 +445,7 @@
     @Mock NetworkPolicyManager mNetworkPolicyManager;
     @Mock VpnProfileStore mVpnProfileStore;
     @Mock SystemConfigManager mSystemConfigManager;
+    @Mock Resources mResources;
 
     private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
             ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -471,7 +473,7 @@
     private class MockContext extends BroadcastInterceptingContext {
         private final MockContentResolver mContentResolver;
 
-        @Spy private Resources mResources;
+        @Spy private Resources mInternalResources;
         private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
 
         // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
@@ -480,21 +482,15 @@
         MockContext(Context base, ContentProvider settingsProvider) {
             super(base);
 
-            mResources = spy(base.getResources());
-            when(mResources.getStringArray(com.android.internal.R.array.networkAttributes)).
-                    thenReturn(new String[] {
+            mInternalResources = spy(base.getResources());
+            when(mInternalResources.getStringArray(com.android.internal.R.array.networkAttributes))
+                    .thenReturn(new String[] {
                             "wifi,1,1,1,-1,true",
                             "mobile,0,0,0,-1,true",
                             "mobile_mms,2,0,2,60000,true",
                             "mobile_supl,3,0,2,60000,true",
                     });
 
-            when(mResources.getStringArray(
-                    com.android.internal.R.array.config_wakeonlan_supported_interfaces))
-                    .thenReturn(new String[]{
-                            WIFI_WOL_IFNAME,
-                    });
-
             mContentResolver = new MockContentResolver();
             mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
         }
@@ -559,7 +555,7 @@
 
         @Override
         public Resources getResources() {
-            return mResources;
+            return mInternalResources;
         }
 
         @Override
@@ -1454,6 +1450,8 @@
         applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
         when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
                 .thenReturn(applicationInfo);
+        when(mPackageManager.getTargetSdkVersion(anyString()))
+                .thenReturn(applicationInfo.targetSdkVersion);
         when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
 
         // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
@@ -1532,6 +1530,17 @@
         }).when(deps).makeMultinetworkPolicyTracker(any(), any(), any());
         doReturn(true).when(deps).getCellular464XlatEnabled();
 
+        doReturn(60000).when(mResources).getInteger(
+                com.android.connectivity.resources.R.integer.config_networkTransitionTimeout);
+        doReturn("").when(mResources).getString(
+                com.android.connectivity.resources.R.string.config_networkCaptivePortalServerUrl);
+        doReturn(new String[]{ WIFI_WOL_IFNAME }).when(mResources).getStringArray(
+                com.android.connectivity.resources.R.array.config_wakeonlan_supported_interfaces);
+        final com.android.server.connectivity.ConnectivityResources connRes = mock(
+                ConnectivityResources.class);
+        doReturn(mResources).when(connRes).get();
+        doReturn(connRes).when(deps).getResources(any());
+
         return deps;
     }
 
@@ -3749,8 +3758,8 @@
             networkCapabilities.addTransportType(TRANSPORT_WIFI)
                     .setNetworkSpecifier(new MatchAllNetworkSpecifier());
             mService.requestNetwork(networkCapabilities, NetworkRequest.Type.REQUEST.ordinal(),
-                    null, 0, null, ConnectivityManager.TYPE_WIFI, mContext.getPackageName(),
-                    getAttributionTag());
+                    null, 0, null, ConnectivityManager.TYPE_WIFI, NetworkCallback.FLAG_NONE,
+                    mContext.getPackageName(), getAttributionTag());
         });
 
         class NonParcelableSpecifier extends NetworkSpecifier {
@@ -4027,7 +4036,8 @@
         grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid());
         final TestNetworkCallback cellBgCallback = new TestNetworkCallback();
         mCm.requestBackgroundNetwork(new NetworkRequest.Builder()
-                .addTransportType(TRANSPORT_CELLULAR).build(), null, cellBgCallback);
+                .addTransportType(TRANSPORT_CELLULAR).build(),
+                mCsHandlerThread.getThreadHandler(), cellBgCallback);
 
         // Make callbacks for monitoring.
         final NetworkRequest request = new NetworkRequest.Builder().build();
@@ -8755,6 +8765,7 @@
         applicationInfo.targetSdkVersion = targetSdk;
         when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
                 .thenReturn(applicationInfo);
+        when(mPackageManager.getTargetSdkVersion(any())).thenReturn(targetSdk);
 
         when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
 
@@ -8769,102 +8780,183 @@
         }
     }
 
-    private int getOwnerUidNetCapsForCallerPermission(int ownerUid, int callerUid) {
+    private int getOwnerUidNetCapsPermission(int ownerUid, int callerUid,
+            boolean includeLocationSensitiveInfo) {
         final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
 
         return mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                netCap, callerUid, mContext.getPackageName(), getAttributionTag()).getOwnerUid();
+                netCap, includeLocationSensitiveInfo, callerUid,
+                mContext.getPackageName(), getAttributionTag())
+                .getOwnerUid();
     }
 
-    private void verifyWifiInfoCopyNetCapsForCallerPermission(
-            int callerUid, boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) {
+    private void verifyWifiInfoCopyNetCapsPermission(
+            int callerUid, boolean includeLocationSensitiveInfo,
+            boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) {
         final WifiInfo wifiInfo = mock(WifiInfo.class);
         when(wifiInfo.hasLocationSensitiveFields()).thenReturn(true);
         final NetworkCapabilities netCap = new NetworkCapabilities().setTransportInfo(wifiInfo);
 
         mService.createWithLocationInfoSanitizedIfNecessaryWhenParceled(
-                netCap, callerUid, mContext.getPackageName(), getAttributionTag());
+                netCap, includeLocationSensitiveInfo, callerUid,
+                mContext.getPackageName(), getAttributionTag());
         verify(wifiInfo).makeCopy(eq(shouldMakeCopyWithLocationSensitiveFieldsParcelable));
     }
 
+    private void verifyOwnerUidAndWifiInfoNetCapsPermission(
+            boolean shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag,
+            boolean shouldInclLocationSensitiveOwnerUidWithIncludeFlag,
+            boolean shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag,
+            boolean shouldInclLocationSensitiveWifiInfoWithIncludeFlag) {
+        final int myUid = Process.myUid();
+
+        final int expectedOwnerUidWithoutIncludeFlag =
+                shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag
+                        ? Process.myUid() : INVALID_UID;
+        assertEquals(expectedOwnerUidWithoutIncludeFlag, getOwnerUidNetCapsPermission(
+                myUid, myUid, false /* includeLocationSensitiveInfo */));
+
+        final int expectedOwnerUidWithIncludeFlag =
+                shouldInclLocationSensitiveOwnerUidWithIncludeFlag ? myUid : INVALID_UID;
+        assertEquals(expectedOwnerUidWithIncludeFlag, getOwnerUidNetCapsPermission(
+                myUid, myUid, true /* includeLocationSensitiveInfo */));
+
+        verifyWifiInfoCopyNetCapsPermission(myUid,
+                false, /* includeLocationSensitiveInfo */
+                shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag);
+
+        verifyWifiInfoCopyNetCapsPermission(myUid,
+                true, /* includeLocationSensitiveInfo */
+                shouldInclLocationSensitiveWifiInfoWithIncludeFlag);
+
+    }
+
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWithFineLocationAfterQ()
+    public void testCreateWithLocationInfoSanitizedWithFineLocationAfterQ()
             throws Exception {
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        final int myUid = Process.myUid();
-        assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                // Ensure that we include owner uid even if the request asks to remove it since the
+                // app has necessary permissions and targetSdk < S.
+                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                // Ensure that we remove location info if the request asks to remove it even if the
+                // app has necessary permissions.
+                true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationPreQ()
+    public void testCreateWithLocationInfoSanitizedWithFineLocationPreSWithAndWithoutCallbackFlag()
+            throws Exception {
+        setupLocationPermissions(Build.VERSION_CODES.R, true, AppOpsManager.OPSTR_FINE_LOCATION,
+                Manifest.permission.ACCESS_FINE_LOCATION);
+
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                // Ensure that we include owner uid even if the request asks to remove it since the
+                // app has necessary permissions and targetSdk < S.
+                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                // Ensure that we remove location info if the request asks to remove it even if the
+                // app has necessary permissions.
+                true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
+    }
+
+    @Test
+    public void
+            testCreateWithLocationInfoSanitizedWithFineLocationAfterSWithAndWithoutCallbackFlag()
+            throws Exception {
+        setupLocationPermissions(Build.VERSION_CODES.S, true, AppOpsManager.OPSTR_FINE_LOCATION,
+                Manifest.permission.ACCESS_FINE_LOCATION);
+
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                // Ensure that we owner UID if the request asks us to remove it even if the app
+                // has necessary permissions since targetSdk >= S.
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                // Ensure that we remove location info if the request asks to remove it even if the
+                // app has necessary permissions.
+                true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
+    }
+
+    @Test
+    public void testCreateWithLocationInfoSanitizedWithCoarseLocationPreQ()
             throws Exception {
         setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION,
                 Manifest.permission.ACCESS_COARSE_LOCATION);
 
-        final int myUid = Process.myUid();
-        assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                // Ensure that we owner UID if the request asks us to remove it even if the app
+                // has necessary permissions since targetSdk >= S.
+                true, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                true, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                // Ensure that we remove location info if the request asks to remove it even if the
+                // app has necessary permissions.
+                true /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedLocationOff() throws Exception {
+    public void testCreateWithLocationInfoSanitizedLocationOff() throws Exception {
         // Test that even with fine location permission, and UIDs matching, the UID is sanitized.
         setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        final int myUid = Process.myUid();
-        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWrongUid() throws Exception {
+    public void testCreateWithLocationInfoSanitizedWrongUid() throws Exception {
         // Test that even with fine location permission, not being the owner leads to sanitization.
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
         final int myUid = Process.myUid();
-        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid + 1, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                true /* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        assertEquals(Process.INVALID_UID,
+                getOwnerUidNetCapsPermission(myUid + 1, myUid,
+                        true /* includeLocationSensitiveInfo */));
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWithCoarseLocationAfterQ()
+    public void testCreateWithLocationInfoSanitizedWithCoarseLocationAfterQ()
             throws Exception {
         // Test that not having fine location permission leads to sanitization.
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION,
                 Manifest.permission.ACCESS_COARSE_LOCATION);
 
-        // Test that without the location permission, the owner field is sanitized.
-        final int myUid = Process.myUid();
-        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
     }
 
     @Test
-    public void testCreateForCallerWithLocationInfoSanitizedWithoutLocationPermission()
+    public void testCreateWithLocationInfoSanitizedWithoutLocationPermission()
             throws Exception {
+        // Test that not having fine location permission leads to sanitization.
         setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */);
 
-        // Test that without the location permission, the owner field is sanitized.
-        final int myUid = Process.myUid();
-        assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
-
-        verifyWifiInfoCopyNetCapsForCallerPermission(myUid,
-                false/* shouldMakeCopyWithLocationSensitiveFieldsParcelable */);
+        verifyOwnerUidAndWifiInfoNetCapsPermission(
+                false, /* shouldInclLocationSensitiveOwnerUidWithoutIncludeFlag */
+                false, /* shouldInclLocationSensitiveOwnerUidWithIncludeFlag */
+                false, /* shouldInclLocationSensitiveWifiInfoWithoutIncludeFlag */
+                false /* shouldInclLocationSensitiveWifiInfoWithIncludeFlag */
+        );
     }
 
     private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
@@ -9455,8 +9547,8 @@
             assertThrows("Expect throws for invalid request type " + reqTypeInt,
                     IllegalArgumentException.class,
                     () -> mService.requestNetwork(nc, reqTypeInt, null, 0, null,
-                            ConnectivityManager.TYPE_NONE, mContext.getPackageName(),
-                            getAttributionTag())
+                            ConnectivityManager.TYPE_NONE, NetworkCallback.FLAG_NONE,
+                            mContext.getPackageName(), getAttributionTag())
             );
         }
     }
@@ -11026,4 +11118,12 @@
         verifyNoNetwork();
         mCm.unregisterNetworkCallback(cellCb);
     }
-}
\ No newline at end of file
+
+    @Test
+    public void testRegisterBestMatchingNetworkCallback() throws Exception {
+        final NetworkRequest request = new NetworkRequest.Builder().build();
+        assertThrows(UnsupportedOperationException.class,
+                () -> mCm.registerBestMatchingNetworkCallback(request, new NetworkCallback(),
+                        mCsHandlerThread.getThreadHandler()));
+    }
+}
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index f97eabf..6232423 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.net.ConnectivityManager;
 import android.net.INetd;
 import android.net.IpSecAlgorithm;
 import android.net.IpSecConfig;
@@ -47,6 +48,7 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructStat;
+import android.util.Range;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -647,9 +649,9 @@
 
     @Test
     public void testReserveNetId() {
-        int start = mIpSecService.TUN_INTF_NETID_START;
-        for (int i = 0; i < mIpSecService.TUN_INTF_NETID_RANGE; i++) {
-            assertEquals(start + i, mIpSecService.reserveNetId());
+        final Range<Integer> netIdRange = ConnectivityManager.getIpSecNetIdRange();
+        for (int netId = netIdRange.getLower(); netId <= netIdRange.getUpper(); netId++) {
+            assertEquals(netId, mIpSecService.reserveNetId());
         }
 
         // Check that resource exhaustion triggers an exception
@@ -661,7 +663,7 @@
 
         // Now release one and try again
         int releasedNetId =
-                mIpSecService.TUN_INTF_NETID_START + mIpSecService.TUN_INTF_NETID_RANGE / 2;
+                netIdRange.getLower() + (netIdRange.getUpper() - netIdRange.getLower()) / 2;
         mIpSecService.releaseNetId(releasedNetId);
         assertEquals(releasedNetId, mIpSecService.reserveNetId());
     }
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index e4e24b4..fec5ef3 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -48,18 +48,22 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.net.INetd;
 import android.net.UidRange;
+import android.net.Uri;
 import android.os.Build;
 import android.os.SystemConfigManager;
 import android.os.UserHandle;
@@ -70,12 +74,11 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.LocalServices;
-import com.android.server.pm.PackageList;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.AdditionalAnswers;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
@@ -112,7 +115,6 @@
     @Mock private Context mContext;
     @Mock private PackageManager mPackageManager;
     @Mock private INetd mNetdService;
-    @Mock private PackageManagerInternal mMockPmi;
     @Mock private UserManager mUserManager;
     @Mock private PermissionMonitor.Dependencies mDeps;
     @Mock private SystemConfigManager mSystemConfigManager;
@@ -131,16 +133,14 @@
         when(mContext.getSystemService(Context.SYSTEM_CONFIG_SERVICE))
                 .thenReturn(mSystemConfigManager);
         when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
+        final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext));
+        doReturn(UserHandle.ALL).when(asUserCtx).getUser();
+        when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx);
 
         mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
 
-        LocalServices.removeServiceForTest(PackageManagerInternal.class);
-        LocalServices.addService(PackageManagerInternal.class, mMockPmi);
-        when(mMockPmi.getPackageList(any())).thenReturn(new PackageList(new ArrayList<String>(),
-                  /* observer */ null));
         when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(/* empty app list */ null);
         mPermissionMonitor.startMonitoring();
-        verify(mMockPmi).getPackageList(mPermissionMonitor);
     }
 
     private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid,
@@ -770,4 +770,32 @@
                 INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS,
                 new int[]{ MOCK_UID2 });
     }
+
+    @Test
+    public void testIntentReceiver() throws Exception {
+        final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
+        final ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(mContext, times(1)).registerReceiver(receiverCaptor.capture(), any(), any(), any());
+        final BroadcastReceiver receiver = receiverCaptor.getValue();
+
+        // Verify receiving PACKAGE_ADDED intent.
+        final Intent addedIntent = new Intent(Intent.ACTION_PACKAGE_ADDED,
+                Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */));
+        addedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1);
+        setPackagePermissions(MOCK_PACKAGE1, MOCK_UID1,
+                new String[] { INTERNET, UPDATE_DEVICE_STATS });
+        receiver.onReceive(mContext, addedIntent);
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+                | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[] { MOCK_UID1 });
+
+        // Verify receiving PACKAGE_REMOVED intent.
+        when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(null);
+        final Intent removedIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
+                Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */));
+        removedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1);
+        receiver.onReceive(mContext, removedIntent);
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[] { MOCK_UID1 });
+    }
+
 }
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 11498de..a0200275 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -593,6 +593,16 @@
         mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
     }
 
+    @Test(expected = SecurityException.class)
+    public void testRemoveVcnUnderlyingNetworkPolicyListenerInvalidPermission() {
+        doThrow(new SecurityException())
+                .when(mMockContext)
+                .enforceCallingOrSelfPermission(
+                        eq(android.Manifest.permission.NETWORK_FACTORY), any());
+
+        mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+    }
+
     @Test
     public void testRemoveVcnUnderlyingNetworkPolicyListenerNeverRegistered() {
         mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);